AI 亂改程式碼?試試這套 SDD 規範驅動工作流

古茗前端團隊 team icon

avatar

古茗前端團隊 @古茗科技

作者:李天生

一、前言

在 AI Coding 快速普及的這兩年,越來越多團隊開始使用 Codex、Cursor、Claude Code 等工具輔助開發。圍繞這些工具,也出現了大量關於 Prompt、Skills、MCP 的使用教學。但很多內容都停留在「單點提效」層面:教你怎麼寫 Prompt、怎麼裝工具、怎麼呼叫能力,卻很少回答一個更關鍵的問題:當 AI 進入研發流程後,我們該如何建立一套穩定、可持續、可複用的工程協作方式?

這篇文章就圍繞這個問題展開。先看一下團隊在使用 AI 輔助開發時最常見的幾個問題:

  • Prompt 過於簡單,AI 取得的資訊不足,會自行補全上下文,導致實作結果跑偏
  • 一次性丟給 AI 的資訊太多,但沒有明確重點和驗收標準,最終輸出容易失焦
  • 程式碼實作可能不符合團隊規範,甚至無意中擴大改動範圍
  • 一些關鍵術語和業務背景只存在於當前會話,新開會話後又要重新解釋

這些問題的癥結,往往在於研發上下文缺乏有效組織,這是單純優化 Prompt 難以跨越的瓶頸。因此,我們需要將重心轉向工程化,搭建一套讓 AI 在既定規則內穩定運行的流程。這正是今天我們要探討的核心——SDD(Spec-Driven Development,規格驅動開發)。

二、SDD 規格驅動開發

SDD 是什麼

SDD(Spec-Driven Development,規格驅動開發)並不是一個全新的概念。它在軟體工程領域很早就已經出現,核心理念是:先定義規則,再進行實作。只是過去編寫和維護規格文件的成本較高,所以並沒有真正成為主流開發方式。

隨著 2024、2025 年 AI Coding 的快速發展,AI 已經可以高效生成程式碼,但也暴露出需求理解不穩、改動範圍失控、上下文無法持續複用等問題。在這個背景下,SDD 被重新討論起來。比如 GitHub 的 Spec Kit 就把 AI 開發流程固化為 Specify - Plan - Tasks - Implement,讓 AI 更像一個執行者,而人的重心則前移到需求定義、架構設計、約束說明和驗收標準上。

SDD 與傳統方案的差異

熟悉傳統研發流程的人會發現,這其實就是「先寫技術方案再開發」的 AI 版本,但也和以前略有不同。

與傳統方案相比,SDD 的差異大致如下:

維度 傳統方案 SDD
文件讀者 AI + 人
文件粒度 可讀即可 需要結構化,便於 AI 解析
驗收標準 相對模糊 盡量可驗證、可判斷
文件與程式碼關係 容易逐漸脫節 由規格驅動實作,更容易保持一致

看到這裡,很多人會有一個自然的疑問:

原本寫方案就已經很耗時了,現在還要把文件寫得更細、更結構化,成本會不會更高?

答案是:現在已經有很多成熟的工具可以輔助我們編寫 Spec,整體成本其實更低。

其實在生成 Spec 時,我們也會進一步加深對需求的理解,而這部分投入換來的,是後續編碼階段更低的返工率和更穩定的協作品質。

SDD 如何解決這些問題

前面提到的「AI 亂改程式碼」,很多時候並不是單一問題,而是幾個問題疊在一起:

  • AI 不知道需求邊界,會自行補邏輯
  • AI 不知道驗收標準,有可能會提前結束
  • AI 不理解專案約束,容易改出不符合團隊習慣的程式碼
  • AI 缺少長期上下文,新會話裡又會重複犯同樣的問題

SDD 要解決的,就是把這些原本藏在需求評審、技術方案、開發經驗裡的資訊,提前整理成 AI 可以持續讀取、複用的規格文件。比如:

Spec 解決「要做什麼、不能做什麼」的問題。它會寫清楚使用者情境、功能邊界、異常情況和驗收標準。這樣 AI 在實作時就不需要自己猜需求,也不容易把無關邏輯一起改掉。

Design 解決「應該怎麼做」的問題。這裡會說明我們的技術方案、模組影響範圍、資料流、介面依賴和相容性要求等。

Tasks 解決「先做哪一塊、做到什麼程度」的問題。它會把需求拆成可執行、可驗證的小任務。AI 每次只處理一個明確範圍,改動就更容易收斂,也更方便人做 review 和回滾。

