1. 为什么AI辅助编程的项目更容易“烂掉”1.1 一个普遍存在的认知误区很多人觉得,用了AI写代码,项目质量应该更高才对。毕竟AI不会手抖打错变量名,不会忘记加分号,生成的代码看起来结构工整、注释齐全。但实际跑过几个AI辅助项目之后,你会发现一个很尴尬的现象:项目烂掉的速度反而更快了。我自己的经历就很典型。去年接手一个内部工具项目,前期用AI辅助生成,两周就把核心功能跑通了,当时觉得效率飞起。结果三个月后要加一个新功能,打开代码库一看,光是搞清楚数据从哪流到哪就花了整整两天。各种函数命名风格不统一,有的地方用驼峰有的地方用下划线,同一个业务逻辑在三四个文件里各写了一遍,改一处漏三处。这就是典型的“AI加速了代码产出,也加速了混乱积累”。1.2 AI编程的“熵增效应”从何而来要理解这个问题,得先搞清楚AI写代码和人类写代码的本质区别。人类开发者写代码时,脑子里有一张隐形的架构图。哪怕这张图不完整,他也会下意识地做取舍:这个函数放哪里合适、这个变量名跟已有的风格保持一致、这段逻辑要不要抽出来复用。这些决策很多是直觉性的,但正是这些直觉在维持项目的秩序。AI不一样。AI每次生成代码,都是基于当前对话上下文的一次“局部最优解”。它不知道你三个月前在另一个文件里定义了一个功能几乎一样的工具函数,也不知道你团队约定所有API返回必须包一层统一格式。它只关心:根据你刚才说的话,生成一段能跑的代码。这就导致一个结果:每次AI生成都是一次“局部合理、全局随机”的决策。单看每个文件都挺像样,合在一起就是一盘散沙。项目越大,这种散沙效应越明显,直到某一天你发现改任何一处都像在拆炸弹。1.3 “烂掉”的具体信号有哪些项目开始烂掉不是一瞬间的事,它有一些渐进式的信号。我总结了几条,你可以对照看看自己的项目有没有中招:改A坏B:修了一个bug,结果另一个不相关的功能挂了。这说明模块之间的隐式耦合已经失控。不敢删代码:看到一段代码觉得没用,但不敢删,因为不确定哪里在调用它。新人上手时间越来越长:最开始新人半天能跑起来,后来变成三天,再后来一周都搞不定本地环境。同一个概念有多个名字:比如“用户”这个概念,在数据库叫user,在接口叫member,在前端叫account,在文档里叫客户。测试越写越多但信心越来越少:测试覆盖率上去了,但每次发版还是提心吊胆。这些信号背后的根源,其实都指向同一件事:项目缺少一套持续维护一致性的机制。而AI辅助编程恰恰放大了这个问题的严重性,因为AI生成代码的速度远超人脑维护一致性的速度。2. 核心解法:把“架构约束”变成AI能读懂的东西2.1 为什么传统的架构文档不管用说到维护项目一致性,很多团队的第一反应是写文档。搞一个架构设计文档,画几张图,写清楚分层规则、命名规范、接口约定。想法很好,但实际执行下来基本是废纸一张。原因很简单:文档是给人看的,但写代码的是AI。你不可能每次让AI生成代码之前,先把几十页的架构文档贴进对话窗口。就算贴了,AI也未必能准确理解那些抽象的描述。更关键的是,文档和代码是分离的,代码改了文档不会自动更新,时间一长文档就变成了“历史文献”。我见过一个团队,架构文档写得非常漂亮,分层清晰、职责明确。但实际代码里,数据访问层直接调用了展示层的工具函数,因为“当时赶时间”。文档里写的规则,在代码里没有任何强制力。2.2 真正有效的做法:可执行的架构约束那什么才是有效的?答案是:把架构约束变成可执行、可验证、AI能直接读取的东西。具体来说,就是不要写“数据访问层不能依赖展示层”这种自然语言描述,而是写一条可以自动检查的规则。比如在代码里加一个lint规则,或者在CI流程里加一个依赖检查步骤。这样每次AI生成代码后,自动跑一遍检查,违规了就直接报错。这个思路的核心转变是:从“告诉AI应该怎么做”变成“让AI不得不这么做”。前者依赖AI的理解力和自觉性,后者依赖工具链的强制力。显然后者靠谱得多。2.3 一个最小可行的约束框架长什么样你不需要一上来就搞一套复杂的架构治理体系。从最小可行的角度,我建议先做三件事:第一,定义模块边界。把项目拆成若干个模块,每个模块有明确的职责。比如core放核心业务逻辑,infra放数据库和外部服务调用,api放接口层。然后规定模块之间的依赖方向:api可以依赖core,core可以依赖infra,但反过来不行。第二,统一命名和代码风格。用工具强制,不要靠自觉。Python用ruff,JavaScript/TypeScript用eslint加prettier,Go用gofmt。配置文件放在项目根目录,AI生成代码后自动格式化。第三,建立“代码地图”。在项目根目录放一个ARCHITECTURE.md文件,用简洁的语言描述每个模块的职责、关键文件的位置、核心数据流的走向。这个文件不需要很详细,但必须保持更新。每次AI生成新代码后,如果涉及新的模块或数据流,就更新这个文件。这三件事做下来,大概花半天时间,但效果非常明显。AI生成代码时,你可以把ARCHITECTURE.md的内容作为上下文传给它,它就能在生成时尽量遵守已有的结构。即使它偶尔跑偏,后面的lint和依赖检查也能兜住。3. 实操:把约束嵌入AI编程工作流3.1 工具链选型与配置先说一下我目前用的工具组合,这套组合在多个项目上验证过,稳定性和效率都不错:工具用途为什么选它ruffPython代码检查和格式化速度快,规则全,配置简单eslint+prettierJS/TS代码检查和格式化生态成熟,插件丰富dependency-cruiserJS/TS模块依赖检查可以自定义依赖规则,可视化依赖图import-linterPython模块依赖检查专门做分层依赖检查,配置直观pre-commitGit提交前自动检查把检查嵌入开发流程,不依赖人工配置的核心思路是:所有检查都自动化,所有规则都写进配置文件,所有配置文件都进版本控制。这样AI生成代码后,你只需要跑一条命令,就能知道有没有违规。以import-linter为例,配置文件大概长这样:[importlinter] root_package = myproject [importlinter:contract:layers] name = 分层依赖约束 type = layers layers = myproject.api myproject.core myproject.infra这段配置的意思是:api可以依赖core和infra,core可以依赖infra,但infra不能依赖上面两层。如果AI生成的代码违反了这条规则,import-linter会直接报错,告诉你哪个文件违反了哪条规则。3.2 把架构上下文喂给AI的正确姿势很多人用AI写代码时,只给一个简单的需求描述,比如“帮我写一个用户注册的接口”。这样AI只能凭感觉生成,出来的代码大概率跟项目现有风格不一致。正确的做法是:在对话开始时,先把项目的架构约束和关键约定告诉AI。具体来说,我通常会把以下内容作为系统提示或首条消息:项目的模块划分和依赖方向命名规范(文件命名、函数命名、变量命名)错误处理约定(比如统一用自定义异常,还是返回错误码)日志和监控的接入方式测试文件的组织方式这些内容不需要很长,控制在500字以内,但必须准确。你可以直接从ARCHITECTURE.md里摘取核心部分。这样AI在生成代码时,就有了明确的边界感,不会天马行空地发挥。3.3 一个完整的AI编程工作流示例下面是我现在用的工作流,从需求到代码入库,一共六步:第一步:需求拆解。把需求拆成若干个小的功能点,每个功能点对应一个模块的改动。比如“用户注册”可以拆成:api层加一个路由、core层加一个注册服务、infra层加一个用户仓储。第二步:上下文准备。打开AI对话窗口,先贴入架构约束和当前模块的关键文件内容。关键文件包括:模块的入口文件、已有的类似功能实现、相关的数据模型定义。第三步:逐模块生成。不要一次性让AI生成所有模块的代码,而是一个模块一个模块来。先生成infra层的仓储代码,确认没问题后再生成core层的服务代码,最后生成api层的路由代码。这样每一步都在上一步的基础上进行,AI能保持上下文一致。第四步:本地检查。每生成一个模块的代码,立刻跑一遍lint和依赖检查。如果有违规,当场让AI修正,不要等到最后一起改。第五步:测试补充。让AI为生成的代码写单元测试。这里有个技巧:先让AI列出测试用例,你确认后再让它写具体代码。这样能避免AI写出一堆无效测试。第六步:提交前检查。所有代码写完后,跑一遍完整的pre-commit检查。通过后再提交,确保入库的代码符合所有约束。这个流程看起来比“直接让AI生成”麻烦,但实际跑下来,前期多花的时间在后期维护时会加倍省回来。我做过对比:用这个流程的项目,三个月后的维护成本比不用流程的项目低至少一半。4. 常见问题与排查技巧实录4.1 AI生成的代码总是绕过我的架构约束怎么办这是最常见的问题。你明明在提示里写了“数据访问层不能调用展示层”,但AI生成的代码还是直接import了展示层的工具函数。原因通常有两个:一是你的约束描述不够具体,AI理解成了“尽量不要”而不是“绝对不能”;二是AI在生成时只关注了当前功能的实现,忽略了全局约束。解决办法分两步:第一步,把约束写得更具体。不要写“不能调用展示层”,而是写“infra目录下的任何文件,不能出现from myproject.api或import myproject.api这样的语句”。越具体,AI越容易遵守。第二步,用工具兜底。即使AI违反了约束,import-linter或dependency-cruiser也能在检查阶段抓住。这时候你只需要把报错信息贴回给AI,让它修正即可。修正几次之后,AI在后续生成中会逐渐“记住”这些约束。4.2 项目已经烂掉了,还有救吗有救,但需要分阶段来。不要试图一次性重构整个项目,那样风险太大。我的建议是:先冻结新功能开发,花一周时间做“架构止血”。具体操作是:第一天:梳理现有模块,画出实际的依赖关系图。不用很精确,大致清楚就行。第二天:定义目标架构,确定模块边界和依赖方向。第三到四天:配置lint和依赖检查工具,先只检查新代码,不检查老代码。第五天:把ARCHITECTURE.md写出来,作为后续AI生成的上下文。第六到七天:选一个相对独立的模块,按照新架构重构一遍,作为样板。一周之后,新代码必须符合架构约束,老代码逐步迁移。每迁移一个模块,就把该模块加入检查范围。这样半年左右,整个项目就能恢复到可控状态。4.3 团队协作时怎么保证每个人都遵守如果是多人协作,光靠工具检查还不够,还需要一些流程上的配合。第一,把架构检查加入CI流程。每次提交代码,CI自动跑一遍依赖检查和lint。不通过就阻止合并。这样即使有人本地忘了跑检查,CI也能兜住。第二,定期做架构回顾。每两周花半小时,大家一起看一下最近的代码变更,有没有出现新的“架构异味”。比如某个模块的依赖数突然增加了很多,或者出现了新的循环依赖。第三,把架构约束写进代码评审清单。每次代码评审时,除了看功能实现,也要看是否符合架构约束。评审人不需要很懂架构,只需要对照检查清单逐条确认即可。4.4 常见问题速查表问题现象可能原因排查方法解决措施AI生成的代码风格不一致提示中未包含风格约定检查AI对话的上下文在提示中加入代码风格示例模块间出现循环依赖架构约束未配置或未生效跑依赖检查工具配置import-linter或dependency-cruiser同一个功能多处实现缺少代码地图,AI不知道已有实现搜索关键函数名建立ARCHITECTURE.md并保持更新测试通过但线上出问题测试未覆盖关键路径检查测试用例列表让AI先列用例再写测试重构后功能异常重构范围过大,未分步验证检查重构提交记录分模块重构,每步都跑测试5. 长期维护:让项目“不烂”的日常习惯5.1 每周花15分钟做“架构体检”项目烂掉不是一天发生的,而是每天烂一点。所以维护也不需要大动干戈,每周花15分钟做一次小检查就够了。我通常周五下午做这件事,具体检查三项:依赖关系有没有变复杂:跑一遍依赖检查工具,看看有没有新增的违规依赖。ARCHITECTURE.md有没有过时:对照最近的代码变更,看看有没有新的模块或数据流需要补充。有没有出现“孤儿代码”:搜索一下最近三个月没被任何文件引用的函数或类,如果有,考虑删除或标记。这三项检查加起来不超过15分钟,但能及时发现苗头,避免问题积累。5.2 让AI帮你维护架构文档ARCHITECTURE.md最大的敌人是“忘记更新”。我的做法是:每次让AI生成新模块的代码后,顺便让它更新ARCHITECTURE.md。具体操作是:在AI对话的最后,加一句“请根据刚才生成的代码,更新ARCHITECTURE.md中关于XX模块的描述”。AI会自动读取当前文件内容,然后生成更新后的版本。你只需要确认一下,没问题就提交。这样ARCHITECTURE.md就能跟代码保持同步,不会变成“历史文献”。5.3 定期重构:小步快跑,不要憋大招很多人对重构有误解,觉得要等到项目烂得不行了再一次性大重构。但大重构风险极高,而且重构期间业务需求还在变,很容易搞成“边重构边加功能”的混乱局面。更好的做法是:每次加新功能时,顺便重构一点点。比如你要在某个模块加一个新函数,发现这个模块已经有点臃肿了,那就顺手把其中一两个职责抽出去,放到更合适的模块。每次只动一点点,风险可控,而且不会影响业务进度。我自己的经验是:每次加功能时,花10%的时间做“顺手重构”。一年下来,项目结构反而比刚开始时更清晰。5.4 一个真实的踩坑记录最后分享一个我踩过的坑。有一次我用AI生成一个数据处理模块,AI生成了一段很漂亮的代码,用了各种设计模式,看起来非常专业。我当时觉得没问题,直接合并了。结果两周后,另一个同事要在这个模块加一个类似的功能,发现AI之前生成的代码里有一个抽象基类,但没有任何子类继承它。也就是说,那段代码里有一堆“预留的扩展点”,但实际上根本用不上。同事不敢删,怕以后要用,结果这个模块越来越臃肿。后来我总结了一条经验:AI生成的代码,如果包含了你当前用不上的抽象或扩展点,果断删掉。AI倾向于“过度设计”,因为它见过太多“最佳实践”的代码,但实际项目中,简单直接往往比“预留扩展”更靠谱。删掉那些用不上的抽象,代码量能减少30%以上,维护成本直线下降。这个坑让我意识到,AI编程的“烂掉”风险,不仅来自代码风格不一致,还来自“过度设计”带来的认知负担。所以我现在让AI生成代码时,会明确加一句:“不要引入当前用不上的抽象或设计模式,保持简单直接。”效果很好,生成的代码明显更接地气。
1. 为什么AI辅助编程的项目更容易“烂掉”1.1 一个普遍存在的认知误区很多人觉得,用了AI写代码,项目质量应该更高才对。毕竟AI不会手抖打错变量名,不会忘记加分号,生成的代码看起来结构工整、注释齐全。但实际跑过几个AI辅助项目之后,你会发现一个很尴尬的现象:项目烂掉的速度反而更快了。我自己的经历就很典型。去年接手一个内部工具项目,前期用AI辅助生成,两周就把核心功能跑通了,当时觉得效率飞起。结果三个月后要加一个新功能,打开代码库一看,光是搞清楚数据从哪流到哪就花了整整两天。各种函数命名风格不统一,有的地方用驼峰有的地方用下划线,同一个业务逻辑在三四个文件里各写了一遍,改一处漏三处。这就是典型的“AI加速了代码产出,也加速了混乱积累”。1.2 AI编程的“熵增效应”从何而来要理解这个问题,得先搞清楚AI写代码和人类写代码的本质区别。人类开发者写代码时,脑子里有一张隐形的架构图。哪怕这张图不完整,他也会下意识地做取舍:这个函数放哪里合适、这个变量名跟已有的风格保持一致、这段逻辑要不要抽出来复用。这些决策很多是直觉性的,但正是这些直觉在维持项目的秩序。AI不一样。AI每次生成代码,都是基于当前对话上下文的一次“局部最优解”。它不知道你三个月前在另一个文件里定义了一个功能几乎一样的工具函数,也不知道你团队约定所有API返回必须包一层统一格式。它只关心:根据你刚才说的话,生成一段能跑的代码。这就导致一个结果:每次AI生成都是一次“局部合理、全局随机”的决策。单看每个文件都挺像样,合在一起就是一盘散沙。项目越大,这种散沙效应越明显,直到某一天你发现改任何一处都像在拆炸弹。1.3 “烂掉”的具体信号有哪些项目开始烂掉不是一瞬间的事,它有一些渐进式的信号。我总结了几条,你可以对照看看自己的项目有没有中招:改A坏B:修了一个bug,结果另一个不相关的功能挂了。这说明模块之间的隐式耦合已经失控。不敢删代码:看到一段代码觉得没用,但不敢删,因为不确定哪里在调用它。新人上手时间越来越长:最开始新人半天能跑起来,后来变成三天,再后来一周都搞不定本地环境。同一个概念有多个名字:比如“用户”这个概念,在数据库叫user,在接口叫member,在前端叫account,在文档里叫客户。测试越写越多但信心越来越少:测试覆盖率上去了,但每次发版还是提心吊胆。这些信号背后的根源,其实都指向同一件事:项目缺少一套持续维护一致性的机制。而AI辅助编程恰恰放大了这个问题的严重性,因为AI生成代码的速度远超人脑维护一致性的速度。2. 核心解法:把“架构约束”变成AI能读懂的东西2.1 为什么传统的架构文档不管用说到维护项目一致性,很多团队的第一反应是写文档。搞一个架构设计文档,画几张图,写清楚分层规则、命名规范、接口约定。想法很好,但实际执行下来基本是废纸一张。原因很简单:文档是给人看的,但写代码的是AI。你不可能每次让AI生成代码之前,先把几十页的架构文档贴进对话窗口。就算贴了,AI也未必能准确理解那些抽象的描述。更关键的是,文档和代码是分离的,代码改了文档不会自动更新,时间一长文档就变成了“历史文献”。我见过一个团队,架构文档写得非常漂亮,分层清晰、职责明确。但实际代码里,数据访问层直接调用了展示层的工具函数,因为“当时赶时间”。文档里写的规则,在代码里没有任何强制力。2.2 真正有效的做法:可执行的架构约束那什么才是有效的?答案是:把架构约束变成可执行、可验证、AI能直接读取的东西。具体来说,就是不要写“数据访问层不能依赖展示层”这种自然语言描述,而是写一条可以自动检查的规则。比如在代码里加一个lint规则,或者在CI流程里加一个依赖检查步骤。这样每次AI生成代码后,自动跑一遍检查,违规了就直接报错。这个思路的核心转变是:从“告诉AI应该怎么做”变成“让AI不得不这么做”。前者依赖AI的理解力和自觉性,后者依赖工具链的强制力。显然后者靠谱得多。2.3 一个最小可行的约束框架长什么样你不需要一上来就搞一套复杂的架构治理体系。从最小可行的角度,我建议先做三件事:第一,定义模块边界。把项目拆成若干个模块,每个模块有明确的职责。比如core放核心业务逻辑,infra放数据库和外部服务调用,api放接口层。然后规定模块之间的依赖方向:api可以依赖core,core可以依赖infra,但反过来不行。第二,统一命名和代码风格。用工具强制,不要靠自觉。Python用ruff,JavaScript/TypeScript用eslint加prettier,Go用gofmt。配置文件放在项目根目录,AI生成代码后自动格式化。第三,建立“代码地图”。在项目根目录放一个ARCHITECTURE.md文件,用简洁的语言描述每个模块的职责、关键文件的位置、核心数据流的走向。这个文件不需要很详细,但必须保持更新。每次AI生成新代码后,如果涉及新的模块或数据流,就更新这个文件。这三件事做下来,大概花半天时间,但效果非常明显。AI生成代码时,你可以把ARCHITECTURE.md的内容作为上下文传给它,它就能在生成时尽量遵守已有的结构。即使它偶尔跑偏,后面的lint和依赖检查也能兜住。3. 实操:把约束嵌入AI编程工作流3.1 工具链选型与配置先说一下我目前用的工具组合,这套组合在多个项目上验证过,稳定性和效率都不错:工具用途为什么选它ruffPython代码检查和格式化速度快,规则全,配置简单eslint+prettierJS/TS代码检查和格式化生态成熟,插件丰富dependency-cruiserJS/TS模块依赖检查可以自定义依赖规则,可视化依赖图import-linterPython模块依赖检查专门做分层依赖检查,配置直观pre-commitGit提交前自动检查把检查嵌入开发流程,不依赖人工配置的核心思路是:所有检查都自动化,所有规则都写进配置文件,所有配置文件都进版本控制。这样AI生成代码后,你只需要跑一条命令,就能知道有没有违规。以import-linter为例,配置文件大概长这样:[importlinter] root_package = myproject [importlinter:contract:layers] name = 分层依赖约束 type = layers layers = myproject.api myproject.core myproject.infra这段配置的意思是:api可以依赖core和infra,core可以依赖infra,但infra不能依赖上面两层。如果AI生成的代码违反了这条规则,import-linter会直接报错,告诉你哪个文件违反了哪条规则。3.2 把架构上下文喂给AI的正确姿势很多人用AI写代码时,只给一个简单的需求描述,比如“帮我写一个用户注册的接口”。这样AI只能凭感觉生成,出来的代码大概率跟项目现有风格不一致。正确的做法是:在对话开始时,先把项目的架构约束和关键约定告诉AI。具体来说,我通常会把以下内容作为系统提示或首条消息:项目的模块划分和依赖方向命名规范(文件命名、函数命名、变量命名)错误处理约定(比如统一用自定义异常,还是返回错误码)日志和监控的接入方式测试文件的组织方式这些内容不需要很长,控制在500字以内,但必须准确。你可以直接从ARCHITECTURE.md里摘取核心部分。这样AI在生成代码时,就有了明确的边界感,不会天马行空地发挥。3.3 一个完整的AI编程工作流示例下面是我现在用的工作流,从需求到代码入库,一共六步:第一步:需求拆解。把需求拆成若干个小的功能点,每个功能点对应一个模块的改动。比如“用户注册”可以拆成:api层加一个路由、core层加一个注册服务、infra层加一个用户仓储。第二步:上下文准备。打开AI对话窗口,先贴入架构约束和当前模块的关键文件内容。关键文件包括:模块的入口文件、已有的类似功能实现、相关的数据模型定义。第三步:逐模块生成。不要一次性让AI生成所有模块的代码,而是一个模块一个模块来。先生成infra层的仓储代码,确认没问题后再生成core层的服务代码,最后生成api层的路由代码。这样每一步都在上一步的基础上进行,AI能保持上下文一致。第四步:本地检查。每生成一个模块的代码,立刻跑一遍lint和依赖检查。如果有违规,当场让AI修正,不要等到最后一起改。第五步:测试补充。让AI为生成的代码写单元测试。这里有个技巧:先让AI列出测试用例,你确认后再让它写具体代码。这样能避免AI写出一堆无效测试。第六步:提交前检查。所有代码写完后,跑一遍完整的pre-commit检查。通过后再提交,确保入库的代码符合所有约束。这个流程看起来比“直接让AI生成”麻烦,但实际跑下来,前期多花的时间在后期维护时会加倍省回来。我做过对比:用这个流程的项目,三个月后的维护成本比不用流程的项目低至少一半。4. 常见问题与排查技巧实录4.1 AI生成的代码总是绕过我的架构约束怎么办这是最常见的问题。你明明在提示里写了“数据访问层不能调用展示层”,但AI生成的代码还是直接import了展示层的工具函数。原因通常有两个:一是你的约束描述不够具体,AI理解成了“尽量不要”而不是“绝对不能”;二是AI在生成时只关注了当前功能的实现,忽略了全局约束。解决办法分两步:第一步,把约束写得更具体。不要写“不能调用展示层”,而是写“infra目录下的任何文件,不能出现from myproject.api或import myproject.api这样的语句”。越具体,AI越容易遵守。第二步,用工具兜底。即使AI违反了约束,import-linter或dependency-cruiser也能在检查阶段抓住。这时候你只需要把报错信息贴回给AI,让它修正即可。修正几次之后,AI在后续生成中会逐渐“记住”这些约束。4.2 项目已经烂掉了,还有救吗有救,但需要分阶段来。不要试图一次性重构整个项目,那样风险太大。我的建议是:先冻结新功能开发,花一周时间做“架构止血”。具体操作是:第一天:梳理现有模块,画出实际的依赖关系图。不用很精确,大致清楚就行。第二天:定义目标架构,确定模块边界和依赖方向。第三到四天:配置lint和依赖检查工具,先只检查新代码,不检查老代码。第五天:把ARCHITECTURE.md写出来,作为后续AI生成的上下文。第六到七天:选一个相对独立的模块,按照新架构重构一遍,作为样板。一周之后,新代码必须符合架构约束,老代码逐步迁移。每迁移一个模块,就把该模块加入检查范围。这样半年左右,整个项目就能恢复到可控状态。4.3 团队协作时怎么保证每个人都遵守如果是多人协作,光靠工具检查还不够,还需要一些流程上的配合。第一,把架构检查加入CI流程。每次提交代码,CI自动跑一遍依赖检查和lint。不通过就阻止合并。这样即使有人本地忘了跑检查,CI也能兜住。第二,定期做架构回顾。每两周花半小时,大家一起看一下最近的代码变更,有没有出现新的“架构异味”。比如某个模块的依赖数突然增加了很多,或者出现了新的循环依赖。第三,把架构约束写进代码评审清单。每次代码评审时,除了看功能实现,也要看是否符合架构约束。评审人不需要很懂架构,只需要对照检查清单逐条确认即可。4.4 常见问题速查表问题现象可能原因排查方法解决措施AI生成的代码风格不一致提示中未包含风格约定检查AI对话的上下文在提示中加入代码风格示例模块间出现循环依赖架构约束未配置或未生效跑依赖检查工具配置import-linter或dependency-cruiser同一个功能多处实现缺少代码地图,AI不知道已有实现搜索关键函数名建立ARCHITECTURE.md并保持更新测试通过但线上出问题测试未覆盖关键路径检查测试用例列表让AI先列用例再写测试重构后功能异常重构范围过大,未分步验证检查重构提交记录分模块重构,每步都跑测试5. 长期维护:让项目“不烂”的日常习惯5.1 每周花15分钟做“架构体检”项目烂掉不是一天发生的,而是每天烂一点。所以维护也不需要大动干戈,每周花15分钟做一次小检查就够了。我通常周五下午做这件事,具体检查三项:依赖关系有没有变复杂:跑一遍依赖检查工具,看看有没有新增的违规依赖。ARCHITECTURE.md有没有过时:对照最近的代码变更,看看有没有新的模块或数据流需要补充。有没有出现“孤儿代码”:搜索一下最近三个月没被任何文件引用的函数或类,如果有,考虑删除或标记。这三项检查加起来不超过15分钟,但能及时发现苗头,避免问题积累。5.2 让AI帮你维护架构文档ARCHITECTURE.md最大的敌人是“忘记更新”。我的做法是:每次让AI生成新模块的代码后,顺便让它更新ARCHITECTURE.md。具体操作是:在AI对话的最后,加一句“请根据刚才生成的代码,更新ARCHITECTURE.md中关于XX模块的描述”。AI会自动读取当前文件内容,然后生成更新后的版本。你只需要确认一下,没问题就提交。这样ARCHITECTURE.md就能跟代码保持同步,不会变成“历史文献”。5.3 定期重构:小步快跑,不要憋大招很多人对重构有误解,觉得要等到项目烂得不行了再一次性大重构。但大重构风险极高,而且重构期间业务需求还在变,很容易搞成“边重构边加功能”的混乱局面。更好的做法是:每次加新功能时,顺便重构一点点。比如你要在某个模块加一个新函数,发现这个模块已经有点臃肿了,那就顺手把其中一两个职责抽出去,放到更合适的模块。每次只动一点点,风险可控,而且不会影响业务进度。我自己的经验是:每次加功能时,花10%的时间做“顺手重构”。一年下来,项目结构反而比刚开始时更清晰。5.4 一个真实的踩坑记录最后分享一个我踩过的坑。有一次我用AI生成一个数据处理模块,AI生成了一段很漂亮的代码,用了各种设计模式,看起来非常专业。我当时觉得没问题,直接合并了。结果两周后,另一个同事要在这个模块加一个类似的功能,发现AI之前生成的代码里有一个抽象基类,但没有任何子类继承它。也就是说,那段代码里有一堆“预留的扩展点”,但实际上根本用不上。同事不敢删,怕以后要用,结果这个模块越来越臃肿。后来我总结了一条经验:AI生成的代码,如果包含了你当前用不上的抽象或扩展点,果断删掉。AI倾向于“过度设计”,因为它见过太多“最佳实践”的代码,但实际项目中,简单直接往往比“预留扩展”更靠谱。删掉那些用不上的抽象,代码量能减少30%以上,维护成本直线下降。这个坑让我意识到,AI编程的“烂掉”风险,不仅来自代码风格不一致,还来自“过度设计”带来的认知负担。所以我现在让AI生成代码时,会明确加一句:“不要引入当前用不上的抽象或设计模式,保持简单直接。”效果很好,生成的代码明显更接地气。