news 2026/10/29 20:49:53

ChatGPT、Codex工程方法:Agent发现5处重复代码,为什么不能马上抽成一个公共模块?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ChatGPT、Codex工程方法:Agent发现5处重复代码,为什么不能马上抽成一个公共模块?

最近让 ChatGPT、Codex 做旧项目重构时,很容易碰到一种特别“顺眼”的建议:

这里有5处重复代码,可以抽成一个公共模块。

乍一看完全没问题。

同样的校验逻辑。

同样的参数转换。

同样的异常处理。

甚至连代码结构都几乎一样。

于是Agent很快帮你抽出一个:

CommonValidator

原来的5份代码变成1份。

Diff一下少了几百行。

测试也全部通过。

看上去这就是一次非常漂亮的重构。

但真正麻烦的事情,往往不是发生在当天。

而是两个月以后。

订单模块说:

这条校验规则要放宽。

支付模块说:

这里必须继续严格。

会员模块又要求:

VIP用户走另一套逻辑。

这时候你才发现:

原来那5段“重复代码”,只是今天长得一样。

它们未来变化的原因完全不同。

而你之前为了消除重复,把5个业务强行绑进了同一个公共模块。

所以这类问题真正要判断的,从来不是:

代码是不是重复。

而是:

这些代码未来是不是会因为同一个原因一起变化。


一、先给核心判断:重复代码,不一定代表同一个抽象

很多人做重构时会天然接受一个原则:

Don't Repeat Yourself。

重复代码不好。

能抽公共逻辑就抽。

但Agent特别擅长发现:

语法相似。

结构相似。

变量名相似。

因为这些特征都非常明显。

问题是:

Syntax Similarity ≠ Semantic Similarity。

两段代码今天可能完全一样。

但它们背后服务的是两个不同业务。

未来变化方向也可能完全不同。

比如订单和退款都需要判断:

amount > 0

今天逻辑完全一样。

但半年后:

订单要求:

金额必须大于0。

退款要求:

允许0金额补偿。

营销活动要求:

负数代表冲正。

这时候“同样的判断”已经不再属于同一个业务概念。

所以抽象真正应该看的是:

语义是不是相同。

而不是:

代码长得像不像。


二、一个特别典型的现场:5个Service都有同样的校验

假设Codex扫描仓库以后发现:

OrderService

PaymentService

RefundService

MemberService

CouponService

里都有一段:

检查用户状态。

检查参数。

检查金额。

异常时抛BusinessException。

于是它建议:

统一抽成:

CommonValidator.validate()

代码看起来马上干净很多。

可问题来了。

这5个模块对“合法”的定义真的一样吗?

订单可能允许游客。

支付必须实名认证。

退款可能允许冻结账号处理历史订单。

会员模块又有自己的等级逻辑。

今天它们代码一样,

可能只是因为:

当前业务规则碰巧重合。

不是因为:

它们本来就是同一个Domain Rule。

这就是最容易误判的地方。


三、Agent为什么特别容易“过早抽象”?

因为从代码表面看,重复是非常明显的Signal。

比如:

5个文件里都有15行相似逻辑。

这对Agent来说很容易识别。

而“未来是不是会分化”,却没有那么容易从当前代码直接看到。

这需要结合:

业务边界。

模块职责。

历史变更。

产品规则。

团队所有权。

未来演进方向。

所以如果你只给Codex一个任务:

帮我重构重复代码。

它很容易优化:

当前代码形态。

却没有足够Evidence判断:

未来变化方向。

这也是为什么Agent参与重构以后,人更应该补上:

业务演进判断。


四、真正应该问的是:它们为什么会一起变化?

这是判断能不能抽公共模块最重要的问题。

假设5段代码都在做:

手机号格式校验。

如果未来规则变化时:

所有模块都必须同步变化。

比如统一从11位手机号,升级到支持国际号码。

那它们大概率确实属于:

同一个稳定抽象。

这时候抽成:

PhoneNumberValidator

就很合理。

但如果5段代码虽然都在判断:

用户状态。

可订单、支付、退款对用户状态的要求各不相同,

那么它们今天相似,

不代表应该共享。

一个非常实用的问题是:

未来如果这里发生变化,谁会要求它变化?

如果答案是:

同一个业务规则。

可以考虑抽象。

如果答案是:

5个不同业务团队。

那就要谨慎。


五、错误抽象最麻烦的地方:它制造Change Coupling

本来5个模块互相独立。

订单改订单。

退款改退款。

支付改支付。

抽成公共模块以后,情况变了。