這樣一來,AI 從分析、規劃到編碼,都能圍繞同一組規格文件展開。人負責定義規則和判斷結果,AI 負責在規則內完成實作。隨著這些 Spec 文件在專案中持續沉澱,它們還會逐漸形成專案級知識庫。後續再開啟新會話時,AI 不需要從零理解業務,也不需要反覆解釋同一批規則,協作穩定性會明顯提升。

三、工具選型

OpenSpec

目前 SDD 已經出現了很多優秀的工具幫助我們提效,較常見的方案包括 OpenSpecSpec Kitsuperpowers 等。

如果團隊希望輕量導入、盡快落地,我會更傾向於選擇 OpenSpec。它不一定適合所有場景,但對現有團隊流程的侵入更小,落地門檻也更低。

初始化方式比較直接:安裝 OpenSpec 後,在專案中執行 openspec init,再選擇對應的 AI Coding 工具即可生成如下目錄結構:

plain 代碼解讀複製代碼openspec/
├── specs/              # Source of truth (your system's behavior)
│   └── <domain>/
│       └── spec.md
├── changes/            # Proposed updates (one folder per change)
│   └── <change-name>/
│       ├── proposal.md
│       ├── design.md
│       ├── tasks.md
│       └── specs/      # Delta specs (what's changing)
│           └── <domain>/
│               └── spec.md
└── config.yaml         # Project configuration (optional)

我們重點關注如下幾個核心檔案:

檔案 作用
spec.md 定義需求、邊界、行為、約束、驗收標準,作為 AI 的長期上下文
design.md 記錄具體的技術方案與實作設計
proposal.md 描述背景、目標、影響範圍與推進理由
tasks.md 任務拆解與執行進度參考

以 Codex 為例,如果已經接入了對應能力,可以透過 /opsx 的快捷指令快速進入這套流程。

更多能力可以參考 OpenSpec GitHub

工作流程

OpenSpec 只是這套工作流的一種承載方式,核心不是某個具體工具,而是「先定義規格,再約束執行,再持續沉澱」的協作方式。在理想狀態下,我們不直接從程式碼開始,而是先定義和持續修正 Spec 文件,再讓 AI 圍繞這些文件進行 Coding。

一次 OpenSpec 的生命週期大致如下:

整體架構

OpenSpec 解決了「按規格推進開發」的主流程,但要讓 AI 輸出更穩定,還需要把上下文取得、專案規則和自動化校驗一起補齊。整體上,可以把這套協作方式理解為下面這張圖:

四、實際落地

只看流程圖和整體的架構圖,很多沒實作過的人還是會覺得抽象,可能並不清楚 MCP、Skills 等約束在裡面扮演的角色,也不清楚在什麼階段應該使用哪個指令。下面我們沿著一次常見的 AI Coding 協作流程,看看 OpenSpec 的一套流程在實際研發中是怎麼落地。

生成 Spec

一次複雜需求開始時,我們可以先使用 /opsx:propose + 需求內容、技術方案等 生成一版 Spec 文件。但實作下來會發現,如果完全依賴複製貼上把資訊塞進對話框,不僅效率低,也容易遺漏關鍵資訊,最終影響 Spec 的完整性。

MCP:取得更完整的上下文

因此,我們需要借助 MCP 的能力,讓 AI 能夠直接取得散落在 Figma、語雀、Yapi 等工具裡的上下文。把研發流程中常用的資料來源統一封裝到 MCP Server 中,一方面可以減少手動複製帶來的資訊損耗,另一方面也能讓 Spec 的生成更貼近真實業務上下文。

有了這個能力之後我們再進行 Spec 編寫就可以:

shell 代碼解讀複製代碼/opsx:propose 
產品文件: 連結1
技術方案: 連結2
...

AI 輸出完成之後,仍然需要我們介入檢查。因為目前專案可能還沒有足夠多的歷史 Spec,AI 對業務知識的理解並不完整,生成內容裡可能會出現錯誤的業務詞彙、不合理的架構設計等問題。這時直接修改生成的 Spec 文件即可。隨著 Spec 持續豐富,後續生成時這類問題也會越來越少。

文件修改完成之後,就可以進行最關鍵的下一步:生成程式碼。

生成程式碼

這一步我們只需 /opsx:apply 即可讓 AI 根據 Spec 文件快速生成程式碼,但是實作下來會發現使用 /opsx:apply 之後它雖然理解了業務名詞和邏輯,但仍然會擴大改動範圍,或者產出一些不符合團隊規範的命名和實作。這一步我們就需要借助一些額外的手段來約束這些不規範行為:

1. Skills:把軟約束變成可複用的規則層

