用了 AI Coding 半年,代码量翻倍但维护变难:我们团队的「技术债决策矩阵」
我们团队 12 个人用 AI 辅助开发 6 个月后代码量涨了 2.3 倍但下半年的迭代速度反而下降了 40%。这篇是我们怎么诊断问题、建立技术债决策框架的过程记录。问题是怎么暴露的去年下半年我们引入了 AI 辅助编码工具。前三个月所有人都很兴奋——Story Points 完成速度快了功能上线节奏也快了。但到第五个月有个工程师跟我说了一句话让我一直记到现在“以前改一个地方我知道会影响哪些地方。现在我不知道了。”我拉了一下数据指标引入 AI 前Q3引入 AI 后Q4变化月均代码提交行数~18,000 行~41,000 行128%平均 PR Review 时间1.2 天2.8 天133%线上 Bug 修复耗时P2 级别4.1h9.3h127%新功能迭代周期Sprint2 周2.7 周35%单元测试覆盖率67%58%-9pp数据很清楚代码量翻倍但系统可维护性显著下降。原因也不难猜。AI 生成代码的速度远快于人工 Review 的速度。当 Review 成为瓶颈时团队的自然反应是先合并后面再优化。后面永远不来技术债就这样积累了。为什么传统技术债管理方法在 AI 时代失效了传统技术债管理通常有几个假设债务是线性积累的——你写多少烂代码债就增加多少Review 能过滤大部分问题——只要 Review 认真技术债可控有空了再还债的窗口期比较长——一两个 sprint 的债不会酿成大问题AI 辅助开发把这三个假设全打碎了。AI 生成代码的速度是指数级的不是线性的。我们团队 12 人月均代码量从 1.8 万行涨到了 4.1 万行。这意味着 Reviewer 的认知负荷也同步翻倍——但人的 Review 能力不会翻倍时间也没增加。Review 开始走形式。当 PR 数量增加 2 倍每个 Reviewer 能投入的时间只有原来的一半Review 质量必然下降。很多 AI 生成的代码——逻辑上能跑、测试能过但命名混乱、边界条件未考虑、与现有架构有隐式耦合——就这样溜进了 main 分支。债务复利效应被放大了。一段糟糕的架构在 AI 辅助开发环境下可能在两周内被引用 20 次因为工程师会让 AI 参考已有代码生成类似代码。债主动在繁殖。我们建立的「技术债决策矩阵」花了大概两个月时间我和团队 Senior 一起讨论、调整、落地了一个决策框架。核心是一个2×2 矩阵两个维度X 轴传播风险Contagion Risk——如果不还这笔债它会不会感染其他代码AI 辅助开发环境下AI 会参考现有代码生成新代码所以烂代码的传播速度比以前快 3-5 倍。Y 轴系统耦合度Coupling Score——这段代码被多少其他模块直接依赖耦合度高 还债成本高但不还的危险也高。高耦合度 (High Coupling) │ [立刻还] [计划还] Q2 象限 Q1 象限 │ ────────┼──────── │ [放行] [观察] Q3 象限 Q4 象限 │ 低耦合度 (Low Coupling) 低传播风险 高传播风险四个象限的决策规则象限传播风险耦合度决策行动Q1 高传播 高耦合高高立刻还本 Sprint 内处理不能 deferQ2 低传播 高耦合低高计划还排进下两个 Sprint BacklogQ3 低传播 低耦合低低放行接受不主动还Q4 高传播 低耦合高低观察标记若被引用超过 3 次升级到 Q1如何量化两个维度这是最关键的部分。矩阵好不好用取决于你能不能快速、一致地给一段债务打分。我们用了以下方法传播风险评分0-10# 传播风险计算逻辑伪代码实际用 AST 分析 人工校准defcontagion_risk(debt_item):score0# 1. 是否是被 AI 频繁引用的样板代码# 检查该文件/模块在过去 30 天内被 git blame 引用的频次ifai_reference_count(debt_item.path)5:score4# 高危AI 会学坏习惯elifai_reference_count(debt_item.path)2:score2# 2. 是否是 utility / helper / base class# 越底层传播风险越高ifdebt_item.layerin[utils,helpers,base,common]:score3# 3. 是否有清晰的接口隔离# 如果接口不清晰调用方会 import 内部实现ifnotdebt_item.has_clear_interface:score2# 4. 过去 2 周是否有新文件 import 了这个模块ifnew_importers_in_2weeks(debt_item.path)0:score1returnmin(score,10)简化版人工评分规则4这段代码在团队的AI Snippet Library里团队会把常用代码教给 AI 参考3工具类 / 基类 / 公共 helper2接口模糊调用方会 import 内部实现1过去 2 周有新文件开始引用它超过 6 分 高传播风险耦合度评分0-10# 用 madge 分析 JS/TS 项目的依赖图npx madge--jsonsrc/|python3 -EOF import json, sys deps json.load(sys.stdin) # 计算每个模块被多少其他模块直接 import reverse_deps {} for module, imports in deps.items(): for imp in imports: reverse_deps.setdefault(imp, []).append(module) # 输出被依赖超过 5 次的模块高耦合候选 for module, dependents in sorted(reverse_deps.items(), keylambda x: -len(x[1])): if len(dependents) 5: print(f{len(dependents):3d} dependents: {module}) EOF对于后端项目Python/Java/Go可以替换成对应的静态分析工具# Python用 pydeps 或 importlabpydeps src/ --max-bacon3--show-deps --no-output21|grep-E^\s[0-9]# Go用 go mod graph 自定义脚本go mod graph|awk{print $2}|sort|uniq-c|sort-rn|head-20# Java用 ArchUnit 或 jdepend耦合度得分参考被依赖模块数耦合分解读0-21-2叶子节点低风险3-53-4中度耦合可接受6-105-7高耦合还债成本高118-10核心节点非常危险实际案例三笔具体的债我用我们实际遇到的三个案例说明矩阵怎么用。案例 AAI 生成的「万能 formatDate 工具函数」背景一个工程师让 AI 生成了一个formatDate函数处理了 7 种格式转换。看起来很好用于是全团队的 AI 都开始把这个函数当作参考样本。3 周后这个函数被 23 个文件 import但其中有 2 种格式转换有 bug时区处理有问题。打分传播风险9/10在 AI Snippet Library 里工具函数接口模糊耦合度8/1023 个文件依赖矩阵判断Q1立刻还。我们的处理暂停使用该函数加deprecated注释修复 bug重构接口一周内迁移所有调用方。代价约 2 个工程师 × 3 天 6 人天。但如果再等 2 个月届时可能被 50 文件依赖成本会是 3-5 倍。案例 B某 API 模块的「临时错误处理占位符」背景上线时为了赶 deadline一个 API 模块的错误处理是这样的try:resultprocess_payment(data)exceptExceptionase:# TODO: 添加具体错误处理logger.error(fPayment failed:{e})return{status:error,message:str(e)}# 直接暴露内部错误信息这段代码的问题直接把内部异常信息返回给前端安全隐患也没有分类处理不同类型的错误。打分传播风险3/10这是业务逻辑不是工具函数AI 不会参考这种特定模块的错误处理作为通用范例耦合度7/10支付模块被订单、退款、账单三个模块依赖矩阵判断Q2计划还。我们的处理排进下个 Sprint写了一个标准化的PaymentException类族重构了错误处理逻辑。不紧急但不能无限期推迟。案例 CAI 生成的「没用上的数据处理管道」背景一个工程师探索性地让 AI 生成了一套数据处理管道代码后来发现业务方向变了这套代码没有被集成但也没有删除。打分传播风险2/10孤立文件没有被其他模块引用AI 也不会参考没有入口的代码耦合度1/10没有任何外部依赖矩阵判断Q3放行直接删掉更干净。我们的处理直接删除了这些文件没有花时间重构它们。在 AI 辅助开发环境下的特殊规则传统技术债框架到这里就够了。但 AI 辅助开发有几个独特的点需要额外的规则覆盖规则 1AI Snippet Library 必须定期审计大多数团队会有意无意地建立一套告诉 AI 参考这个的习惯。不管是.cursorrules、Copilot 的 Custom Instructions还是团队内部的 prompt 模板这些都是AI 的认知地基。地基烂了AI 生成的代码都会往烂的方向走。我们的做法每个 Sprint 末专门检查一次 Snippet Library 和 Custom Instructions# 检查 .cursorrules 中的代码示例是否仍然符合当前最佳实践cat.cursorrules|grep-A10example\|示例\|sample# 检查团队 prompt 模板ls-la~/.config/cursor/prompts/规则Snippet Library 中的代码传播风险直接默认为 8/10。发现问题立即移除或修正不等到债务扫描周期。规则 2AI 生成代码的首次合并要打「传播标记」我们在 PR 模板里加了一个字段## PR 信息 - [ ] 包含 AI 生成代码比例估算____% - 传播风险自评[ ] 低不太可能被复用/参考[ ] 中 [ ] 高工具类/基类/通用逻辑 - 如果是高传播风险[ ] 已确认符合当前团队最佳实践这不是为了管控而是为了让 Reviewer 分配注意力。高传播风险的 AI 代码需要比低传播风险的人工代码更严格的 Review。规则 3技术债还款窗口要主动调度在 AI 辅助开发之前我们是有空了再还。现在我们强制规定每个 Sprint 保留 15% 的容量给技术债偿还不能被需求挤占每季度一次债务审计跑一遍依赖分析重新给所有已知债务打分升级/降级象限这 15% 起初被业务方抵制。我们的解释是“我们现在用 AI 每个月多写了 2 万行代码如果不还债6 个月后所有人都会把时间花在理解旧代码上新功能的速度会归零。”数据说话执行这套框架的第三个月我们的 PR Review 时间从 2.8 天降回到了 1.6 天P2 Bug 修复时间从 9.3h 降到了 6.1h。没有完全回到 AI 引入之前但趋势在好转。一个容易踩的坑「测试债」是最危险的在我们的经验里测试债是所有技术债里传播风险最高的一类而且在 AI 辅助开发环境下被严重低估。原因AI 生成代码的测试覆盖率通常很低AI 更擅长生成业务代码测试代码质量相对差而且当测试缺失时AI 生成代码时无法验证其正确性。没有测试的代码 AI 的盲区AI 看不到约束就会生成看起来合理但实际破坏了某个边界条件的代码。我们的规则任何传播风险 ≥ 6 的代码如果单元测试覆盖率 80%自动升级到 Q1立刻还。这条规则我们用 CI 强制执行# .github/workflows/debt-check.ymlname:Tech Debt Gateon:pull_request:branches:[main]jobs:coverage-check:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-name:Run tests with coveragerun:|pytest --covsrc --cov-reportjson-name:Check high-risk modules coveragerun:|python scripts/check_high_risk_coverage.py \ --coverage-report coverage.json \ --high-risk-modules config/high-risk-modules.txt \ --threshold 80# scripts/check_high_risk_coverage.pyimportjsonimportsysdefcheck_coverage(coverage_file,high_risk_file,threshold):withopen(coverage_file)asf:coveragejson.load(f)withopen(high_risk_file)asf:high_risk_modules[l.strip()forlinf.readlines()]failures[]formoduleinhigh_risk_modules:# 在 coverage report 中查找对应模块forfile_path,file_dataincoverage[files].items():ifmoduleinfile_path:pctfile_data[summary][percent_covered]ifpctthreshold:failures.append(f{file_path}:{pct:.1f}% {threshold}%)iffailures:print(❌ 高风险模块覆盖率不足需先补全测试才能合并)forfinfailures:print(f -{f})sys.exit(1)else:print(f✅ 所有高风险模块覆盖率达标≥{threshold}%)if__name____main__:importargparse parserargparse.ArgumentParser()parser.add_argument(--coverage-report,requiredTrue)parser.add_argument(--high-risk-modules,requiredTrue)parser.add_argument(--threshold,typefloat,default80)argsparser.parse_args()check_coverage(args.coverage_report,args.high_risk_modules,args.threshold)框架落地的三个前提光有矩阵不够还需要三个前提才能让框架真正运转1. Tech Lead 要亲自定义高传播风险的边界不同团队、不同项目高传播风险的定义不同。我们是 Node.js 全栈utils 和 hooks 是高风险区。如果你是微服务团队SDK 层和 proto 定义是高风险区。矩阵是模板象限的边界要你自己校准。2. 还债任务要和功能需求一起估点、一起排优先级不能把技术债放进一个有空再说的 Backlog 里。Q1 债务要出现在当前 Sprint 的 board 上有 Assignee有 Story Point有 DoD完成定义。我们的 DoD 模板- [ ] 修复了原始问题代码/测试 - [ ] 更新了 CHANGELOG技术债标记 - [ ] 如果是工具函数更新了 AI Snippet Library 中的参考示例 - [ ] Reviewer 确认传播风险已消除或降低3. 不要试图把所有债还清这是最重要的认知调整。AI 辅助开发会持续产生技术债速度可能永远快于还债速度。目标不是零债务而是控制 Q1高危债务的数量让它保持在团队可管理的范围内我们的目标是每时每刻 Q1 债务不超过 5 条。Q3 的债可能永远不还这是可以接受的。常见问题Q矩阵里的评分是主观的怎么保证团队打分一致A我们用了两个方法一是每季度做一次校准会议拿 3-5 个历史案例重新打分对齐理解二是对于边界情况传播风险 5-6 分我们直接规定疑似高风险按高风险处理宁可多还一点债不要因为争议而放行。Q15% Sprint 容量真的够用吗AI 产生的债速度太快了。A对于大多数团队15% 是起点不是终点。我们第一个月是 10%发现不够用升到了 15%目前稳定在 15-18%。如果你的 Q1 债务持续积压说明还款速度不够要继续调高。反过来如果 Q1 债务每个 Sprint 都能清零你可以试着降到 12%。这个数字本身要跟债务积压速度动态调整。QPM/产品不理解技术债每次都被业务需求挤掉怎么办A我们的做法是把技术债的成本翻译成业务语言。不说技术债很严重说上个季度我们有 37% 的工程师时间在理解和修复已有代码而不是开发新功能如果这个比例继续增长6 个月后新功能交付速度会下降 50%。数据比抽象概念有说服力。给其他 Tech Lead 的建议用了 6 个月 AI 辅助开发我最大的体会是AI 改变了技术债积累的速度和模式但不改变它最终需要被管理的事实。如果你现在带的团队也在用 AI 辅助开发我的建议是先跑一次依赖分析摸清你的代码库里有多少高耦合节点检查一下你的 AI Snippet Library 和 Custom Instructions里面的代码是否仍然是最佳实践在下个 Sprint 里留出 10% 的容量专门还债哪怕只是先试一试建立一个简单的 Q1 债务跟踪表哪怕只是一个 GitHub Issue Label不需要一开始就建一套完整的系统。先从最危险的 Q1 债开始其他的慢慢来。