支付要改一条规则。

你修改:

CommonValidator

结果马上要问:

订单会不会受影响?

退款呢?

会员呢?

优惠券呢?

于是一次原本只属于支付的需求,

变成:

5个模块一起回归。

这就是:

Change Coupling——变更耦合。

代码确实少了。

但变更影响面变大了。

真正危险的是:

公共模块越来越多人依赖以后,

任何人都不敢轻易改。

最后它会慢慢变成:

一个看起来“公共”,实际上谁都害怕碰的模块。


六、所以“减少代码行数”不是抽象成功的指标

Agent重构以后经常会给出非常漂亮的结果:

删除420行重复代码。

新增80行公共实现。

测试全部通过。

从Diff来看:

非常成功。

但工程上真正应该继续看:

新增一个业务规则时,要改几个地方?

公共模块改变时,需要回归多少下游?

一个团队的需求,会不会强迫其他模块同步升级?

如果这些成本上升了,

那么:

代码重复减少,并不代表系统复杂度下降。

你只是把:

重复成本

换成了:

耦合成本。

而后者往往更难看见。


七、什么时候应该暂时保留重复代码?

这句话很多人不太喜欢:

有时候重复代码是可以接受的。

但实际工程里确实如此。

尤其当:

业务刚开始发展。

规则还不稳定。

多个模块只是暂时长得一样。

未来方向还看不清。

这时候保留两三份简单实现,有时比过早抽公共模块更安全。

因为重复的成本是:

修改时多改几处。

而错误抽象的成本可能是:

以后每次改动都要担心所有下游。

如果现在还不知道两段代码是否属于同一个长期概念,

我更倾向于:

Wait for the Pattern to Stabilize。

先观察变化。

不要看到两次重复就马上抽。


八、我更关注“第二次变化”而不是“第一次重复”

假设两个模块第一次都需要:

检查用户等级。

这时候代码一样。

还不能证明应该抽。

后来规则变化了一次。

如果两个模块仍然同时变化成同样逻辑,

说明它们背后的概念可能确实一致。

再过一段时间第二次变化。

仍然一起变化。

这时候再抽象,可信度就高很多。

也就是说:

Stable Abstraction往往来自多次共同变化。

不是来自第一次代码重复。

这点特别适合Agent重构。

因为Agent很容易在第一次看到重复时就出手。

而人可以提醒它:

先查这几段代码过去半年是不是经常一起变化。

历史变更其实是一种很重要的Evidence。


九、Git历史比当前代码更能判断“是不是同一个概念”

如果我怀疑5段重复代码是不是值得抽,

我会先看:

过去几次修改。

比如:

订单模块修改了4次。

退款模块修改了3次。

但从来没有一起变化。

那说明:

虽然今天代码相似,

它们的演化路径其实是独立的。

反过来,如果过去一年:

这5处每次规则变化都一起改,

而且原因也相同,

那公共抽象就更有价值。

所以让ChatGPT、Codex重构之前,可以先让它做一件事:

分析Co-change History。

不是只扫描当前代码。


十、公共模块还有一个问题:到底谁负责?

假设最终真的抽出了:

CommonValidator

接下来马上会出现一个很现实的问题:

谁拥有它?

订单团队?

支付团队?

基础架构团队?

还是谁都能改?

如果Owner不清楚,

公共模块很容易慢慢变成:

谁有需求谁就往里面加参数。

最后:

validate(user, scene, source, mode, strict, ignoreX...)

越来越复杂。

因为每个业务都想让公共模块照顾自己的例外。

于是原来为了“消除重复”建立的抽象,

最后变成:

所有业务差异的集中地。

这种公共模块,比5份重复代码更难维护。


十一、一个好抽象应该让接口越来越稳定

真正成熟的公共模块通常有一个特点:

使用方很多。

但它本身变化频率越来越低。

比如:

日期解析。

统一ID格式。

手机号格式。

标准加密。

协议序列化。

这些概念边界相对稳定。

相反,如果一个公共模块:

每来一个业务需求就要加一个if。

每接一个新模块就多一个参数。

每次修改都要通知很多下游。

那很可能说明:

抽象层级选错了。

它不是一个真正稳定的公共概念。

只是把多份业务代码强行搬到一个文件里。


十二、我更建议让Agent先做“重复分类”,而不是直接抽象

以后看到Codex提示:

发现多处重复代码,建议提取公共方法。

不要马上Accept。

可以先让它回答三个问题:

这些代码只是语法相似,还是业务语义相同?

它们过去是否经常一起变化?