每個團隊都有自己的開發規範,如果每次把這些團隊通用規則都透過 Prompt 和 Spec 的方式告訴 AI,會造成很大的資源浪費,在這裡我們可以把這些軟約束變成可複用的規則層。

在我的工作流中 Skills 分為兩層:底層 Skill(約束模型) + 上層 Skill(引導模型理解業務)

底層 Skill

你可以把這個 Skill 理解成「給模型的長期工作習慣」,放一些編碼通用規則,比如:

  • 編碼前先思考
  • 優先做小改動,不隨意擴大範圍
  • 修改後要自查並說明影響面
  • 遇到不確定的地方先確認,不要硬猜
  • ...

這類通用規則放在工具全域,所有專案共享使用。

推薦使用:andrej-karpathy-Skills

上層 Skill

但真正和你專案業務強相關的東西,往往是另一層內容,比如:

  • 我們的專案架構哪一層負責請求,哪一層負責資料處理
  • 元件命名習慣
  • 業務裡的專有名詞
  • 哪些歷史坑不能再踩

這部分更適合做成專案級 Skill,持續迭代,放在我們的專案中。

Skills 和 Spec 本質上都是在給 AI 提供上下文,但是放的內容 Spec 重心在做什麼,而 Skills 重心在怎麼做。

2. AGENTS.md:給 AI 的專案入場說明書

除了 Skills,還可以利用 AGENTS.md。很多 AI Coding 工具都會優先讀取專案中的規則說明文件。它適合放那些「進入專案後預設就該知道」的內容,可以把它理解成:專案給 AI 的入場說明書。

例如,較適合放進去的內容包括:

  • 專案結構怎麼讀
  • 哪些規範優先級最高
  • 哪些目錄或檔案不要隨便改
  • ...

如果你不知道這個文件格式怎麼寫,這一步同樣可以使用 AI 去生成,你只需要驗證它生成的文件即可。

為什麼這些內容不全部放進 Skills?

因為兩者的觸發機制和作用範圍並不完全一樣:

  • Skills 是按需啟用,更適合通用規則或者單項客製化規則,像我們上面提到的通用編碼規範等等。
  • AGENTS.md 是進入我們專案生效,存放我們當前專案的客製化規則,像我們這個特殊專案程式碼結構等等。

在很多工具裡,AGENTS.md 的讀取時機會比 Skills 更早,因此它特別適合放最底層、最穩定的專案規則。

3. Hooks:把軟約束升級成硬約束

即便有了 Spec、Skills 和 AGENTS.md,AI 仍然可能在複雜任務裡偶爾「失常」。因為我們上面那些約束都是軟約束,AI 可能並不會遵守這個約定,我們想要強驗證和規範我們的程式碼,就需要使用 Hooks 能力。

很多 AI Coding 工具都提供了 Hook 能力,允許我們在特定生命週期節點執行腳本。以 Codex Hooks 為例,常見的節點包括:

  • UserPromptSubmit:使用者提示詞提交後、AI 接收前
  • PreToolUse:呼叫工具前,可攔截高風險操作
  • PostToolUse:工具執行後、結果回傳給 AI 前
  • Stop:會話停止前,可執行檢查、測試或 CR 流程

例如在實作中會發現 Codex AGENTS.md/Prompt 已經寫了「前端程式碼不能直接寫中文,要走國際化」,但在長會話或複雜任務裡,AI 還是偶爾會輸出中文硬編碼。

這時候就可以在程式碼修改後強制執行一個檢測腳本:

bash 代碼解讀複製代碼#!/bin/bash

FILE="$1"

# 檢測中文
if grep -nE '[\u4e00-\u9fa5]' "$FILE"; then
  echo ""
  echo "檢測到中文硬編碼"
  echo "請改成:"
  echo "t('xxx')"
  exit 2
fi

exit 0

hook 示意設定如下:

json 代碼解讀複製代碼{
  "Hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "Hooks": [
          {
            "type": "command",
            "command": "./scripts/check-i18n.sh $CLAUDE_FILE"
          }
        ]
      }
    ]
  }
}

在擁有了這些設定後,AI 產出的品質會有明顯提升。AI Coding 完成後,我們需要繼續做的工作是:

  • review AI 生成的程式碼
  • 驗證介面與互動是否符合預期
  • 識別這次協作中暴露出來的新問題

這些問題後續可以反過來沉澱到 Skills、AGENTS.md 和 Hooks 中,形成下一輪更穩定的協作基礎。

規格同步

