結論先講

你有沒有過這種經驗:加入新公司第一天,花了整整八小時還沒把本地開發環境跑起來?README 寫著「直接 npm start」,結果缺了五個環境變數、三個系統套件,還有一個只有某個前輩腦中才有的 workaround。

開發者體驗(DX)就是讓工程師不要把時間浪費在跟程式碼無關的事情上。 好的 DX 讓新人第一天就能送出 PR,壞的 DX 讓資深工程師也想離職。

這篇是系列的最後一篇,也是最容易被忽略的一篇。因為 DX 不會直接產出商業價值,所以常常被犧牲。但它影響的是整個團隊的生產力和幸福感


體檢清單

1. README 品質

一個好的 README 至少要有:

# Project Name
 
一句話說明這個專案是什麼。
 
## Quick Start
 
git clone ...
cp .env.example .env
docker compose up -d
npm install
npm run dev
# 打開 http://localhost:3000
 
## Architecture
 
簡單的架構圖或說明。
 
## Tech Stack
 
- Frontend: Next.js 14, TypeScript, Tailwind
- Backend: FastAPI, PostgreSQL, Redis
- Infra: Docker, GitHub Actions, AWS
 
## Development
 
### Prerequisites
- Node.js 20+
- Docker Desktop
- ...
 
### Available Commands
- `npm run dev` - 啟動開發伺服器
- `npm run test` - 跑測試
- `npm run lint` - 跑 linter
- ...
 
## Deployment
 
如何部署到各環境。
 
## Contributing
 
如何貢獻程式碼。
  • README 有且內容是最新的
  • 有 quick start 可以五分鐘內跑起來
  • 有 tech stack 說明
  • 有 available commands 列表

2. Onboarding Guide

README 是給所有人看的,onboarding guide 是給新人看的。

# 新人上手指南
 
## 第一天
1. 設定開發環境(按照 README)
2. 閱讀架構文件
3. 閱讀 coding conventions
4. 認領一個 "good first issue"
 
## 第一週
1. 完成一個小 PR
2. 參加 code review(觀摩)
3. 了解部署流程
4. 了解值班制度
 
## 第一個月
1. 獨立完成一個 feature
2. 開始參與 code review(審查)
3. 了解系統全貌
  • 有 onboarding 文件
  • 有 “good first issue” label
  • 有 buddy / mentor 制度
  • 新人第一天能跑起開發環境

3. 一鍵啟動本地開發環境

新人 clone 下來,一個指令就能跑。 這是 DX 的黃金標準。

# 理想狀態
git clone git@github.com:company/project.git
cd project
make dev  # 或 ./scripts/setup.sh
 
# 這個指令應該做到:
# 1. 安裝依賴
# 2. 設定環境變數(從 .env.example 複製)
# 3. 啟動 Docker 容器(DB、Redis 等)
# 4. 跑 migration 和 seed
# 5. 啟動開發伺服器
# Makefile 範例
.PHONY: dev setup test
 
setup:
	cp -n .env.example .env || true
	docker compose up -d
	npm install
	npm run db:migrate
	npm run db:seed
 
dev: setup
	npm run dev
 
test:
	docker compose -f docker-compose.test.yml up -d
	npm run test
	docker compose -f docker-compose.test.yml down
  • 一個指令(make devnpm run setup
  • Docker Compose 管理外部依賴
  • .env.example
  • 有 seed data

4. Linting / Formatting(自動化)

永遠不要在 code review 裡討論程式碼風格。 讓工具來處理。

// .husky/pre-commit
// #!/usr/bin/env sh
// npx lint-staged
 
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md,yml}": ["prettier --write"]
  }
}
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100
}
  • ESLint + Prettier(或 Biome)
  • Pre-commit hook 自動格式化(Husky + lint-staged)
  • CI 也跑 lint(雙保險)
  • 團隊統一的規則(不是每個人各自設定)

5. PR Template

<!-- .github/pull_request_template.md -->
 
## What does this PR do?
<!-- 簡短說明這個 PR 做了什麼 -->
 
## Why?
<!-- 為什麼要做這個改動 -->
 
## How to test?
<!-- 如何測試這個改動 -->
1.
2.
3.
 
## Screenshots (if UI change)
<!-- 如果有 UI 變動,附上截圖 -->
 
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated (if needed)
- [ ] No console.log left
- [ ] Self-reviewed the diff
  • 有 PR template
  • Template 包含 what/why/how to test
  • 有 checklist

6. Code Review Guidelines

# Code Review 原則
 
## 作為 Reviewer
- 24 小時內回覆(至少 acknowledge)
- 區分 "must fix"、"suggestion"、"nit"
- 給建設性回饋,不是批評
- 大 PR 可以要求拆分
 
## 作為 Author
- PR 盡量小(< 400 行)
- 寫清楚 PR description
- Self-review 後再請人看
- 回覆所有 comment
 
## Convention
- 使用 Conventional Comments:
  - `suggestion:` 建議但不強制
  - `issue:` 需要修改
  - `question:` 想了解原因
  - `nitpick:` 非常小的事
  - `praise:` 稱讚好的寫法
  • 有 code review guidelines
  • Review 有時間限制(24h 內回覆)
  • 有 comment convention
  • 大 PR 會被要求拆分

7. 文件系統(ADR、Runbook)

ADR(Architecture Decision Record)

<!-- docs/adr/001-use-postgresql.md -->
 
# ADR 001: 使用 PostgreSQL 作為主要資料庫
 
## Status: Accepted
 
## Context
我們需要選擇一個關聯式資料庫。團隊有 PostgreSQL 和 MySQL 的經驗。
 