未来一个模块单独变化时,会不会被公共抽象限制?

然后把重复分成:

Mechanical Duplication

例如格式转换、通用解析。

通常更适合抽。

Domain Duplication

不同业务暂时有同样规则。

谨慎。

Accidental Similarity

只是碰巧长得一样。

通常不应该抽。

这一步比直接“Extract Method”有价值得多。


十三、如果已经抽错了,怎么判断?

一个很明显的信号是:

公共模块开始出现越来越多:

if (scene == ...)

if (type == ...)

if (source == ...)

甚至每个业务都要传:

一个特殊Flag。

这说明:

原来被认为相同的逻辑,正在不断分化。

另一个信号是:

每次只改一个业务需求,

却必须让5个下游一起跑完整回归。

这时候就要重新问:

这个公共模块是不是已经制造了更多耦合?

有时候正确动作不是继续优化它。

而是:

重新拆开。


十四、一个简单指标:Change Coupling Rate

这篇我建议只留一个指标:

Change Coupling Rate——变更耦合率

可以简单理解为:

某个公共模块发生一次业务修改后,需要一起验证或修改的下游模块数量 ÷ 总下游数量

假设一个公共Validator有5个业务依赖。

支付规则变化一次,

结果5个模块都必须重新验证。

说明耦合很高。

如果公共模块长期稳定,

只有真正的公共规则变化时才影响全部下游,

那这个抽象就比较健康。

所以真正值得看的不是:

少了多少重复代码。

而是:

一个业务变化会被放大到多少地方。


十五、什么时候我会明确支持抽公共模块?

如果满足下面几个条件,我通常会更放心:

业务语义明确相同。

过去经常一起变化。

未来变化原因大概率一致。

接口边界清晰。

Owner明确。

可以独立测试。

下游不需要不断传特殊参数。

这时候抽象真正减少的是:

重复决策。

而不仅仅是:

重复代码。

这才是公共模块最有价值的地方。


十六、Plus和Pro怎么判断?

如果你平时主要让ChatGPT、Codex:

处理单模块重构。

分析几处重复代码。

查看少量Git历史。

判断一次公共方法是否值得提取。

这类任务比较集中,Plus通常够用。

关键是不要只让Agent看当前Diff。

最好把:

模块职责。

历史修改。

未来需求。

一起给它。

如果你的日常已经是:

大型Monorepo。

几十个模块。

大量Shared Library。

一次重构需要同时分析代码依赖、Git历史、下游调用、测试和兼容范围,

并让Codex连续做影响分析、重构、回归和二次调整,

这种长时间、多模块的架构工作已经成为常态,

那Pro会更适合。

真正的判断标准仍然是:

ChatGPT、Codex是不是已经长期参与大型工程决策,而不只是写几段代码。


最后

Agent发现5处重复代码,为什么不能马上抽成一个公共模块?

因为:

重复代码不等于重复业务概念。

今天长得一样的两段代码,

未来可能因为完全不同的业务原因分别变化。

如果过早抽象,

你消除的是:

几百行重复。

但可能引入的是:

长期Change Coupling。

所以真正值得判断的不是:

这5段代码有多像?

而是:

它们以后会不会因为同一个原因一起变化?

如果只是当前实现相似,

先保留重复并不可怕。

等变化模式真正稳定以后再抽,

往往更安全。

Agent时代真正需要防的,也不是代码重复本身。

而是:

AI太快地帮我们把“暂时相似”,固化成了“长期耦合”。

持续分享 Codex、大模型开发与 AI 编程实战内容

也整理了稳定的 Plus/Pro 订阅渠道,有需要下方可自取。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 13:42:58

为什么AI漫剧要一站式?知漫剧工作流深度解析

因为漫剧是连载,不是单条视频,环节之间的衔接成本会随集数累积放大。 知漫剧(jj.jiaxunai.cn)是一站式 AI 漫剧创作平台,专为新手小白、漫剧制作者、漫剧创作者打造,用户可以在一个平台进行漫剧全流程制作&…

作者头像 李华
网站建设 2026/10/25 0:20:34

Android 软键盘与输入框对不齐?聊聊输入交互里的坐标问题

getGlobalVisibleRect 拿到的"全局可见区域",在输入框被键盘顶起来之后可能已经不准了——很多人在这上面栽了跟头。 前言 很多 App 都有这个交互:键盘弹着,用户点输入框外面的区域,键盘收起来。实现思路很简单——监听全局触摸事件,如果点击点不在输入框范围内…

作者头像 李华