用了 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.1h 9.3h +127%
新功能迭代周期(Sprint) 2 周 2.7 周 +35%
单元测试覆盖率 67% 58% -9pp

数据很清楚:代码量翻倍,但系统可维护性显著下降。

原因也不难猜。AI 生成代码的速度远快于人工 Review 的速度。当 Review 成为瓶颈时,团队的自然反应是"先合并,后面再优化"。"后面"永远不来,技术债就这样积累了。


为什么传统技术债管理方法在 AI 时代失效了

传统技术债管理通常有几个假设:

  1. 债务是线性积累的------你写多少烂代码,债就增加多少
  2. Review 能过滤大部分问题------只要 Review 认真,技术债可控
  3. "有空了再还债"的窗口期比较长------一两个 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)------这段代码被多少其他模块直接依赖?耦合度高 = 还债成本高,但不还的危险也高。
scss 复制代码
高耦合度 (High Coupling)
        │
  [立刻还]        [计划还]
  Q2 象限          Q1 象限
        │
────────┼────────
        │
  [放行]           [观察]
  Q3 象限          Q4 象限
        │
低耦合度 (Low Coupling)
        低传播风险          高传播风险

四个象限的决策规则:

象限 传播风险 耦合度 决策 行动
Q1 高传播 + 高耦合 立刻还 本 Sprint 内处理,不能 defer
Q2 低传播 + 高耦合 计划还 排进下两个 Sprint Backlog
Q3 低传播 + 低耦合 放行 接受,不主动还
Q4 高传播 + 低耦合 观察 标记,若被引用超过 3 次升级到 Q1

如何量化两个维度

这是最关键的部分。矩阵好不好用,取决于你能不能快速、一致地给一段债务打分。我们用了以下方法:

传播风险评分(0-10)

python 复制代码
# 传播风险计算逻辑(伪代码,实际用 AST 分析 + 人工校准)
def contagion_risk(debt_item):
    score = 0
    
    # 1. 是否是被 AI 频繁引用的"样板代码"
    # 检查该文件/模块在过去 30 天内被 git blame 引用的频次
    if ai_reference_count(debt_item.path) > 5:
        score += 4  # 高危:AI 会学坏习惯
    elif ai_reference_count(debt_item.path) > 2:
        score += 2
    
    # 2. 是否是 utility / helper / base class
    # 越底层,传播风险越高
    if debt_item.layer in ['utils', 'helpers', 'base', 'common']:
        score += 3
    
    # 3. 是否有清晰的接口隔离
    # 如果接口不清晰,调用方会 import 内部实现
    if not debt_item.has_clear_interface:
        score += 2
    
    # 4. 过去 2 周是否有新文件 import 了这个模块
    if new_importers_in_2weeks(debt_item.path) > 0:
        score += 1
    
    return min(score, 10)

简化版人工评分规则:

  • +4:这段代码在团队的"AI Snippet Library"里(团队会把常用代码教给 AI 参考)
  • +3:工具类 / 基类 / 公共 helper
  • +2:接口模糊,调用方会 import 内部实现
  • +1:过去 2 周有新文件开始引用它

超过 6 分 = 高传播风险

耦合度评分(0-10)

bash 复制代码
# 用 madge 分析 JS/TS 项目的依赖图
npx madge --json src/ | 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(), key=lambda x: -len(x[1])):
    if len(dependents) >= 5:
        print(f"{len(dependents):3d} dependents: {module}")
EOF

对于后端项目(Python/Java/Go),可以替换成对应的静态分析工具:

bash 复制代码
# Python:用 pydeps 或 importlab
pydeps src/ --max-bacon=3 --show-deps --no-output 2>&1 | 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-2 1-2 叶子节点,低风险
3-5 3-4 中度耦合,可接受
6-10 5-7 高耦合,还债成本高
11+ 8-10 核心节点,非常危险

实际案例:三笔具体的债

我用我们实际遇到的三个案例说明矩阵怎么用。

案例 A:AI 生成的「万能 formatDate 工具函数」

背景: 一个工程师让 AI 生成了一个 formatDate 函数,处理了 7 种格式转换。看起来很好用,于是全团队的 AI 都开始把这个函数当作参考样本。3 周后,这个函数被 23 个文件 import,但其中有 2 种格式转换有 bug(时区处理有问题)。