## Decision
選擇 PostgreSQL。
 
## Reasons
- JSONB 支援好,未來可能需要彈性 schema
- 全文搜尋內建
- 授權比 MySQL (GPL) 友善
- 團隊多數人較熟悉
 
## Consequences
- 需要學習 PostgreSQL 特有的語法
- 部署需要 PostgreSQL 16+

Runbook

<!-- docs/runbooks/database-disk-full.md -->
 
# Database Disk Full
 
## Symptoms
- 告警:DiskWillFull
- 寫入操作失敗
 
## Steps
1. 確認磁碟使用率:`df -h`
2. 檢查大表:`SELECT relname, pg_size_pretty(pg_total_relation_size(oid)) FROM pg_class ORDER BY pg_total_relation_size(oid) DESC LIMIT 10;`
3. 清理可能的候選:
   - 舊的 audit logs(超過 90 天的可以刪)
   - 過期的 sessions
4. 如果需要緊急空間:擴展 EBS volume
5. 長期:設定自動清理 cron job
 
## Escalation
如果磁碟 > 95%,聯繫 SRE 團隊。
  • 重要決策有 ADR
  • 常見問題有 runbook
  • 文件跟程式碼放在一起(docs/ 目錄)
  • 有人負責維護文件(不然會腐爛)

8. Development Container

// .devcontainer/devcontainer.json
{
  "name": "My Project",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "dev",
  "workspaceFolder": "/workspace",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "20" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" }
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "bradlc.vscode-tailwindcss"
      ],
      "settings": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      }
    }
  },
  "postCreateCommand": "npm install && npm run db:migrate"
}
  • 有 Dev Container 或 Docker-based 開發環境
  • 預裝必要的 VS Code extensions
  • 預設 editor settings

9. Seed Data

// seeds/development.ts
async function seed() {
  // 建立測試帳號
  await db.users.createMany([
    { email: 'admin@dev.local', role: 'admin', password: hash('password') },
    { email: 'editor@dev.local', role: 'editor', password: hash('password') },
    { email: 'viewer@dev.local', role: 'viewer', password: hash('password') },
  ]);
 
  // 建立測試資料
  await db.articles.createMany([
    { title: 'Test Article 1', authorId: admin.id, status: 'published' },
    { title: 'Draft Article', authorId: editor.id, status: 'draft' },
  ]);
 
  console.log('Seed completed!');
}
  • 有 seed script
  • 涵蓋各種角色和狀態
  • 資料量足以測試分頁、搜尋等功能
  • Seed 可以重複跑(idempotent)

10. Hot Reload

  • 前端有 HMR(Hot Module Replacement)
  • 後端有 file watching(nodemon、uvicorn —reload)
  • 改 code 後 < 3 秒看到結果

11. IDE Config

# .editorconfig
root = true
 
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
 
[*.md]
trim_trailing_whitespace = false
 
[Makefile]
indent_style = tab
// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}
 
// .vscode/extensions.json
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "bradlc.vscode-tailwindcss",
    "prisma.prisma"
  ]
}
  • .editorconfig(跨 IDE 統一)
  • .vscode/settings.json(VS Code 設定)
  • .vscode/extensions.json(推薦的 extensions)
  • 不要 commit 個人化設定

DX 成熟度對照

等級特徵新人 setup 時間
L0 糟糕沒有 README、問前輩才知道怎麼跑2-3 天
L1 基本有 README、但步驟很多半天
L2 不錯Docker Compose + seed、一鍵啟動1 小時
L3 優秀Dev Container、完整文件、自動化15 分鐘
L4 頂級Codespaces/Gitpod、即開即用5 分鐘

DX 投資回報計算

假設:
- 團隊 5 人
- 每人每天因 DX 不好浪費 30 分鐘
- 工程師月薪 8 萬台幣

每月浪費 = 5 人 × 30 分鐘 × 22 天 = 55 小時
換算金額 = 55 × (80000 / 176) ≈ 25,000 台幣/月

投入 2 週(80 小時)改善 DX:
成本 = 80 × 455 ≈ 36,400 台幣
回收期 = 36,400 / 25,000 ≈ 1.5 個月

結論:DX 改善的 ROI 非常好。

FAQ

Q1: DX 這些東西誰來做?什麼時候做?

每個 sprint 留 10-15% 的時間做。不需要一次到位,每週改善一點。可以輪流由不同的人負責(developer experience champion)。新人加入時最能發現 DX 的問題,讓新人記錄下來是很好的做法。

Q2: PR 大小怎樣算合理?

經驗法則:差異在 400 行以內。超過 400 行的 PR,reviewer 的注意力會急劇下降,review 品質也會跟著掉。如果 feature 很大,拆成多個 PR(feature flag 控制上線)。

Q3: ADR 需要多正式?

不需要很正式。一個 Markdown 檔案,記錄 context、decision、reasons 就好。重點是「未來的自己(或新同事)能理解當初為什麼這樣決定」。不需要開會審批,PR review 就好。

Q4: 每個專案都要 Dev Container 嗎?

不一定。如果你的專案只需要 Node.js,本地跑就好。Dev Container 最有價值的場景是:需要多個系統依賴(特定版本的 Python + Node + 系統套件)、或團隊成員用不同 OS。

Q5: 文件怎麼維持更新?

兩個策略:1) 文件放在程式碼旁邊(docs/ 目錄),隨 code 一起 review。2) 定期的 documentation audit(每季一次,檢查文件是否過期)。最重要的是建立文化——改了 code 就更新相關文件,就像寫了 code 就寫 test 一樣自然。


系列導航