發(fā)實(shí)戰(zhàn)教程(八):先讓項(xiàng)目能穩(wěn)定地跑起來(lái))
“AI 軟件開(kāi)發(fā)實(shí)戰(zhàn)教程”系列第 7 篇把產(chǎn)品規(guī)劃、工程架構(gòu)和頁(yè)面設(shè)計(jì)整理成縱向開(kāi)發(fā)任務(wù)、狀態(tài)看板與驗(yàn)收覆蓋矩陣讓 TDD 不會(huì)變成一堆彼此失聯(lián)的測(cè)試。到上一篇為止鄰行已經(jīng)有了三份重要事實(shí)源產(chǎn)品規(guī)劃說(shuō)明首版做什么、為什么做和怎樣驗(yàn)收工程架構(gòu)說(shuō)明事務(wù)、并發(fā)、權(quán)限、后臺(tái)任務(wù)和測(cè)試邊界頁(yè)面體驗(yàn)說(shuō)明用戶(hù)看到什么、怎樣操作以及各種狀態(tài)如何表達(dá)?,F(xiàn)在可以開(kāi)始寫(xiě)代碼了嗎還差一個(gè)容易被低估的步驟把這些橫跨幾十頁(yè)的規(guī)則拆成可以獨(dú)立完成、獨(dú)立驗(yàn)證和獨(dú)立提交的任務(wù)。如果直接讓 AI“按照文檔把整個(gè)項(xiàng)目實(shí)現(xiàn)出來(lái)”常見(jiàn)結(jié)果是先一次性創(chuàng)建所有數(shù)據(jù)庫(kù)模型再一次性創(chuàng)建所有頁(yè)面最后補(bǔ)一些測(cè)試看板顯示功能很多實(shí)際沒(méi)有一條完整流程能運(yùn)行產(chǎn)品規(guī)劃中的邊界散落在代碼里沒(méi)人知道有沒(méi)有遺漏。這次使用dev-harness-planning生成計(jì)劃但沒(méi)有把模板填滿(mǎn)就算完成。計(jì)劃必須證明兩件事每個(gè)任務(wù)能產(chǎn)生一個(gè)可運(yùn)行結(jié)果所有產(chǎn)品驗(yàn)收?qǐng)鼍岸加形ㄒ回?fù)責(zé)人??窗搴腿蝿?wù)詳情為什么要分開(kāi)一個(gè)計(jì)劃文檔同時(shí)寫(xiě)狀態(tài)、背景、文件、步驟、測(cè)試和風(fēng)險(xiǎn)很快就會(huì)變得難以?huà)呙琛`徯邪延?jì)劃分成三層Dashboard.md 當(dāng)前階段、任務(wù)狀態(tài)、優(yōu)先級(jí)和跳轉(zhuǎn) TaskDetails.md 每個(gè)任務(wù)的目標(biāo)、文件、TDD 步驟、驗(yàn)證和提交邊界 AcceptanceMatrix.md AC-01 到 AC-56 的負(fù)責(zé)人、證據(jù)類(lèi)型和真實(shí)狀態(tài)看板只回答“現(xiàn)在做到哪里、下一項(xiàng)是什么”。任務(wù)詳情回答“這項(xiàng)工作怎樣做完”。驗(yàn)收矩陣回答“哪一條產(chǎn)品規(guī)則由誰(shuí)證明”。三份文檔職責(zé)不同可以避免在多個(gè)地方復(fù)制同一大段實(shí)現(xiàn)說(shuō)明。任務(wù)狀態(tài)變化時(shí)更新看板測(cè)試證據(jù)變化時(shí)更新驗(yàn)收矩陣實(shí)現(xiàn)步驟只在任務(wù)詳情維護(hù)。不按技術(shù)層拆而按用戶(hù)閉環(huán)拆一種常見(jiàn)拆法是任務(wù)一設(shè)計(jì)全部數(shù)據(jù)表 任務(wù)二實(shí)現(xiàn)全部后端接口 任務(wù)三實(shí)現(xiàn)全部前端頁(yè)面 任務(wù)四補(bǔ)自動(dòng)測(cè)試這種拆法對(duì)分工看起來(lái)整齊對(duì)驗(yàn)證卻很不友好。完成第一項(xiàng)以后用戶(hù)還不能做任何事完成第二項(xiàng)以后仍然沒(méi)有可用頁(yè)面到了最后才發(fā)現(xiàn)表結(jié)構(gòu)或接口不適合真實(shí)流程前面的“完成”需要大面積返工。鄰行改用縱向切片社區(qū)邀請(qǐng)、登錄與私密資料 → 從規(guī)則、數(shù)據(jù)庫(kù)、服務(wù)到真實(shí)注冊(cè)頁(yè)面一起完成 結(jié)構(gòu)化發(fā)布、信息大廳與詳情 → 從時(shí)間規(guī)則、發(fā)布冪等到移動(dòng)頁(yè)面一起完成 候選計(jì)算、解釋與站內(nèi)事件 → 從匹配純函數(shù)到雙方頁(yè)面一起完成 聯(lián)系方式雙向交換 → 從權(quán)限、事務(wù)、加密快照到復(fù)制降級(jí)一起完成每項(xiàng)完成后都多出一條可以實(shí)際運(yùn)行的用戶(hù)能力也能立即用瀏覽器檢查架構(gòu)和頁(yè)面設(shè)計(jì)是否真的成立。技術(shù)層仍然存在但它們服務(wù)于同一個(gè)切片而不是各自成為“已經(jīng)完成”的孤島。第一個(gè)任務(wù)不是業(yè)務(wù)功能在縱向開(kāi)發(fā)以前計(jì)劃保留一個(gè)前置任務(wù) V0工程骨架與驗(yàn)證契約。它要建立Python、Django 和 PostgreSQL 的鎖定環(huán)境Web、Worker 和數(shù)據(jù)庫(kù)的本地運(yùn)行方式自定義用戶(hù)模型的第一條遷移格式、靜態(tài)檢查、單元、集成和瀏覽器測(cè)試可注入時(shí)鐘不會(huì)調(diào)用真實(shí)第三方的假提醒渠道setup、quick、test、e2e、check 等統(tǒng)一命令。V0 不是先搭一套宏大平臺(tái)。它只負(fù)責(zé)讓后面的每個(gè)任務(wù)都能以相同方式啟動(dòng)、測(cè)試和驗(yàn)收。例如產(chǎn)品大量依賴(lài)截止時(shí)刻如果沒(méi)有可注入時(shí)鐘測(cè)試就會(huì)靠真實(shí)等待或修改系統(tǒng)時(shí)間既慢又不穩(wěn)定。又例如最后一個(gè)座位依賴(lài) PostgreSQL 行鎖如果測(cè)試環(huán)境默認(rèn)偷偷使用 SQLite測(cè)試通過(guò)也不能說(shuō)明并發(fā)正確。驗(yàn)證環(huán)境本身是產(chǎn)品正確性的一部分。每個(gè)任務(wù)都先寫(xiě)“怎樣失敗”任務(wù)詳情沒(méi)有只列“實(shí)現(xiàn)賬號(hào)、實(shí)現(xiàn)發(fā)布、實(shí)現(xiàn)匹配”而是為每項(xiàng)寫(xiě)了 RED、GREEN、REFACTOR。以聯(lián)系方式交換為例RED 非候選、跨社區(qū)、受限賬戶(hù)、失效候選必須拒絕 重復(fù)點(diǎn)擊和并發(fā)點(diǎn)擊不能產(chǎn)生兩次交換 非參與者和頁(yè)面源碼不能看到微信號(hào) GREEN 實(shí)現(xiàn)短事務(wù)、重新校驗(yàn)、一對(duì)一交換和加密快照 雙方同時(shí)得到對(duì)方微信號(hào) REFACTOR 模板上下文只裝入當(dāng)前用戶(hù)有權(quán)看到的一方資料 權(quán)限判斷集中在服務(wù)不散落在頁(yè)面“先寫(xiě)失敗測(cè)試”不是追求紅色輸出本身。測(cè)試必須因?yàn)槟繕?biāo)能力尚未實(shí)現(xiàn)而失敗而不是因?yàn)閷?dǎo)入路徑寫(xiě)錯(cuò)、數(shù)據(jù)庫(kù)沒(méi)啟動(dòng)或測(cè)試代碼本身報(bào)錯(cuò)。確認(rèn)紅燈原因正確以后才寫(xiě)最小實(shí)現(xiàn)讓它變綠。一個(gè)任務(wù)要同時(shí)擁有多個(gè)證據(jù)層次不是所有規(guī)則都適合用同一種測(cè)試。匹配時(shí)間窗口可以用純函數(shù)測(cè)試社區(qū)權(quán)限需要 HTTP 集成測(cè)試最后一個(gè)座位需要 PostgreSQL 雙連接并發(fā)復(fù)制降級(jí)需要瀏覽器微信分享和會(huì)話(huà)保持最終需要真機(jī)。因此任務(wù)詳情為每項(xiàng)規(guī)定證據(jù)層次純規(guī)則 → 時(shí)間、地點(diǎn)、狀態(tài)、提醒分類(lèi) 服務(wù)和數(shù)據(jù)庫(kù) → 冪等、授權(quán)、事務(wù)、事件一致性 HTTP → 登錄、跨社區(qū)、CSRF、表單和源碼 Playwright → 兩個(gè)用戶(hù)的完整移動(dòng)頁(yè)面流程 真實(shí)設(shè)備和外部人員 → 微信 Gate、群管理員、隱私與法律審查越接近用戶(hù)環(huán)境的測(cè)試覆蓋面越大但它不能代替更底層的精確規(guī)則測(cè)試。反過(guò)來(lái)單元測(cè)試再多也不能證明微信內(nèi)置瀏覽器真的可用。56 個(gè)驗(yàn)收?qǐng)鼍盀槭裁葱枰獑为?dú)矩陣產(chǎn)品規(guī)劃已經(jīng)寫(xiě)了 56 個(gè)驗(yàn)收?qǐng)鼍?。如果只在任?wù)描述中隨手標(biāo)幾個(gè)編號(hào)很難發(fā)現(xiàn)某一條沒(méi)人負(fù)責(zé)同一條被多個(gè)任務(wù)都認(rèn)為由對(duì)方負(fù)責(zé)人工 Gate 被寫(xiě)成自動(dòng)化已通過(guò)任務(wù)完成后沒(méi)有真實(shí)測(cè)試路徑。驗(yàn)收矩陣為 AC-01 到 AC-56 每條記錄場(chǎng)景摘要唯一負(fù)責(zé)人計(jì)劃證據(jù)當(dāng)前真實(shí)狀態(tài)。生成以后做了機(jī)器檢查編號(hào)范圍AC-01–AC-56 總數(shù)56 順序完整是 重復(fù)0 遺漏0 當(dāng)前自動(dòng)通過(guò)0最后一行很重要。計(jì)劃覆蓋了 56 條不代表實(shí)現(xiàn)通過(guò)了 56 條。當(dāng)前狀態(tài)全部是“未實(shí)現(xiàn)”或“等待成品”這才符合項(xiàng)目事實(shí)?!皡f(xié)作任務(wù)”和“最終負(fù)責(zé)人”要分開(kāi)一條驗(yàn)收往往跨多個(gè)模塊。例如 AC-24 要求第三方提醒失敗不能回滾發(fā)布、候選或交換而且一方失敗不影響另一方。候選任務(wù)會(huì)創(chuàng)建業(yè)務(wù)事件交換任務(wù)會(huì)保存交換事實(shí)提醒任務(wù)會(huì)處理發(fā)送失敗。三項(xiàng)都參與但驗(yàn)收矩陣仍把 K7 提醒任務(wù)設(shè)為最終負(fù)責(zé)人。這樣關(guān)閉任務(wù)時(shí)不會(huì)出現(xiàn)K3我已經(jīng)創(chuàng)建事件剩下不是我的問(wèn)題 K4交換已經(jīng)保存提醒由別人測(cè)試 K7上游應(yīng)該已經(jīng)保證事務(wù)我只測(cè) HTTP協(xié)作關(guān)系可以有多個(gè)最終關(guān)閉責(zé)任只能有一個(gè)。把最危險(xiǎn)的測(cè)試單獨(dú)標(biāo)出來(lái)驗(yàn)收矩陣沒(méi)有把所有場(chǎng)景都寫(xiě)成“自動(dòng)測(cè)試”。幾類(lèi)證據(jù)被明確加粗AC-27 最后一個(gè)座位必須在 PostgreSQL 做真實(shí)并發(fā)AC-23、AC-39、AC-40必須主動(dòng)掃描“不包含”敏感資料AC-31 到 AC-35只能由 iOS 和 Android 微信真機(jī)最終通過(guò)AC-39除了代碼和備份檢查還需要隱私與法律審查共同關(guān)閉。這能防止自動(dòng)化系統(tǒng)為了提高通過(guò)率用容易執(zhí)行但證據(jù)不足的測(cè)試替換真實(shí)要求。外部 Gate 也要進(jìn)看板但不能自動(dòng)完成Gate B 和 Gate C 被當(dāng)作正式任務(wù)保留G1微信成品真機(jī)驗(yàn)收 狀態(tài)等待可運(yùn)行成品 G2受控試用準(zhǔn)備 狀態(tài)等待外部人員與合規(guī)證據(jù)它們有明確輸入、步驟和通過(guò)條件卻不會(huì)在 AI 完成代碼后自動(dòng)變綠。G1 需要真實(shí) iOS、Android、微信版本和測(cè)試群G2 需要群管理員同意、地點(diǎn)確認(rèn)、七天基線(xiàn)、試用名單以及隱私和法律意見(jiàn)。計(jì)劃可以替這些工作準(zhǔn)備記錄模板不能替責(zé)任人簽字??窗宓臓顟B(tài)必須反映事實(shí)鄰行統(tǒng)一使用幾種狀態(tài) 規(guī)劃中任務(wù)定義完成代碼尚未開(kāi)始 開(kāi)發(fā)中當(dāng)前正在執(zhí)行而且同時(shí)只能有一個(gè)? 已完成退出條件和證據(jù)全部滿(mǎn)足?? 等待成品/外部任務(wù)定義清楚但缺少真實(shí)輸入 遠(yuǎn)期沒(méi)有真實(shí)數(shù)據(jù)支持現(xiàn)在進(jìn)入首版。創(chuàng)建了文件不等于任務(wù)完成測(cè)試通過(guò)一部分也不等于任務(wù)完成AI 暫時(shí)停止更不等于任務(wù)受阻。每個(gè)任務(wù)完成時(shí)必須同時(shí)更新看板狀態(tài)任務(wù)詳情中的驗(yàn)證記錄驗(yàn)收矩陣中的真實(shí)證據(jù)路徑節(jié)點(diǎn)檢查點(diǎn)本地 Git 提交。這讓第二天查看項(xiàng)目的人不必從聊天記錄猜測(cè)實(shí)際進(jìn)度。計(jì)劃也要接受自動(dòng)檢查文檔不是代碼但仍然可以做一些確定性驗(yàn)證。本次計(jì)劃?rùn)z查了Dashboard 中有 14 個(gè)任務(wù)TaskDetails 中有對(duì)應(yīng)的 14 個(gè)任務(wù)標(biāo)題驗(yàn)收矩陣恰好有 56 行編號(hào)嚴(yán)格等于 1 到 56沒(méi)有重復(fù)沒(méi)有TBD、TODO、FIXME等模板殘留Markdown 沒(méi)有明顯空白錯(cuò)誤。這不能證明任務(wù)拆分一定完美卻能消除鏈接缺失、編號(hào)漏掉和模板沒(méi)填完這類(lèi)低級(jí)錯(cuò)誤。本節(jié)點(diǎn)形成的實(shí)際開(kāi)發(fā)順序鄰行首版最終按這個(gè)順序推進(jìn)V0 工程骨架 → K1 社區(qū)訪(fǎng)問(wèn)與私密資料 → K2 發(fā)布、大廳和詳情 → K3 候選與站內(nèi)事件 → K4 聯(lián)系方式交換 → K5 雙方反饋與座位 → K6 編輯、截止和狀態(tài)生命周期 → K7 外部提醒 Worker → K8 刪除、審計(jì)和社區(qū)管理 → K9 完整移動(dòng)瀏覽器驗(yàn)收 → K10 部署、恢復(fù)和試用數(shù)據(jù) → G1 微信真機(jī) → G2 受控試用準(zhǔn)備K4 完成以后登錄—詳情—交換—復(fù)制的首條成品閉環(huán)已經(jīng)存在可以開(kāi)始準(zhǔn)備微信真機(jī)測(cè)試但完整 Gate B 仍等到 K9 頁(yè)面和瀏覽器質(zhì)量收束。寫(xiě)在最后一個(gè)好計(jì)劃不是把所有工作都提前寫(xiě)得很詳細(xì)。它應(yīng)該讓下一步足夠明確讓每條產(chǎn)品規(guī)則都有負(fù)責(zé)人讓不同證據(jù)不會(huì)互相冒充也讓遠(yuǎn)期想法不會(huì)偷偷混進(jìn)首版。現(xiàn)在鄰行的下一步已經(jīng)非常具體執(zhí)行 V0先寫(xiě)配置和健康檢查的失敗測(cè)試建立 PostgreSQL 與統(tǒng)一驗(yàn)證命令。從這一刻開(kāi)始后續(xù)教程會(huì)進(jìn)入真正的 TDD 開(kāi)發(fā)。文章不會(huì)只展示最后的綠色測(cè)試還會(huì)記錄第一條測(cè)試為什么失敗、最小實(shí)現(xiàn)怎樣讓它通過(guò)、重構(gòu)改變了什么、瀏覽器看到了什么以及還有哪些真實(shí) Gate 不能由 AI 關(guān)閉。本篇驗(yàn)證摘要計(jì)劃按用戶(hù)可完成的縱向流程拆分而不是按模型、頁(yè)面和接口橫向切塊每項(xiàng)任務(wù)都記錄產(chǎn)品規(guī)則、第一條失敗測(cè)試、實(shí)現(xiàn)范圍和完成門(mén)禁56 條驗(yàn)收?qǐng)鼍胺謩e標(biāo)注負(fù)責(zé)人、自動(dòng)證據(jù)、瀏覽器證據(jù)或人工驗(yàn)證要求PostgreSQL 并發(fā)、微信真機(jī)和真實(shí)用戶(hù)接受度不會(huì)被普通單元測(cè)試代替看板允許“部分通過(guò)”和“環(huán)境待驗(yàn)”避免任務(wù)完成被誤寫(xiě)成產(chǎn)品已上線(xiàn)。附錄相關(guān)工具與倉(cāng)庫(kù)gstack倉(cāng)庫(kù)garrytan/gstack地址https://github.com/garrytan/gstackdev-harness倉(cāng)庫(kù)Dev-Wiki/dev-harness地址https://github.com/Dev-Wiki/dev-harnessUI UX Pro Max Skill倉(cāng)庫(kù)nextlevelbuilder/ui-ux-pro-max-skill地址https://github.com/nextlevelbuilder/ui-ux-pro-max-skill