實際開發中,我們經常會遇到一些計劃外的變化:程式碼寫到一半,產品臨時調整了互動;後端 API 欄位發生變化;開發過程中發現原來的技術方案走不通;或者 review 時發現某個邊界場景之前沒有考慮到等。

這些變化本身很正常,但在 AI Coding 流程裡,如果只改程式碼、不更新 Spec,就會埋下一個問題:程式碼已經變了,但 AI 後續讀取到的規則還是舊的。

短期看,這可能沒什麼影響,因為當前會話裡大家都還記得發生了什麼。但一旦換一個會話,或者過幾天繼續迭代,AI 仍然會按照舊 Spec 理解需求。結果就是:它可能把你剛修過的邏輯又改回去,或者在舊規則基礎上繼續生成程式碼,導致程式碼和規格越來越不一致。

這個時候有兩種做法:

  1. 先透過 /opsx:sync + 變動描述,去修改 Spec 文件,然後基於最新的 Spec 文件去 /opsx:apply
  2. 特殊情況我們手動修改了程式碼,那就一定要使用 /opsx:sync + 描述,去更新 Spec

這樣就可以保證我們的程式碼和 Spec 保持一致。

歸檔沉澱

當所有任務完成、驗證通過後,最後一步就是 /opsx:archive

很多人會把 archive 理解成「把這次任務結束掉」,但在 SDD 裡,它更重要的作用是:把一次臨時協作中產生的有效經驗,沉澱成後續可以複用的專案上下文。

一次需求做完以後,真正有價值的東西不只有程式碼本身,還包括:

  • 這次需求最終確認下來的功能邊界
  • 哪些設計決策被採納,哪些方案被放棄
  • 實作過程中發現了哪些歷史問題
  • 哪些規則值得沉澱到主 Spec、Skills 或 AGENTS.md
  • 哪些測試和驗收方式可以複用到後續同類需求

如果不歸檔,這些資訊往往只存在於當前會話、臨時分支或某個人的記憶裡。等下次再做類似需求時,AI 還是要重新理解一遍,團隊也要重新解釋一遍,很多已經踩過的坑還會再踩一次。

比如某次需求中我們發現,「後台匯出類功能必須區分任務建立成功和檔案生成成功」。這個規則如果只留在當前程式碼裡,下一次 AI 做另一個匯出功能時未必會知道。但如果在 archive 時把它沉澱進專案規範,後續類似需求就能直接複用這條經驗。

從長期來看,archive 做得越紮實,專案裡的上下文就越完整。AI 對專案的「熟悉度」不是憑空產生的,而是靠這些持續沉澱下來的規格、規則和歷史決策一點點建立起來的。後續協作的邊際成本,也會隨著這些上下文的累積逐漸降低。

適用場景

不是所有需求都值得走完整的 SDD 流程,有些時候直接 Vibe Coding 可能效率更高。

更推薦使用 SDD 的場景包括:

  • 新業務模組
  • 複雜功能開發
  • 多人協作專案
  • 長期維護專案
  • 大型重構

一個很實用的經驗判斷是:如果一個需求預計會持續超過 2 天,通常就值得建立 Spec。

因為這類需求的複雜度已經足夠高,AI 很難僅靠一輪對話穩定完成;而一旦有了 Spec,後續協作成本會明顯下降。

不太推薦使用 SDD 的場景包括:

  • 過於簡單的改動
  • 純樣式調整
  • 一次性方案驗證
  • 非持續性的臨時腳本

這些場景本身資訊密度不高,強行走一整套 SDD 流程,反而會拖慢效率。

實用技巧

  • 剛開始生成 Spec 時,不要追求一步到位。先有一版能工作的草稿,再在驗證過程中持續補齊。
  • 先寫清楚使用者場景和目標,再討論實作細節,不要一上來就陷入技術方案。
  • 一次只推進一個最小閉環,例如「補一個頁面能力」或「修一條設定鏈路」,避免範圍持續膨脹。
  • 如果需求還不穩定,先把不確定項顯式列出來,再開始實作,避免邊做邊猜。
  • 任務拆分盡量按「可提交、可驗證、可回退」來切,不要把多個業務點揉成一個大改動。
  • 每完成一個子任務就做一次最小驗證,至少確認頁面渲染、請求參數和型別檢查沒有明顯問題。

五、總結

現在模型的能力已經很強了,但它並不會自動替我們完成所有判斷。真正決定產出品質的,還是取決於我們的用法,SDD 只是當下一種比較好的選擇,但未來肯定遠不止於此。


原文出處:https://juejin.cn/post/7656050265522913280


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝14   💬2   ❤️2
592
🥈
我愛JS
📝2   💬3   ❤️2
100
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登