協(xié)同開發(fā)的黃金分工法則)
1. 這不是在夸Codex是在教你怎么“使喚”它“Codex 完成率只有六成我卻把臟活全扔給它”——這句話剛看到時(shí)我下意識(shí)點(diǎn)了收藏。不是因?yàn)楸患夹g(shù)震撼而是太真實(shí)了。干了十多年代碼相關(guān)工作的人都知道所謂“AI編程助手”從來不是寫完就跑的全自動(dòng)流水線而更像一個(gè)剛?cè)肼?、學(xué)歷光鮮但實(shí)操經(jīng)驗(yàn)為零的實(shí)習(xí)生能看懂需求文檔能查API文檔能拼出80%語(yǔ)法正確的代碼但剩下那20%往往就是變量命名不一致、邊界條件漏判、異常沒兜底、日志埋點(diǎn)位置錯(cuò)位、甚至把寫成這種低級(jí)但致命的坑。Codex 的官方論文里說它在HumanEval基準(zhǔn)上能達(dá)到70%左右的pass1但那是理想實(shí)驗(yàn)室環(huán)境——單函數(shù)輸入、標(biāo)準(zhǔn)測(cè)試用例、無上下文依賴、無業(yè)務(wù)約束。真實(shí)項(xiàng)目里你讓它補(bǔ)一段訂單超時(shí)自動(dòng)取消的邏輯它可能真給你生成個(gè)帶setTimeout的前端輪詢完全無視后端定時(shí)任務(wù)消息隊(duì)列冪等校驗(yàn)這一整套工業(yè)級(jí)方案。它的“完成率六成”不是能力缺陷而是設(shè)計(jì)哲學(xué)決定的它被訓(xùn)練成“最可能接下去的token序列預(yù)測(cè)器”不是“業(yè)務(wù)邏輯合規(guī)性審查員”。所以標(biāo)題里那個(gè)“扔”字才是關(guān)鍵。這不是在抱怨工具不好用而是在講一種人機(jī)分工新范式把重復(fù)、機(jī)械、模式化、高噪音、低創(chuàng)造性但又必須有人盯的“臟活”全部結(jié)構(gòu)化、切片化、指令化地塞給Codex而人類則退到更高維度做需求翻譯、邊界定義、結(jié)果校驗(yàn)、異常兜底和架構(gòu)對(duì)齊。就像建筑工地上的塔吊司機(jī)——他不砌磚、不綁鋼筋、不畫圖紙但他精準(zhǔn)吊裝每一塊預(yù)制構(gòu)件讓整個(gè)施工節(jié)奏穩(wěn)如鐘表。Codex 就是那個(gè)塔吊而你得先學(xué)會(huì)怎么寫吊裝指令單。適合誰讀三類人最該細(xì)看一是寫了三年以上業(yè)務(wù)代碼、正被CRCode Review疲勞和重復(fù)造輪子壓得喘不過氣的中階開發(fā)者二是帶團(tuán)隊(duì)的技術(shù)負(fù)責(zé)人想快速拉起一支“人AI”協(xié)同開發(fā)流程但苦于找不到可落地的分工切口三是剛轉(zhuǎn)行進(jìn)來的新人別急著背算法題先搞懂怎么讓AI幫你把環(huán)境配置、腳手架初始化、單元測(cè)試樁這些“入門攔路虎”一次性干干凈凈掃掉。這篇文章不講原理推導(dǎo)不堆模型參數(shù)只講我在三個(gè)真實(shí)項(xiàng)目里怎么把Codex當(dāng)“高級(jí)藍(lán)領(lǐng)”用以及踩過哪些坑才摸清它的脾氣。2. 為什么非得是“六成完成率”這恰恰是它最可靠的地方2.1 完成率六成不是短板是安全閥很多人一看到“六成”第一反應(yīng)是“這玩意兒不準(zhǔn)啊”。但如果你真把它當(dāng)“程序員替代品”用那確實(shí)會(huì)天天崩潰??蓳Q個(gè)角度——它完成率要是95%你反而該警惕了。為什么因?yàn)楦咄瓿陕释馕吨P驮趶?qiáng)行“編造確定性”。它遇到模糊需求、缺失上下文、或自己也不確定時(shí)不是返回“無法判斷”而是憑概率選一個(gè)看起來最順的路徑硬往下走。這種“自信的錯(cuò)誤”比“坦誠(chéng)的失敗”危險(xiǎn)十倍。Codex 的六成完成率本質(zhì)是它在說“這部分我有把握給你一個(gè)靠譜初稿那部分信息不足/邏輯存疑/風(fēng)險(xiǎn)未明我主動(dòng)停手等你來拍板?!边@就像老司機(jī)開車不是全程油門到底而是頻繁微調(diào)方向、預(yù)判盲區(qū)、在路口提前減速——它的“不完美”恰恰是系統(tǒng)級(jí)魯棒性的體現(xiàn)。我拿一個(gè)真實(shí)案例說明某次要給支付回調(diào)接口加防重放校驗(yàn)。我給Codex的提示是“請(qǐng)為Spring Boot的RestController添加時(shí)間戳隨機(jī)數(shù)簽名的防重放校驗(yàn)要求攔截器統(tǒng)一處理拒絕非法請(qǐng)求并返回400”。它生成的代碼里時(shí)間戳校驗(yàn)用了System.currentTimeMillis()但沒做服務(wù)端時(shí)鐘漂移容錯(cuò)隨機(jī)數(shù)用了UUID.randomUUID()但沒考慮分布式環(huán)境下唯一性保障簽名驗(yàn)證邏輯里密鑰直接寫死在代碼里。三處全是典型“六成完成”——核心骨架攔截器結(jié)構(gòu)、參數(shù)解析、簽名比對(duì)流程完全正確但所有涉及生產(chǎn)環(huán)境安全邊界的細(xì)節(jié)它都聰明地留白了。提示Codex 不會(huì)主動(dòng)告訴你“這里需要配置中心管理密鑰”但它生成的代碼里密鑰字段一定是個(gè)占位符變量名比如SECRET_KEY_PLACEHOLDER。這個(gè)“留白”就是它給你劃的決策紅線它負(fù)責(zé)把路鋪到懸崖邊剩下的橋怎么搭、護(hù)欄怎么焊、警示牌怎么立必須由你親手完成。2.2 “臟活”的定義決定了你能甩出去多少所謂“臟活”不是指技術(shù)含量低而是指高重復(fù)性、強(qiáng)模式化、低創(chuàng)造性、但出錯(cuò)成本高的任務(wù)。Codex 最擅長(zhǎng)的恰恰是這類任務(wù)。我們拆解一下它能穩(wěn)定承接的“臟活”類型環(huán)境與基建類Dockerfile編寫指定基礎(chǔ)鏡像、安裝依賴、暴露端口、設(shè)置啟動(dòng)命令、CI/CD流水線腳本GitHub Actions YAML、GitLab CI YAML、K8s Deployment/YAML模板生成根據(jù)服務(wù)名、鏡像、資源限制自動(dòng)生成樣板代碼類DTO/VO/Entity三層對(duì)象相互轉(zhuǎn)換的MapStruct配置、MyBatis XML映射文件根據(jù)數(shù)據(jù)庫(kù)表結(jié)構(gòu)生成基礎(chǔ)CRUD、Swagger注解批量添加根據(jù)Controller方法簽名生成對(duì)應(yīng)Api、ApiOperation測(cè)試支撐類JUnit5測(cè)試樁Mockito模擬依賴、生成基礎(chǔ)斷言、Postman Collection JSON根據(jù)OpenAPI規(guī)范生成請(qǐng)求示例、覆蓋率報(bào)告配置JaCoCo插件集成文檔與注釋類從方法簽名自動(dòng)生成JavaDoc含參數(shù)、返回值、異常說明、SQL注釋解釋JOIN邏輯、WHERE條件意圖、README.md功能模塊描述基于package結(jié)構(gòu)歸納。這些活的共同點(diǎn)是有清晰的輸入輸出格式、有大量公開范例可學(xué)習(xí)、規(guī)則明確但人工編寫極其枯燥。Codex 在這類任務(wù)上完成率遠(yuǎn)不止六成——實(shí)測(cè)在結(jié)構(gòu)化提示下穩(wěn)定達(dá)到85%以上。它的“六成”主要體現(xiàn)在業(yè)務(wù)邏輯實(shí)現(xiàn)環(huán)節(jié)而這恰恰是我們應(yīng)該守住的主戰(zhàn)場(chǎng)。2.3 人機(jī)分工的黃金比例6:3:1法則經(jīng)過二十多個(gè)迭代周期的磨合我總結(jié)出一個(gè)實(shí)操中非常穩(wěn)定的分工比例60%的體力活交給Codex生成初稿30%由我做結(jié)構(gòu)化校驗(yàn)與安全加固10%用于重構(gòu)與抽象升級(jí)。60%生成不是讓它寫完整功能而是按“最小可交付單元”切分。比如做一個(gè)用戶導(dǎo)出Excel功能我不讓它寫整個(gè)Controller而是分三步指令① 生成Apache POI寫入Excel的工具類含樣式、多Sheet支持② 生成Service層數(shù)據(jù)查詢與分頁(yè)邏輯含DTO轉(zhuǎn)換③ 生成Controller接收參數(shù)與返回ResponseEntity的骨架。每步獨(dú)立提示獨(dú)立校驗(yàn)。30%校驗(yàn)這是最關(guān)鍵的環(huán)節(jié)。我有一份《Codex產(chǎn)出物五維校驗(yàn)清單》每次必過①安全性密鑰、密碼、敏感路徑是否硬編碼②健壯性空值、異常、邊界值是否覆蓋③一致性命名風(fēng)格、日志格式、錯(cuò)誤碼是否與項(xiàng)目現(xiàn)有規(guī)范對(duì)齊④可觀測(cè)性關(guān)鍵路徑是否有日志埋點(diǎn)耗時(shí)是否打點(diǎn)⑤可維護(hù)性魔法數(shù)字是否提取為常量重復(fù)邏輯是否可抽方法。10%重構(gòu)Codex生成的代碼往往缺乏“設(shè)計(jì)感”。比如它寫的工具類方法全是static沒有封裝狀態(tài)它寫的Service事務(wù)邊界可能包得太寬或太窄。這10%就是我把散落的珠子串成項(xiàng)鏈的過程——引入策略模式替換if-else、用Builder模式簡(jiǎn)化復(fù)雜對(duì)象構(gòu)造、將通用校驗(yàn)邏輯下沉為AOP切面。這個(gè)比例不是理論推導(dǎo)而是血淚教訓(xùn)換來的。早期我試圖讓Codex完成80%結(jié)果花兩小時(shí)debug它生成的Redis分布式鎖實(shí)現(xiàn)它用setnx但沒配expire導(dǎo)致死鎖后來壓到40%又發(fā)現(xiàn)人力投入過大ROI太低。6:3:1是效率與質(zhì)量的最優(yōu)平衡點(diǎn)。3. 實(shí)操四步法從“扔給它”到“它真聽話”3.1 第一步把需求“翻譯”成Codex能懂的“工單語(yǔ)言”Codex不是人它不理解“我要做個(gè)好用的導(dǎo)出功能”。它只認(rèn)結(jié)構(gòu)化、帶約束、有上下文的指令。我把提示詞Prompt設(shè)計(jì)成標(biāo)準(zhǔn)化工單模板包含四個(gè)強(qiáng)制字段角色定義明確告訴它此刻的身份。例如“你是一個(gè)有5年Spring Boot開發(fā)經(jīng)驗(yàn)的后端工程師熟悉Alibaba Druid連接池和Logback日志框架。”為什么重要沒有角色定義它默認(rèn)用通用編程知識(shí)作答容易生成Hibernate而非MyBatis的DAO層或用Log4j2而非Logback的配置。輸入約束限定它能“看到”的信息范圍。例如“當(dāng)前項(xiàng)目使用MySQL 8.0JDK 17Spring Boot 3.1已存在User實(shí)體類含id, name, email, createTime字段數(shù)據(jù)庫(kù)表名為t_user?!睘槭裁粗匾狢odex沒有記憶你不說它就假設(shè)最通用場(chǎng)景。指定JDK版本它才不會(huì)生成RecordsJDK14或Text BlocksJDK15等低版本不兼容語(yǔ)法。輸出規(guī)范精確描述你要的代碼形態(tài)。例如“生成一個(gè)Service類類名UserExportService包含一個(gè)public方法exportUsersToExcel(List users)返回byte[]要求使用Apache POI 5.2.4Excel第一行為表頭ID,姓名,郵箱,創(chuàng)建時(shí)間日期格式為yyyy-MM-dd HH:mm:ss中文列寬自動(dòng)適配?!睘槭裁粗匾白詣?dòng)適配”這種模糊詞必須拆解。我實(shí)際會(huì)寫“調(diào)用sheet.autoSizeColumn(i) for i in 0..3并設(shè)置中文列寬為25個(gè)字符寬度即25 * 256”。禁止事項(xiàng)用否定句式堵死常見雷區(qū)。例如“禁止使用Lombok Data注解項(xiàng)目禁用Lombok禁止硬編碼數(shù)據(jù)庫(kù)連接URL禁止在方法內(nèi)打印System.out必須用log.info?!睘槭裁粗匾狢odex對(duì)“禁止”指令響應(yīng)極強(qiáng)。相比說“請(qǐng)用log.info”不如直接說“禁止System.out”它會(huì)主動(dòng)規(guī)避所有print語(yǔ)句。我試過同一需求用自然語(yǔ)言描述 vs 用工單模板生成質(zhì)量差異巨大。前者它可能生成一個(gè)帶Transactional但沒指定rollbackFor的Service后者它生成的代碼里Transactional(rollbackFor Exception.class)會(huì)原樣出現(xiàn)——因?yàn)槟阍凇敖故马?xiàng)”里寫了“禁止未指定rollbackFor的Transactional”。3.2 第二步用“三明治校驗(yàn)法”快速過濾初稿Codex一次生成的代碼我從不直接復(fù)制粘貼。而是用“三明治”結(jié)構(gòu)快速掃描先看頭入口、再看尾出口、最后夾心核心邏輯。頭入口檢查方法簽名是否符合預(yù)期。參數(shù)類型是否匹配是否加了必要的NotNull、Size等校驗(yàn)注解Controller層是否用了Valid如果入口就錯(cuò)了后面全廢立刻重發(fā)指令。尾出口重點(diǎn)看返回值和異常處理。return語(yǔ)句是否在所有分支都存在有沒有遺漏else或catch后的返回是否對(duì)null返回做了防御性處理我見過Codex生成的工具類在InputStream為null時(shí)直接調(diào)用.read()導(dǎo)致NPE。夾心核心邏輯這是最需經(jīng)驗(yàn)的部分。我重點(diǎn)關(guān)注三個(gè)“魔鬼細(xì)節(jié)”資源釋放所有IO流、數(shù)據(jù)庫(kù)連接、HTTP客戶端是否在finally或try-with-resources中關(guān)閉Codex有時(shí)會(huì)漏掉close()尤其在嵌套try塊里。并發(fā)安全如果代碼涉及靜態(tài)變量、單例Bean、緩存操作是否加了synchronized、ReentrantLock或用了ConcurrentHashMap它很少主動(dòng)加鎖但你的業(yè)務(wù)場(chǎng)景可能需要。SQL注入防護(hù)所有動(dòng)態(tài)拼接SQL的地方是否用了?占位符是否調(diào)用了PreparedStatement它偶爾會(huì)生成SELECT * FROM t_user WHERE name name 這種高危代碼。注意校驗(yàn)不是逐行讀代碼而是帶著“攻擊者思維”找破綻。比如看到String sql UPDATE t_user SET status status WHERE id id;不用看后面立刻標(biāo)紅——這就是典型的“夾心毒丸”必須重寫。3.3 第三步建立你的“Codex錯(cuò)誤模式庫(kù)”Codex不是隨機(jī)犯錯(cuò)它有穩(wěn)定的“錯(cuò)誤人格”。我花了兩個(gè)月把所有它犯過的錯(cuò)歸類建了一個(gè)內(nèi)部Wiki叫《Codex高頻失控行為圖譜》。遇到新問題先查圖譜80%能秒解。分享幾個(gè)最典型的錯(cuò)誤類型典型表現(xiàn)應(yīng)對(duì)策略根本原因魔法數(shù)字幽靈生成代碼中出現(xiàn)if (status 3)、for (int i 0; i 100; i)但項(xiàng)目中3應(yīng)為UserStatus.DELETED.getCode()100應(yīng)為Constants.PAGE_SIZE在Prompt中強(qiáng)制要求“所有數(shù)字必須定義為private static final常量常量名需見名知義如MAX_RETRY_TIMES”Codex訓(xùn)練數(shù)據(jù)中大量開源代碼直接寫數(shù)字它學(xué)到了“快捷寫法”日志埋點(diǎn)失焦在關(guān)鍵業(yè)務(wù)路徑如扣款成功沒打日志卻在無關(guān)的工具方法如字符串拼接里打了log.debug(拼接完成)在Prompt中明確“僅在以下節(jié)點(diǎn)打INFO日志方法入口含參數(shù)、核心業(yè)務(wù)成功/失敗分支、方法出口含返回值摘要”它把“日志”理解為“代碼行”而非“業(yè)務(wù)信號(hào)”傾向于在每段邏輯后加一行異常處理擺爛try { ... } catch (Exception e) { e.printStackTrace(); }或catch (Exception e) { throw new RuntimeException(e); }在Prompt中禁令“禁止e.printStackTrace()禁止裸throw new RuntimeException(e)必須捕獲具體異常類型并按業(yè)務(wù)含義轉(zhuǎn)換為自定義業(yè)務(wù)異常如UserNotFoundException”Codex見過太多“懶人寫法”且認(rèn)為printStackTrace()是調(diào)試標(biāo)配這個(gè)圖譜最大的價(jià)值是讓我把“救火”變成“防火”。現(xiàn)在寫Prompt時(shí)我會(huì)主動(dòng)把圖譜里的禁令前置。比如要生成文件上傳代碼我第一句就寫“禁止使用MultipartFile.transferTo()存在臨時(shí)文件泄露風(fēng)險(xiǎn)必須使用try-with-resources讀取InputStream并寫入目標(biāo)路徑”。3.4 第四步用“漸進(jìn)式交付”馴服它的不確定性Codex最讓人抓狂的是它“每次生成都不一樣”。同一指令第一次生成A版第二次生成B版第三次可能連編譯都過不了。這不是bug是概率采樣的必然結(jié)果。我的解法是永遠(yuǎn)不追求“一次生成永久可用”而是設(shè)計(jì)“三次迭代逐步逼近”。以生成一個(gè)JWT Token解析工具為例第一輪V1指令聚焦“能跑通”。只提最基本需求“生成一個(gè)工具類JwtUtil包含static方法parseToken(String token)返回MapString, Object使用jjwt-api 0.11.5忽略簽名驗(yàn)證僅解析payload?!?目標(biāo)先拿到一個(gè)語(yǔ)法正確、能編譯的版本。不管它用Jwts.parser().parseClaimsJws(token).getBody()還是Jwts.parser().parseClaimsJwt(token).getBody()只要不報(bào)錯(cuò)就行。第二輪V2基于V1代碼做“精準(zhǔn)修補(bǔ)”。我復(fù)制V1的類名、方法簽名、核心解析邏輯然后追加指令“在parseToken方法中增加對(duì)token格式的校驗(yàn)必須包含.分隔的三段且第二段base64Url解碼后是合法JSON若校驗(yàn)失敗拋出IllegalArgumentException消息為Invalid JWT format?!?這次它只改校驗(yàn)部分主體邏輯不變穩(wěn)定性大幅提升。第三輪V3做“生產(chǎn)加固”。指令變?yōu)椤霸赩2基礎(chǔ)上將密鑰管理改為從Spring Environment獲取key: jwt.secret若未配置則拋出IllegalStateException所有日志使用SLF4J的logger級(jí)別為DEBUG增加單元測(cè)試方法testParseValidToken()使用Mockito驗(yàn)證解析結(jié)果。”三次迭代每次只動(dòng)一個(gè)關(guān)注點(diǎn)錯(cuò)誤被牢牢鎖死在小范圍內(nèi)。V1解決“有無”V2解決“正確”V3解決“健壯”。這比盯著一個(gè)“完美初稿”死磕三小時(shí)高效得多。而且V1的代碼哪怕最終沒用上也成了我理解JWT解析流程的絕佳教學(xué)材料——Codex的“不完美”反而成了最好的學(xué)習(xí)腳手架。4. 常見問題與排查技巧實(shí)錄那些沒寫在文檔里的坑4.1 問題Codex生成的代碼總在“差不多”的地方卡殼比如循環(huán)里少一個(gè)分號(hào)、if后面忘加大括號(hào)現(xiàn)象還原我讓它生成一個(gè)遍歷List并過濾空字符串的工具方法。它輸出public static ListString filterEmpty(ListString list) { if (list null) return Collections.emptyList(); ListString result new ArrayList(); for (String s : list) if (s ! null !s.trim().isEmpty()) result.add(s); return result; }這段代碼語(yǔ)法合法但邏輯有嚴(yán)重隱患for循環(huán)體只有一行if判斷也只有一行看似沒問題。但一旦后續(xù)有人在if里加第二行邏輯就會(huì)因缺少大括號(hào)導(dǎo)致邏輯錯(cuò)亂。這是典型的“技術(shù)正確工程危險(xiǎn)”。排查思路這不是Codex的錯(cuò)而是它遵循了Java社區(qū)部分“簡(jiǎn)潔主義”風(fēng)格尤其來自Python背景的開發(fā)者貢獻(xiàn)的代碼。它認(rèn)為if (condition) doSomething();是可接受的。解決方案在Prompt中加入風(fēng)格契約。我現(xiàn)在的標(biāo)準(zhǔn)指令是“所有if/for/while語(yǔ)句無論單行或多行必須使用大括號(hào){}包裹代碼塊。這是本項(xiàng)目的強(qiáng)制代碼規(guī)范違反者視為嚴(yán)重錯(cuò)誤?!?加上這條它生成的代碼立刻變成for (String s : list) { if (s ! null !s.trim().isEmpty()) { result.add(s); } }實(shí)操心得Codex對(duì)“強(qiáng)制規(guī)范”類指令響應(yīng)極佳但對(duì)“建議”“最好”“推薦”這類軟性詞匯完全免疫。所以把團(tuán)隊(duì)代碼規(guī)范直接寫成Prompt里的“禁止”和“必須”效果立竿見影。4.2 問題Codex對(duì)“性能”毫無概念生成的代碼在大數(shù)據(jù)量下慢得像蝸?,F(xiàn)象還原要生成一個(gè)從List 中查找最新注冊(cè)用戶的工具方法。它給出public static User findLatestUser(ListUser users) { if (users null || users.isEmpty()) return null; User latest users.get(0); for (User u : users) { if (u.getCreateTime().after(latest.getCreateTime())) { latest u; } } return latest; }邏輯沒錯(cuò)但getCreateTime()每次調(diào)用都是getter如果User對(duì)象是Hibernate代理可能觸發(fā)N1查詢。更糟的是它沒考慮Collections.max()或Stream API的max(Comparator)這種更優(yōu)解。排查思路Codex的訓(xùn)練數(shù)據(jù)里90%的代碼樣本是教學(xué)示例或小規(guī)模Demo性能不是首要考量。它優(yōu)先選擇“人最容易看懂”的寫法而非“機(jī)器執(zhí)行最快”的寫法。解決方案在Prompt中植入性能上下文。我會(huì)寫“此方法可能被調(diào)用10萬次/天List大小平均為5000條。請(qǐng)優(yōu)先使用O(n)時(shí)間復(fù)雜度方案避免在循環(huán)內(nèi)調(diào)用可能觸發(fā)數(shù)據(jù)庫(kù)查詢的方法若使用Stream API請(qǐng)確保其并行流不會(huì)帶來額外開銷本場(chǎng)景無需并行?!奔由闲阅芗s束后它生成的代碼會(huì)變成public static User findLatestUser(ListUser users) { if (users null || users.isEmpty()) return null; // 預(yù)先提取createTime避免多次getter調(diào)用 LocalDateTime maxTime null; User result null; for (User u : users) { LocalDateTime createTime u.getCreateTime(); if (maxTime null || createTime.isAfter(maxTime)) { maxTime createTime; result u; } } return result; }它學(xué)會(huì)了“緩存中間結(jié)果”這個(gè)經(jīng)典優(yōu)化技巧。這說明Codex不是不能懂性能而是你需要把性能要求翻譯成它能理解的“計(jì)算步驟約束”。4.3 問題Codex生成的單元測(cè)試總是“假陽(yáng)性”——測(cè)試用例通過了但實(shí)際業(yè)務(wù)邏輯是錯(cuò)的現(xiàn)象還原我讓它為一個(gè)金額計(jì)算方法生成JUnit5測(cè)試。它生成Test void calculateTotalAmount_shouldReturnSumOfItems() { ListItem items Arrays.asList( new Item(A, new BigDecimal(10.00)), new Item(B, new BigDecimal(20.00)) ); BigDecimal result OrderCalculator.calculateTotal(items); assertEquals(new BigDecimal(30.00), result); }測(cè)試本身沒問題但OrderCalculator.calculateTotal()方法里它把BigDecimal加法寫成了導(dǎo)致精度丟失而測(cè)試用例恰好用整數(shù)沒暴露出問題。這就是“用例覆蓋不全”導(dǎo)致的假陽(yáng)。排查思路Codex生成測(cè)試主要靠“模式匹配”——它見過太多assertEquals(expected, actual)就照貓畫虎。但它不懂業(yè)務(wù)邊界不會(huì)主動(dòng)構(gòu)造0.1 0.2這種精度陷阱用例也不會(huì)覆蓋null、空集合、負(fù)數(shù)等邊界。解決方案采用“測(cè)試驅(qū)動(dòng)反向生成法”。我不讓它生成測(cè)試而是先寫一個(gè)失敗的測(cè)試再讓它“修復(fù)實(shí)現(xiàn)”。例如我手動(dòng)寫Test void calculateTotalAmount_shouldHandlePrecisionLoss() { ListItem items Arrays.asList( new Item(A, new BigDecimal(0.1)), new Item(B, new BigDecimal(0.2)) ); BigDecimal result OrderCalculator.calculateTotal(items); // 此時(shí)OrderCalculator是空實(shí)現(xiàn)測(cè)試必?cái)?assertEquals(new BigDecimal(0.3), result); // 期望0.3 }然后指令Codex“修復(fù)OrderCalculator.calculateTotal方法使其通過上述測(cè)試。要求使用BigDecimal.add()禁止使用運(yùn)算符?!彼⒖躺蓀ublic static BigDecimal calculateTotal(ListItem items) { if (items null || items.isEmpty()) { return BigDecimal.ZERO; } BigDecimal total BigDecimal.ZERO; for (Item item : items) { total total.add(item.getAmount()); } return total; }這個(gè)方法天然通過所有精度測(cè)試。因?yàn)樗潜弧笆y(cè)試”逼出來的而不是憑空想象的。4.4 問題Codex對(duì)“項(xiàng)目上下文”極度健忘前后兩次生成的代碼風(fēng)格、命名、工具鏈完全不一致現(xiàn)象還原第一次讓它生成DTO它用UserDto第二次生成VO它用UserViewObject第三次生成Entity它又用UserPO。同一個(gè)項(xiàng)目三種后綴混用Code Review時(shí)直接勸退。排查思路Codex沒有長(zhǎng)期記憶每次請(qǐng)求都是全新會(huì)話。它不知道你上周用Dto也不知道你團(tuán)隊(duì)約定VO只用于前端展示層。解決方案建立項(xiàng)目上下文快照Context Snapshot。我維護(hù)一個(gè)Markdown文件叫PROJECT_CONTEXT.md內(nèi)容如下# 項(xiàng)目上下文快照2024-Q3 - 語(yǔ)言Java 17 - 框架Spring Boot 3.1, MyBatis-Plus 3.5.3 - 代碼規(guī)范 * DTO后綴xxxDto如UserDto * VO后綴xxxVo如UserVo * Entity后綴xxxEntity如UserEntity * 工具類命名xxxUtil如DateUtil - 常用工具 * JSONJacksonJsonInclude(JsonInclude.Include.NON_NULL) * 日志SLF4J Logbacklogger名類全名 * HTTPRestTemplate已配置Error Handler每次寫Prompt前我先把這段上下文復(fù)制進(jìn)去作為指令的“前導(dǎo)語(yǔ)”。Codex會(huì)把這段當(dāng)作最高優(yōu)先級(jí)的輸入生成結(jié)果風(fēng)格高度統(tǒng)一。這個(gè)快照比任何口頭約定都管用。5. 我的個(gè)人體會(huì)Codex不是終點(diǎn)是重新定義“開發(fā)者”的起點(diǎn)寫完這篇我打開終端運(yùn)行了今天第7次Codex調(diào)用讓它根據(jù)我剛寫的這篇博文大綱生成一份面向新人的《Codex協(xié)作開發(fā)速查手冊(cè)》Markdown。它30秒內(nèi)交卷結(jié)構(gòu)清晰要點(diǎn)齊全連emoji都用得恰到好處雖然我刪掉了所有emoji畢竟生產(chǎn)環(huán)境不需要表情包。我花了12分鐘把它的初稿里“禁止事項(xiàng)”部分重寫了一遍把“不要用Lombok”改成“項(xiàng)目已禁用Lombok所有Getter/Setter必須手寫”又補(bǔ)充了兩個(gè)我們團(tuán)隊(duì)特有的校驗(yàn)陷阱。整個(gè)過程我沒有寫一行業(yè)務(wù)邏輯代碼但完成了知識(shí)沉淀。這讓我想起十年前我第一次用Maven替代Ant第一次用Git替代SVN第一次用Docker替代虛擬機(jī)——每一次工具革命都不是讓我們寫更多代碼而是讓我們把精力從“如何實(shí)現(xiàn)”轉(zhuǎn)向“為何實(shí)現(xiàn)”和“如何更好”。Codex的六成完成率不是它的局限而是給我們劃出的一條分界線線這邊是機(jī)器最擅長(zhǎng)的、可被模式化的體力勞動(dòng)線那邊是人類獨(dú)有的、關(guān)于權(quán)衡、關(guān)于共情、關(guān)于長(zhǎng)期價(jià)值的思考。它把“臟活”全接過去恰恰是為了逼我們直面那個(gè)更難的問題當(dāng)代碼不再是瓶頸什么才是真正的技術(shù)壁壘我現(xiàn)在每天開工的第一件事不是敲代碼而是打開PROJECT_CONTEXT.md更新一行“今日重點(diǎn)校驗(yàn)Codex生成的Redis分布式鎖實(shí)現(xiàn)確認(rèn)是否已添加看門狗續(xù)期邏輯。” 這行字比任何一行Java代碼都更接近我作為開發(fā)者的核心價(jià)值。最后分享一個(gè)小技巧當(dāng)你對(duì)Codex生成的某段代碼猶豫不決時(shí)別急著修改先問自己一個(gè)問題“如果這段代碼明天上線凌晨三點(diǎn)報(bào)警我愿不愿意爬起來修它” 如果答案是否定的那就別讓它進(jìn)倉(cāng)庫(kù)——哪怕它語(yǔ)法完美測(cè)試全綠。因?yàn)檎嬲耐瓿陕蕪膩聿皇荂odex的六成而是你按下Merge按鈕那一刻心里的百分之百。