社区
接了个两千四百行的老项目,我把重构这活儿交给了Codex
那个客户甩过来的项目,压缩包解开就一个文件,order_service.py,滚轮往下拉了小半分钟才到底,两千四百行,一个类,下单、改价、退款、对账、发券全揉在里头。
写这堆代码的人,离职快两年了,留了一行注释:TODO,这里以后要拆,日期还是2019。
我接单接惯了这种烂摊子,本来没当回事——直到翻到一个叫_do_calc的函数。参数十一个(对,我数了两遍,没数错),函数体三百多行,里头套了足足五层if。最要命的是第180行那句status==7,我盯着它看了快两小时,死活猜不出7到底代表哪个状态,代码里连个枚举都没有(注释?想多了),全靠猜。
客户那边要加一个下单自动叠满减券的小需求,听着简单,我改了一行,本地跑通,上线。
当晚客户在群里@我,退款金额算错了。
后来查出来是退款和下单共用了同一段折扣逻辑,我动下单那行的时候,顺手把退款也带歪了。
连夜回滚,第二天在群里补了句「已修复」,没人回我,那种安静比被说两句还难受。
打那以后我心里门儿清这文件早晚得拆,可我惹不起,没有一行测试,动哪都像拆炸弹,不知道哪根线连着哪。
最烦的就是这种活儿,你明知道它烂,还得天天在上头糊新代码,一糊就是小一年。
这次是同一个客户又甩了个新需求过来,躲不掉了,我把这活儿交给了Codex。
动手之前我先干了件以前嫌麻烦、这次说什么也要干的事——项目根目录建了个AGENTS.md,Codex会自己去读这个当员工守则,我把技术栈、测试怎么跑,还有几条铁律都写进去:重构严禁改变对外行为、改完必须跑通现有测试、不许删我的注释、不许顺手改无关代码。写的时候心里还骂自己矫情,写完又想起那次退款算错的乌龙,也就不觉得矫情了。
顺带把git status过了一遍,确认这仓库是干净的——真要出岔子,我一条checkout就能全须全尾退回去,这习惯是别的项目上吃过亏才养成的(不细讲,丢人,说来话长)。规矩立好、后悔药备好,这才轮到真派活。
先别急着改,我跟它说得挺直白:先读order_service.py里的_do_calc,别动代码——我得先弄明白它到底在干嘛,那十一个参数分别是干嘛的,哪些压根没用上,status==7这类数字结合上下文猜猜代表什么状态,顺带在整个仓库里grep一下这函数被谁调用。
它读完告诉我,7是已退款待对账,这函数被六个地方调用,其中两个还是测试文件(这两个测试我压根不知道存在,估计是更早一个自由职业者留下的)。
十一个参数里头,有三个它说翻遍全仓库都没人传值,大概率是早年某次改需求留下的僵尸参数(人跑了,参数忘删了),压根没删。
这个「没人传值」我没全信,顺手自己在终端里敲了遍grep核对了一下——它讲的东西我大体信;但凡是「没被调用」这种一口咬死的结论,我习惯自己验一遍,免得哪天它看漏了一个隐藏调用,我却拿着这结论去跟客户打包票。核对下来是真的,那三个参数确实是死的。
这步搁以前靠我自己啃,两千四百行少说啃两天,这回半小时不到就摸清楚了。
这一趟摸明白,我也没让它急着动手,接着让它出方案——照你刚才的理解,拆_do_calc,拆成几个各管一摊的小函数(我随口甩了句每个别超过四十行,就当个大概的尺子),魔法数字换成有名字的常量,对外行为一个字都不能变(那六个调用它的地方,一行都不许动),我把话撂死:先给方案,别碰代码。
它给我列了个分步计划,把折扣计算拆成一个叫_calc_discount的小函数,退款分支单独拎出来叫_calc_refund,再引入一个OrderStatus的枚举,把7、8、9这些数字都换成能读的名字,我看着挺顺,放行。
方案放行,这才轮到真动手,规矩我也跟它讲死:一次只改一处,改完立刻跑pytest tests/test_order.py -v,绿了才准往下走,红了就停——别自己闷头往下莽。
我坐旁边看着它一格一格往前挪,跟看进度条似的,挺有意思,就是看久了眼睛发酸,起身倒了杯水。
它是真听话,改一步跑一遍,跑到第三处的时候测试红了一次,它自己停下来问我,是回退还是修。
我当时正在给另一个客户改报价单,手机弹了这条消息,愣了两秒才反应过来——它没继续莽,自己刹住了。
退款分支里有个边界没对齐,它给我讲了原因,我确认了改法,继续跑。
全改完,我先git diff --stat扫了眼一共动了几个文件几行,心里有个数,再回头逐块细看。心里最没底的,是它会不会手一抖把客户原先留的注释给顺没了;顺带也盯着函数签名有没有被偷偷动过、魔法数字是不是真换成了常量。
怕什么来什么,还真有一行客户留的旧注释被它顺手删了,我到现在都没想起来那行注释原来写的是什么,补不回去了。
diff有一段我看得云里雾里,直接甩过去问它,拿人话把这段逐行讲一遍,重点告诉我哪几行有可能动了对外行为,它倒是讲得挺清楚,我照着又核对了一遍。
diff看完,我用git add -p一块一块挑着加,顺便又复审了一遍,重构一条提交,补测试另开一条,commit message写的是「拆分_do_calc,魔法数字换枚举,行为不变」,没揉在一起。
重构完这一票,我趁热把整个仓库扫了一遍裸奔的文件——哪些.py在tests目录里连个对应的测试都没有,按行数乘被引用次数排了个序,排在最上头最该补的几个,全是核心业务(意料之中)。
补测试这块我跟它说,就按这函数现在实际怎么跑的写,不是你觉得它该怎样写,先把现状焊死,正常流程和边界都要,金额为0、券超额、状态非法这些,测试函数名说人话,比如test_退款金额不能为负这种,别给我整一排test_1、test_2。
配环境那半小时倒是比写代码还磨人,版本装错过一次,报错一堆看着吓人,我折腾了半天才顺,过程无聊到我自己都懒得细讲。
第一批测试跑完,有仨是红的。
我让它先别急着改代码,一个个分清楚是测试自己写错了,还是代码真有毛病。
其中一个是真bug,退款金额在某个边界下会算成负数,前端从来没传过那个值,才一直没爆,这雷埋了得有两年。
我盯着这行报错愣了会儿,心说这要真赶上前端哪天传了那个值,锅算谁的都说不清。
我把这事截图发给客户,客户回了个「好在没出事」,没提要不要加钱修这个,我也没好意思提。
跑了遍pytest --cov=src --cov-report=term-missing,覆盖率从0跑到了71%,还差一块没覆盖,是支付回调那部分(对,就是当年那个离职工程师留的另一坨),这次单子没包这个,我也没主动揽。
客户那边我按人话报了个总结:重构了订单核心模块,补齐了单测,行为零改变,顺带挖出一个潜在bug,覆盖率0到71%。
客户回了句挺好,项目验收,尾款倒是拖了快两周,我也没细催,催急了显得掉价。
那两周我手头还压着另外两单,报价单改来改去页面配色调了三版,跟这个订单系统一点关系没有,凑一块儿倒也没耽误。
折腾完这票,我顺手把同一套问法挪去看另一个新接的仓库,跟它说了句大白话:只读别动,先画张依赖图看看谁依赖谁,挑出被引用最多的三个文件,再给我一条从哪本读起最快搞懂全局的路线——省得我自己啃两天。
新仓库那边我没细抠,粗粗看了眼它给的依赖图,照着读了那三个最核心的文件,剩下的等真用到再说。
多提一句,这活儿也不是全靠它一个,方案那步我先在Claude聊天里问了问思路顺不顺,再拿到Codex里落地,两边配着用,省得它一上来就闷头乱改。
写这些的时候,我桌上那台显示器还留着一道用了两年蹭出来的划痕,跟这事一点关系没有。
那行被顺手删掉的注释,我后来在git log的老版本里翻到过一次,想抄回去,又懒得为一行注释单独开个commit,就这么放着了。
我跟它念叨的也就这几句,抄走改改就能用,别整花里胡哨的。
- 先读别动:「先读order_service.py里的_do_calc,别动代码——我得先弄明白它到底在干嘛,那十一个参数分别是干嘛的,哪些压根没用上,status==7这类数字结合上下文猜猜代表什么状态,顺带在整个仓库里grep一下这函数被谁调用。」
- 先出方案:「照你刚才的理解,拆_do_calc,拆成几个各管一摊的小函数,每个别超过四十行,魔法数字换成有名字的常量,对外行为一个字都不能变,那六个调用它的地方一行都不许动。先给方案,别碰代码。」
- 小步改动:「一次只改一处,改完立刻跑pytest tests/test_order.py -v,绿了才准往下走,红了就停——别自己闷头往下莽。」
- 讲人话审diff:「拿人话把这段逐行讲一遍,重点告诉我哪几行有可能动了对外行为。」
- 补测试规矩:「就按这函数现在实际怎么跑的写,不是你觉得它该怎样写,先把现状焊死,正常流程和边界都要,金额为0、券超额、状态非法这些,测试函数名说人话,比如test_退款金额不能为负这种,别给我整一排test_1、test_2。」
- 摸新仓库:「只读别动,先画张依赖图看看谁依赖谁,挑出被引用最多的三个文件,再给我一条从哪本读起最快搞懂全局的路线。」
别信我们的,你自己试试
花 ¥10,看 Codex / Claude 真帮你做件事——把一件重复的活自动化、做个能用的小网页,或写个小工具,一个真任务绰绰有余。
3 天有效 · 含 2 张配图 · 一把 key,Claude 和 Codex 都能用
新文章第一时间发到你邮箱
新教程、实战笔记,偶尔聊聊产品更新。不发垃圾,随时退订。