打分:

  • 传播风险:9/10(在 AI Snippet Library 里,工具函数,接口模糊)
  • 耦合度:8/10(23 个文件依赖)

矩阵判断:Q1,立刻还。

我们的处理: 暂停使用该函数(加 @deprecated 注释),修复 bug,重构接口,一周内迁移所有调用方。

代价:约 2 个工程师 × 3 天 = 6 人天。但如果再等 2 个月(届时可能被 50+ 文件依赖),成本会是 3-5 倍。

案例 B:某 API 模块的「临时错误处理占位符」

背景: 上线时为了赶 deadline,一个 API 模块的错误处理是这样的:

python 复制代码
try:
    result = process_payment(data)
except Exception as e:
    # TODO: 添加具体错误处理
    logger.error(f"Payment failed: {e}")
    return {"status": "error", "message": str(e)}  # 直接暴露内部错误信息

这段代码的问题:直接把内部异常信息返回给前端(安全隐患),也没有分类处理不同类型的错误。

打分:

  • 传播风险:3/10(这是业务逻辑,不是工具函数;AI 不会参考这种特定模块的错误处理作为通用范例)
  • 耦合度:7/10(支付模块,被订单、退款、账单三个模块依赖)

矩阵判断:Q2,计划还。

我们的处理: 排进下个 Sprint,写了一个标准化的 PaymentException 类族,重构了错误处理逻辑。不紧急,但不能无限期推迟。

案例 C:AI 生成的「没用上的数据处理管道」

背景: 一个工程师探索性地让 AI 生成了一套数据处理管道代码,后来发现业务方向变了,这套代码没有被集成,但也没有删除。

打分:

  • 传播风险:2/10(孤立文件,没有被其他模块引用,AI 也不会参考没有入口的代码)
  • 耦合度:1/10(没有任何外部依赖)

矩阵判断:Q3,放行(直接删掉更干净)。

我们的处理: 直接删除了这些文件,没有花时间"重构"它们。


在 AI 辅助开发环境下的特殊规则

传统技术债框架到这里就够了。但 AI 辅助开发有几个独特的点,需要额外的规则覆盖:

规则 1:AI Snippet Library 必须定期审计

大多数团队会有意无意地建立一套"告诉 AI 参考这个"的习惯。不管是 .cursorrules、Copilot 的 Custom Instructions,还是团队内部的 prompt 模板,这些都是 AI 的认知"地基"

地基烂了,AI 生成的代码都会往烂的方向走。

我们的做法:每个 Sprint 末,专门检查一次 Snippet Library 和 Custom Instructions:

bash 复制代码
# 检查 .cursorrules 中的代码示例是否仍然符合当前最佳实践
cat .cursorrules | grep -A 10 "example\|示例\|sample"

# 检查团队 prompt 模板
ls -la ~/.config/cursor/prompts/

规则:Snippet Library 中的代码,传播风险直接默认为 8/10。发现问题立即移除或修正,不等到债务扫描周期。

规则 2:AI 生成代码的首次合并要打「传播标记」

我们在 PR 模板里加了一个字段:

markdown 复制代码
## 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 强制执行:

yaml 复制代码
# .github/workflows/debt-check.yml
name: Tech Debt Gate

on:
  pull_request:
    branches: [main]

jobs:
  coverage-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run tests with coverage
        run: |
          pytest --cov=src --cov-report=json
          
      - name: Check high-risk modules coverage
        run: |
          python scripts/check_high_risk_coverage.py \
            --coverage-report coverage.json \
            --high-risk-modules config/high-risk-modules.txt \
            --threshold 80
python 复制代码
# scripts/check_high_risk_coverage.py
import json
import sys

def check_coverage(coverage_file, high_risk_file, threshold):
    with open(coverage_file) as f:
        coverage = json.load(f)
    
    with open(high_risk_file) as f:
        high_risk_modules = [l.strip() for l in f.readlines()]
    
    failures = []
    for module in high_risk_modules:
        # 在 coverage report 中查找对应模块
        for file_path, file_data in coverage['files'].items():
            if module in file_path:
                pct = file_data['summary']['percent_covered']
                if pct < threshold:
                    failures.append(f"{file_path}: {pct:.1f}% < {threshold}%")
    
    if failures:
        print("❌ 高风险模块覆盖率不足,需先补全测试才能合并:")
        for f in failures:
            print(f"  - {f}")
        sys.exit(1)
    else:
        print(f"✅ 所有高风险模块覆盖率达标(≥{threshold}%)")

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--coverage-report', required=True)
    parser.add_argument('--high-risk-modules', required=True)
    parser.add_argument('--threshold', type=float, default=80)
    args = parser.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 模板:

