FE Day 43 文件與交接
執行需求:CPU 可跑。今天是「前端開發實戰:React 與 Next.js 全套」系列的第四十三天,貫穿專案「預約管理系統前端」進入交付階段。今天不寫產品功能,只做一件事:把 42 天累積的知識從「在某個人腦子裡」變成「在專案裡」。具體交付八份資料:一份主說明(README)、一份架構與路由地圖、一份 API 契約對照表、三份決策紀錄(ADR)、一份環境變數清單、一份值班手冊(Runbook)、一份貢獻指南加 PR 模板、一份新成員上線檢核表;再寫一支自動產生路由文件的腳本,讓文件不會跟程式碼脫節。今天所有資料都是虛構示範,沿用 Day 31–42 的共用設定,不連任何外部服務。
引言
大部分 side project 不是死在「做不出來」,而是死在「做出來之後沒人敢動」。三個月後你想加一個新欄位,打開專案才發現:不知道 AuthProvider 為什麼把 access token 放非 httpOnly cookie、不知道 NEXT_PUBLIC_API_MODE 有哪幾種值、不知道 mock 帳號在哪、不知道部署失敗時該先看哪個儀表板。於是你花了半天重新讀程式碼,才敢下第一行修改。這段「重新理解」的時間就是交接成本,而它的高低完全取決於今天這一天有沒有做。
貫穿專案的「預約管理系統」是個小型服務業線上預約平台(情境涵蓋攝影棚、健身教練、諮詢工作室,所有資料都是虛構示範)。專案到 Day 42 為止的樣貌是:Next.js 15 App Router、React 19.x、TypeScript 5.9、Tailwind CSS 4.x;11 個頁面、兩種角色、5 個 reducer 動作的登入狀態機、一套 components/ui/ 設計系統、三層錯誤處理、Vitest 3.x 加 Playwright 1.5x 的測試、以及 Day 41 的部署設定與 Day 42 的品質檢查。
今天的內容分四段:第一段把 Day 31–42 的共用設定再次統整;第二段說明「交接要交付哪四種資料」以及為什麼「為什麼」比「怎麼做」重要;第三段實作八份交接資料與一支自動產生腳本;第四段講文件維護的節奏與常見錯誤。讀完之後你會拿到一套可以直接放進自己專案的資料骨架。
貫穿專案共用設定(Day 31–44 沿用)
今天的交接資料要把從 Day 31 到 Day 42 的設定一次講清楚,這份設定本身就是要交付的核心內容:
- 框架:Next.js 15(App Router、Server Components 預設)、React 19.x、TypeScript 5.9
- 樣式:Tailwind CSS 4.x、CSS variables 主題、Day 32 的
components/ui/ - 後端:FastAPI 預約管理系統(Web 系列 Day 35–44 定義的 REST API);
NEXT_PUBLIC_API_MODE=mock時走lib/mock/*.tsfixture,live時走/api/proxy轉發 - 認證:JWT access token(12 小時,
bf_accesscookie)、refresh token(14 天,httpOnly cookiebf_refresh);沿用 Day 38 的AuthProvider與 5 個 action(BOOTSTRAP / LOGIN / REFRESH / LOGOUT / EXPIRE) - 角色:admin(管理者)、customer(客戶);公開頁面
/、/services、/services/[id]、/login、/register;客戶頁/bookings、/bookings/new;後台/admin、/admin/bookings、/admin/services、/admin/services/[id]/slots - 狀態管理:TanStack Query 5.x(伺服器狀態)、
useState與useReducer(UI 狀態)、Context(使用者狀態) - 品質工具:Vitest 3.x、Playwright 1.5x、axe-core 4.x、ESLint 9 flat config
- 環境變數:
NEXT_PUBLIC_API_BASE_URL、NEXT_PUBLIC_API_MODE、NEXT_PUBLIC_SENTRY_DSN、SENTRY_AUTH_TOKEN、SENTRY_ORG、SENTRY_PROJECT、NEXT_PUBLIC_APP_URL
這份設定從 Day 31 到 Day 44 不變。今天的工作是把這份設定「從口耳相傳變成可查閱」,而且刻意只寫事實與理由,不寫教學。教學在系列裡有 40 多篇可以回頭查;交接資料要回答的是「這個專案現在長什麼樣、為什麼這樣、要改的時候去哪裡改」。
原理解念:交接的四種資料與「為什麼」優先
交接資料可以分成四類,每一類回答一個不同的問題。第一類是導覽型:回答「這是什麼專案、怎麼把它跑起來」。對應 README 與上線檢核表。第二類是規格型:回答「系統由哪些部分組成、彼此怎麼溝通」。對應架構與路由地圖、API 契約對照表、環境變數清單。第三類是決策型:回答「為什麼當初這樣選、什麼情況下應該重新考慮」。對應 ADR。第四類是操作型:回答「發生問題時第一件事做什麼」。對應值班手冊與貢獻指南。四類各有各的讀者與時機:新人第一天讀導覽型,第一週讀規格型,遇到爭議時讀決策型,半夜被叫起來時只讀操作型。
四類之中最常被省略、卻最有價值的是決策型。原因很直觀:程式碼本身會告訴你「做了什麼」,但不會告訴你「為什麼不做另一種」。以 Day 38 的 token 儲存為例,程式碼寫著把 access token 放進非 httpOnly 的 bf_access cookie,如果沒有決策紀錄,接手的人很可能「順手修正」成 localStorage 或純記憶體,結果把 BFF 的中介層打壞。把「因為我們用 Route Handler 做轉發,server 端需要讀得到 token,而純記憶體做不到,所以選擇 SameSite=Lax 的短效 cookie」這一段寫下來,就能保護這個決策不被誤改。
決策型資料的標準格式是 ADR(Architecture Decision Record)。一份 ADR 只需要五個欄位:標題與編號、狀態(提議中/已採用/已取代)、背景(當時面對什麼問題)、決策(選了什麼)、後果(得到什麼、犧牲什麼)。篇幅通常半頁到一頁,不需要寫成論文。ADR 的價值來自「累積」而不是「單篇」:當專案有 20 份 ADR,新成員只要依序讀完,就能重建整個系統的思考脈絡。我們今天會寫三份:token 儲存位置、mock 與 live 雙模式、以及 Server Component 與 Client Component 的分界。
另一個要建立的心態是「文件是程式碼的一部分」。文件放在同一個 repo、同一個 PR、跟著程式碼一起被審查;改動行為的 PR 如果沒有同步更新對應文件,就是未完成的 PR。這條規則聽起來嚴格,但它反而讓文件變少:因為你不再需要維護一份「獨立於程式碼之外、遲早會過期」的文件站。今天寫的 scripts/gen-docs.mjs 就是這個原則的具體落實——路由清單不是手寫的,而是從 app/ 目錄自動產生的,程式碼改了、文件就重跑一次。
完整實作:八份交接資料與自動產生腳本
第一步,先把文件的位置定下來。我們不把所有東西塞進 README,而是用 docs/ 目錄分類,讓每一份有自己的責任:
# 交接資料的目錄結構(全部跟程式碼同一個 repo)
booking-frontend/
├── README.md # 導覽:這是什麼、怎麼跑起來
├── RUNBOOK.md # 操作:出事了先做什麼
├── CONTRIBUTING.md # 操作:怎麼送 PR
├── docs/
│ ├── ARCHITECTURE.md # 規格:架構、路由、資料流
│ ├── API-CONTRACT.md # 規格:前端用到的所有端點
│ ├── env-vars.md # 規格:每個環境變數的用途與來源
│ ├── ONBOARDING.md # 導覽:第一週的檢核表
│ ├── routes.md # 自動產生:路由清單(勿手改)
│ └── adr/
│ ├── 0001-token-storage.md
│ ├── 0002-mock-and-live-mode.md
│ └── 0003-server-vs-client-components.md
└── .github/
└── pull_request_template.md
第二步寫 README。README 是最多人讀、也最常被寫砸的一份。常見的錯誤是「把 README 寫成技術手冊」:貼上完整的架構圖、所有 API、所有環境變數,結果新人讀完還是不會跑。好的 README 只回答三件事:這是什麼、怎麼在本機跑起來、接下來去哪裡看更多。以下用註解形式呈現 Markdown 的內容結構。
# 預約管理系統|前端(booking-frontend)
#
# 小型服務業的線上預約平台前端。客戶可以瀏覽服務、選擇時段、建立預約;
# 管理者可以管理服務品項、確認或取消預約、設定可預約時段。
#
# ## 快速開始
#
# pnpm install
# cp .env.example .env.local # 預設 NEXT_PUBLIC_API_MODE=mock
# pnpm dev # http://localhost:3000
#
# 沒有後端也能跑:mock 模式內建三筆服務與兩筆預約的示範資料。
# 示範帳號:admin@example.com / admin-pass、alice@example.com / alice-pass。
# 這些帳號只存在於 lib/mock/,不會被打包進 production。
#
# ## 常用指令
#
# pnpm dev # 開發伺服器
# pnpm build # production 建置
# pnpm typecheck # TypeScript 檢查
# pnpm lint # ESLint 9
# pnpm test # Vitest 單元與元件測試
# pnpm test:e2e # Playwright 端對端與 a11y 檢查
#
# ## 更多資料
#
# - 架構、路由與資料流:docs/ARCHITECTURE.md
# - API 契約對照:docs/API-CONTRACT.md
# - 環境變數清單:docs/env-vars.md
# - 事故處理:RUNBOOK.md
# - 決策紀錄:docs/adr/
第三步寫架構與路由地圖。這份資料的目標讀者是「要在這裡改東西的人」,所以重點不是畫得漂亮,而是「從畫面找到檔案」與「從資料找到來源」兩條路徑都通。我們用文字樹與表格,不用複雜的圖表工具,因為文字在終端機、在 diff、在 PR 描述裡都能讀。
// docs/ARCHITECTURE.md
//
// ## 分層
//
// app/ 路由與版面(Server Component 為主)
// └── 只負責讀參數、取資料、組版面;不放業務邏輯
// components/ui/ 設計系統(Button、Input、Card、Badge、Dialog、Toast)
// components/ 功能元件(booking/ 客戶端、admin/ 後台)
// lib/api/ 資料層;依 API_MODE 切換 mock 與 live
// lib/mock/ 離線示範資料(fixture)
// hooks/ 共用鉤子(useMediaQuery、useToast)
// tests/ Vitest 與 Playwright
//
// ## 一條資料流(客戶看服務清單)
//
// 1. app/services/page.tsx(Server Component)讀 URL 的 searchParams
// 2. 呼叫 lib/api/services.ts 的 fetchServices(filter)
// 3. fetchServices 依 API_MODE 決定走 mock 或 /api/proxy/services
// 4. 資料以 props 傳進 components/booking/ServicesList(Client Component)
// 5. ServicesList 用 router.replace 把篩選條件寫回 URL
//
// ## 為什麼這樣切
//
// - 讀取與渲染交給 Server Component:少一段 client 的載入狀態
// - 互動與 URL 同步交給 Client Component:需要 useSearchParams 與事件
// - 兩者的交界只有 props;不互相 import 對方的內部檔案
第四步寫 API 契約對照表。這份資料是前端與後端的交接點,最重要的原則是「唯一來源」。我們不在前端自己發明端點,而是照抄 Web 系列 Day 35–44 的定義,並標上出處,讓兩邊永遠對得起來。表格同時標明每個端點需要的角色,這樣寫權限檢查時不必再回去翻規格。
// docs/API-CONTRACT.md
//
// 後端來源:FastAPI 預約管理系統(Web 系列 Day 35-44)
// 前端一律透過 /api/proxy/* 轉發;mock 模式不走網路。
//
// | 方法 | 端點 | 角色 | 用途 |
// | ------ | --------------------------------- | -------- | ------------------ |
// | POST | /auth/register | 公開 | 註冊客戶帳號 |
// | POST | /auth/login | 公開 | 登入並取得 token |
// | POST | /auth/refresh | 公開 | 換發 access token |
// | GET | /services | 公開 | 列出啟用中的服務 |
// | GET | /services/{id}/slots?from=&to= | 公開 | 查可預約時段 |
// | GET | /bookings | customer | 自己的預約 |
// | POST | /bookings | customer | 建立預約 |
// | POST | /bookings/{id}/cancel | customer | 取消預約 |
// | GET | /admin/bookings | admin | 所有客戶預約 |
// | PATCH | /bookings/{id} | admin | 確認或改期 |
// | POST | /services | admin | 新增服務 |
// | PATCH | /services/{id} | admin | 編輯服務 |
//
// 錯誤格式:{ "detail": "找不到服務" },前端在 lib/api/client.ts 統一轉成 ApiError。
// 時間格式:ISO 8601 含時區位移;顯示時一律用 Intl.DateTimeFormat 轉當地時間。
第五步寫環境變數清單。這份資料最常被需要、又最容易缺漏:新人拿到專案,第一個卡住的地方永遠是「少一個環境變數」。每一列都必須寫清楚「用途」「哪個環境要填」「去哪裡拿」,尤其不能只寫變數名。敏感值本身絕對不進 repo,只寫來源與取得方式。
// docs/env-vars.md
//
// | 變數 | 環境 | 用途 | 來源 |
// | --------------------------- | --------------- | ------------------------------- | ------------------------ |
// | NEXT_PUBLIC_API_BASE_URL | 全部 | 後端基底網址 | production / staging 網址 |
// | NEXT_PUBLIC_API_MODE | 全部 | mock 或 live | 固定值 |
// | NEXT_PUBLIC_APP_URL | production | 產生絕對網址(分享、信件連結) | 自訂網域 |
// | NEXT_PUBLIC_SENTRY_DSN | production | 前端錯誤回報 | Sentry 專案設定 |
// | SENTRY_AUTH_TOKEN | build 階段 | 上傳 source map | Sentry 使用者設定 |
// | SENTRY_ORG / SENTRY_PROJECT | build 階段 | 指定要上傳到哪個專案 | Sentry 專案設定 |
//
// 規則一:只有 NEXT_PUBLIC_ 前綴會被送進瀏覽器,其餘只在 server 端可讀。
// 規則二:SENTRY_* 四個變數要嘛全填、要嘛全不填,lib/env.ts 會在啟動時檢查。
// 規則三:新增變數時,同一個 PR 必須同步更新 .env.example 與本文件。
第六步寫決策紀錄。三份 ADR 各對應一個「看起來怪、其實有理由」的設計。每份都短,但每一份都能省下接手的人半天摸索。以下用第一份(token 儲存)當範本,其餘兩份依同一格式撰寫。
// docs/adr/0001-token-storage.md
//
// # ADR 0001:access token 儲存位置
//
// ## 狀態
// 已採用(2026-04-11,Day 38)
//
// ## 背景
// 我們用 Next.js Route Handler 做 BFF,由 server 端轉發請求到 FastAPI。
// server 端讀不到 React 記憶體中的 token,因此 token 必須存在 cookie。
//
// ## 決策
// - access token:非 httpOnly 的 bf_access cookie,Max-Age 12 小時,SameSite=Lax
// - refresh token:httpOnly 的 bf_refresh cookie,14 天,僅由 /api/auth/refresh 讀取
//
// ## 後果
// 好處:BFF 轉發時讀得到 token,程式碼大幅簡化;refresh token 不受 XSS 影響。
// 代價:access token 對 JavaScript 可見,XSS 成功時最長有 12 小時的冒用風險。
// 重新考慮的時機:若未來改成純 SPA(無 server 中介層),應改存記憶體。
第七步是 Runbook。Runbook 的讀者通常在壓力下閱讀,所以它的寫法跟其他資料完全不同:短句、編號、先做什麼再確認什麼,不要有背景說明。Day 41 已經寫過第一版,今天把「已知限制」補進去,讓值班的人知道哪些問題現在沒有解法、該找誰。
// RUNBOOK.md(Day 43 補充:已知限制)
//
// ## 已知限制(不是故障,不要重開服務)
//
// 1. Serverless 冷啟動:閒置後第一次請求約慢 200-500 毫秒,屬預期行為。
// 2. Preview 環境的 Sentry 是獨立專案:在 Preview 看到的錯誤不會出現在 production。
// 3. mock 模式不會寫入任何資料:重新整理後示範預約會回到初始狀態。
//
// ## 部署失敗
// 1. 開 Vercel → Deployments → 看最新一筆的 build log。
// 2. 若是 Type error,本機先跑 pnpm typecheck 重現。
// 3. 修好後 push 到 main,Vercel 會自動重新部署。
//
// ## production 出現大量 5xx
// 1. 開 Sentry → Issues,找最熱門的錯誤。
// 2. 到 Vercel → Deployments,把上一個成功的版本 Promote to Production。
// 3. 在事故頻道開一個討論串,附上 Sentry 與 Vercel 的截圖。
第八步寫貢獻指南與 PR 模板。這份資料的功能是「把隱性規範變成顯性檢查」,讓每個 PR 在送出前就自我檢查一次。我們刻意把檢查寫少,只留真的會被擋下的幾條,否則模板會變成沒人看的例行公事。
// .github/pull_request_template.md
//
// ## 這個 PR 做了什麼
// (一句話說明,並附上相關的 issue 編號)
//
// ## 我怎麼驗證
// - [ ] pnpm typecheck 與 pnpm lint 通過
// - [ ] pnpm test 通過(新增行為有對應測試)
// - [ ] pnpm test:e2e 通過(含 a11y 與四斷點檢查)
// - [ ] 手動走過受影響的流程(桌機與 390 寬)
//
// ## 需要同步更新的資料
// - [ ] docs/ARCHITECTURE.md(若改動分層或路由)
// - [ ] docs/API-CONTRACT.md(若改動使用的端點)
// - [ ] docs/env-vars.md 與 .env.example(若新增環境變數)
// - [ ] docs/adr/(若做出難以回頭的技術選擇)
最後是自動產生腳本。手寫的路由清單一定會過期,所以我們讓它自己長出來:掃描 app/ 目錄、找出所有 page.tsx、把 route group(例如 (auth))排除在 URL 之外,再依第一層目錄推斷需要的角色,輸出成一份 Markdown 表格。這支腳本只用 Node 內建模組,可以直接執行。
// scripts/gen-docs.mjs
// 掃描 app/ 目錄產生 docs/routes.md,讓路由文件不會跟程式碼脫節。
// 執行:node scripts/gen-docs.mjs
import { readdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
const APP_DIR = path.resolve('app');
const ROLE_BY_PREFIX = { admin: 'admin', bookings: 'customer' };
async function walk(dir, segments = []) {
const entries = await readdir(dir, { withFileTypes: true });
const routes = [];
for (const entry of entries) {
if (entry.isDirectory()) {
// route group 例如 (auth) 不進 URL,所以不加入路徑片段
const next = entry.name.startsWith('(') ? segments : [...segments, entry.name];
routes.push(...(await walk(path.join(dir, entry.name), next)));
} else if (entry.name === 'page.tsx') {
routes.push(`/${segments.join('/')}`.replace(/\/$/, '/'));
}
}
return routes.sort();
}
function roleOf(route) {
const head = route.replace(/^\//, '').split('/')[0];
return ROLE_BY_PREFIX[head] ?? 'public';
}
const routes = await walk(APP_DIR);
const rows = routes.map((route) => {
const file = route === '/' ? 'app/page.tsx' : `app${route.replace(/\/$/, '')}/page.tsx`;
return `| \`${route}\` | ${roleOf(route)} | \`${file}\` |`;
});
const markdown = [
'# 路由清單',
'',
'由 scripts/gen-docs.mjs 自動產生,請勿手動編輯。',
'',
'| URL | 角色 | 來源檔案 |',
'| --- | --- | --- |',
...rows,
'',
].join('\n');
await writeFile('docs/routes.md', markdown, 'utf8');
console.log(`已產生 docs/routes.md,共 ${routes.length} 條路由`);
常見錯誤與踩雷
第一個常見錯誤是「README 寫成技術手冊」。把所有架構、所有端點、所有環境變數都塞進 README,結果新人讀完還是不會把專案跑起來。修法是把 README 的功能收斂成「讓人 5 分鐘內跑起來」,其餘內容拆到 docs/ 底下並在 README 末端放一張索引。判斷標準很簡單:如果一段內容的讀者是「已經跑起來、要改東西的人」,它就不該在 README。
第二個踩雷是「文件在獨立的地方」。把交接資料放在共用的雲端硬碟、或是另一個 wiki,短期看起來很乾淨,長期一定會過期——因為改程式碼的人不會記得去改另一份。修法是把資料放進同一個 repo,並在 PR 模板裡加上「需要同步更新的資料」檢查項。我們今天的做法就是如此:文件在版控裡、跟著 PR 一起被審查,甚至可以寫測試檢查連結是否失效。
第三個是「只寫怎麼做、不寫為什麼」。這是最容易犯、代價也最高的一個。程式碼已經寫了「怎麼做」,再抄一次只是浪費;真正需要留下的是取捨的理由,例如「為什麼用 cookie 不用記憶體」、「為什麼公開頁面用 ISR 而個人頁面強制動態」。沒有這層理由,接手的人會以為那是疏忽而「順手修好」,把一個刻意的設計改壞。今天三份 ADR 就是為了這件事。
第四個是「把 mock 示範資料當成規格寫進交接資料」。mock 帳號與示範資料是為了讓專案能離線跑的權宜設計,不是產品規格。如果交接資料只寫 mock 的形狀、沒寫真實 API 契約,接手的人會照著 mock 開發,上線後才發現欄位對不上。修法是把 mock 明確標為示範,並以 docs/API-CONTRACT.md 為唯一規格來源,同時提醒 mock 帳號不會被打包進 production。
第五個是「Runbook 寫成教學」。Runbook 的讀者在半夜、有壓力、只想解決眼前的問題,任何背景說明都是干擾。修法是把每一段寫成「先做 A、再做 B、若 C 就 D」的編號步驟,並且把「這不是故障」的已知限制也列進去——很多深夜告警其實是預期行為,先寫下來可以省下一次無意義的搶修。
效能與實務提醒
交接資料的維護成本要刻意壓低,否則寫完就過期。我們用三個機制來控制:第一,能自動產生的就自動產生(今天的路由清單),人工只寫推導不出來的部分(理由與取捨)。第二,把更新時機綁在既有流程上(PR 模板的檢查項),而不是靠「記得要更新」。第三,讓每份資料都短:README 一頁、ADR 半頁、Runbook 兩頁。短的文件才有機會被維護,長的文件一定過期。
另一件事是「寫給未來的自己」。大多數 side project 沒有第二位開發者,要交接的人其實就是六個月後的自己。判斷一份資料該不該寫有個實用標準:如果六個月後的我會多花超過 15 分鐘才想起來,就值得寫。
關於自動產生腳本,要注意它產生的是「事實」而不是「判斷」。今天的 gen-docs.mjs 能準確列出路由與推斷出的角色,但它沒辦法判斷「這個頁面為什麼需要 admin」。因此自動產生的檔案要開頭標明「請勿手動編輯」,而判斷性內容寫在 ARCHITECTURE.md 與 ADR 裡,兩者互相連結。
最後是文件的可驗證性。文件很容易腐化,因為沒有人會測它。低成本的防線有兩條:一是把 gen-docs.mjs 放進 CI,產生的內容與版控內容不一致就失敗(等同於「文件沒同步」的告警);二是把文件裡的指令抽出來當測試,例如確認 README 列的每一個 pnpm 指令真的存在於 package.json。這兩條加起來不到 30 行程式,卻能擋掉最常見的「文件寫了一個早就改名的指令」。
小結
今天把預約管理系統前端從「能跑」推進到「能交」。我們建立了 docs/ 目錄結構、寫了 README(導覽)、ARCHITECTURE.md(分層與資料流)、API-CONTRACT.md(12 個端點的對照表)、env-vars.md(7 個環境變數的來源與規則)、三份 ADR(token 儲存、雙模式、Server 與 Client 的分界)、Runbook(含已知限制)、PR 模板(把規範變成檢查項),最後寫了一支 scripts/gen-docs.mjs 自動產生路由清單。整套資料不引入任何新依賴,全部跟程式碼同一個 repo。
回頭看今天的產出,可以歸納成一句話:交接資料的價值不在「記錄了什麼」,而在「省下了誰的時間」。README 省下新人摸索環境的時間,API 契約表省下前後端對欄位的時間,ADR 省下重新論辯同一個問題的時間,Runbook 省下事故當下的判斷時間。每一份都對應一個具體的、反覆發生的浪費;沒有對應到浪費的文件,就不該寫。
也因此,今天刻意沒有做完整的端點參考手冊或逐行註解導覽——那兩件事成本高、折舊快,真正需要的讀者卻少。判斷一份交接資料值不值得寫,問的不是「這資訊重要嗎」,而是「這個問題會被重複問幾次」。
結語
今天的重點是把專案從「在某個人腦子裡」搬到「在專案裡」。我們刻意把導覽、規格、決策、操作四類資料分開寫,因為它們的讀者、時機與篇幅都不同;也刻意寫了一支會自動產生文件的小腳本,示範「文件也該有自己的自動化」。明天,我們會做整個系列的收尾:把 44 天的路徑完整回顧一次,盤點這個專案最後交付了什麼、哪些決定最關鍵、哪些地方還有明顯的改進空間;然後跑一次完整的驗收流程(型別檢查、lint、單元測試、端對端與 a11y 檢查、production build),把今天的交接資料當成驗收清單的一部分,確認這份專案真的能交給下一個人。
延伸資源
- Architecture Decision Records 社群:
https://adr.github.io/,ADR 的格式、範本與實務案例。 - Diátaxis 文件分類框架:
https://diataxis.fr/,把文件分成教學、操作、參考、說明四類的判斷準則。 - Keep a Changelog:
https://keepachangelog.com/zh-TW/1.1.0/,變更紀錄的撰寫慣例(繁體中文版)。 - GitHub 文件 — PR 模板:
https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates,PR 與 issue 模板的設定方式。 - Next.js 專案結構說明:
https://nextjs.org/docs/app/getting-started/project-structure,App Router 的目錄慣例。 - Day 41 部署實戰:原文連結,環境變數與 Runbook 的第一版。
留言
張貼留言