css 复制代码
- [ ] 修复了原始问题(代码/测试)
- [ ] 更新了 CHANGELOG(技术债标记)
- [ ] 如果是工具函数:更新了 AI Snippet Library 中的参考示例
- [ ] Reviewer 确认传播风险已消除或降低

3. 不要试图把所有债还清

这是最重要的认知调整。AI 辅助开发会持续产生技术债,速度可能永远快于还债速度。目标不是"零债务",而是控制 Q1(高危债务)的数量,让它保持在团队可管理的范围内(我们的目标是每时每刻 Q1 债务不超过 5 条)

Q3 的债可能永远不还,这是可以接受的。


常见问题

Q:矩阵里的评分是主观的,怎么保证团队打分一致?

A:我们用了两个方法:一是每季度做一次"校准会议",拿 3-5 个历史案例重新打分,对齐理解;二是对于边界情况(传播风险 5-6 分),我们直接规定"疑似高风险按高风险处理",宁可多还一点债,不要因为争议而放行。

Q:15% Sprint 容量真的够用吗?AI 产生的债速度太快了。

A:对于大多数团队,15% 是起点,不是终点。我们第一个月是 10%,发现不够用,升到了 15%,目前稳定在 15-18%。如果你的 Q1 债务持续积压,说明还款速度不够,要继续调高。反过来,如果 Q1 债务每个 Sprint 都能清零,你可以试着降到 12%。这个数字本身要跟债务积压速度动态调整。

Q:PM/产品不理解技术债,每次都被业务需求挤掉,怎么办?

A:我们的做法是把技术债的成本翻译成业务语言。不说"技术债很严重",说"上个季度我们有 37% 的工程师时间在理解和修复已有代码,而不是开发新功能;如果这个比例继续增长,6 个月后新功能交付速度会下降 50%"。数据比抽象概念有说服力。


给其他 Tech Lead 的建议

用了 6 个月 AI 辅助开发,我最大的体会是:AI 改变了技术债积累的速度和模式,但不改变它最终需要被管理的事实。

如果你现在带的团队也在用 AI 辅助开发,我的建议是:

  1. 先跑一次依赖分析,摸清你的代码库里有多少"高耦合节点"
  2. 检查一下你的 AI Snippet Library 和 Custom Instructions,里面的代码是否仍然是最佳实践
  3. 在下个 Sprint 里留出 10% 的容量专门还债,哪怕只是先试一试
  4. 建立一个简单的 Q1 债务跟踪表(哪怕只是一个 GitHub Issue Label)

不需要一开始就建一套完整的系统。先从最危险的 Q1 债开始,其他的慢慢来。

相关推荐
SimonKing2 小时前
实用,DynamicTP进阶之数据采集与告警
java·后端·程序员
DogDaoDao2 小时前
【GitHub】AutoGPT 深度技术解析:开源自主 AI Agent 平台架构全解
人工智能·程序员·开源·github·ai编程·ai agent·智能体
Captaincc3 小时前
置身钉内:一个 AI 办公产品的理想、失焦与组织困境
前端·程序员
DogDaoDao3 小时前
【GitHub】Hermes Agent 深度技术分析
程序员·大模型·github·ai编程·ai agent·智能体·hermers agent
云恒要逆袭4 小时前
Hello World背后的秘密:Java程序是这样运行的
java·后端·程序员
用户852495071846 小时前
Bun 到底是什么?一个比 Node.js "更快更香"的 JS 运行时
javascript·程序员
CodeSheep6 小时前
宇树科技,即将上市!
前端·后端·程序员
树獭非懒8 小时前
智能体演化简史:从符号规则到涌现智能
人工智能·程序员·agent
wing981 天前
我的AI编程体验:从白嫖到付费,我为什么最终留下了Codex
前端·人工智能·程序员