Tech Debt Didn't Start with AI

最近有个论调很流行:AI 生成的代码质量差,遍地 tech debt,好像 tech debt 是 AI 时代才有的新问题。

我不同意。

Tech debt 不是什么新东西。它在 COBOL 时代就有了,在瀑布流时代就有了,在敏捷时代就有了,在 GitHub Copilot 出来之前就已经是每个技术团队的核心议题。

AI 确实让一些问题更明显了,但它不是 tech debt 的根源。

这篇文章想讲清楚一件事:tech debt 的根源是人和组织的决策,不是工具的选择。理解了这一点,才知道怎么真正解决 tech debt,而不是把它当成 AI 的锅。

Tech Debt 是什么?不止是"写烂代码"

Ward Cunningham 在 1992 年提出了"技术债务"这个比喻:

技术债务就像金融债务:我们为了快速前进而承担的额外成本,未来需要通过重构和修复来"还利息"。

这个比喻被广泛引用,但大多数人对 tech debt 的理解还是太浅。

Tech debt 不只是"写烂代码"。它有四种形态:

类型一:代码债务

这是最常见的类型,也是大多数人理解的 tech debt。

# 代码债务的典型例子
def calculate_price(order, customer):
    # 硬编码折扣规则,散落在代码各处
    discount = 0.1  # 基础折扣
    if customer.type == 'vip':
        discount = 0.15
    if customer.age > 60:
        discount = 0.2
    if order.total > 1000:
        discount += 0.05
    if customer.registered_year < 2020:
        discount += 0.03
    # ... 更多硬编码规则

    return order.total * (1 - discount)

# 问题:
# 1. 折扣规则分散在各处,没有统一管理
# 2. 每加一个规则要改这个函数
# 3. 测试困难,无法覆盖所有组合
# 4. 规则之间可能有冲突

代码债务的特点是看得见,摸得着,可以通过代码审查、静态分析工具检测出来。

类型二:架构债务

架构债务比代码债务更严重,但更隐蔽。

# 架构债务的典型例子:越来越难改的订单服务
class OrderService:
    def __init__(self):
        self.db = LegacyDatabaseConnection()
        self.cache = InMemoryCache()
        self.email_sender = EmailService()
        self.sms_sender = SmsService()
        self.payment_gateway = PaymentGateway()
        self.inventory_system = InventorySystem()
        self.accounting_system = AccountingSystem()

    def create_order(self, order_data):
        # 订单创建牵涉 7 个系统的调用
        # 没有事务边界,任何一步失败都不知道回滚到哪里
        # 每个系统有自己的 error handling 逻辑
        # 每次加新需求要在 OrderService 里加 if-else

        self.db.save(order_data)
        self.inventory_system.reserve(order_data['items'])
        self.payment_gateway.charge(order_data['payment'])
        self.accounting_system.record(order_data)
        self.email_sender.send_confirmation(order_data['customer_email'])
        self.sms_sender.send_sms(order_data['customer_phone'], "Order confirmed")
        return order_data

# 问题:
# 1. OrderService 知道太多,违反单一职责原则
# 2. 系统间耦合严重,改一个会影响其他
# 3. 无法独立测试每个系统
# 4. 任何改动都是高风险的

架构债务的特点是改不动,测不了,推倒重来成本高

类型三:测试债务

测试债务是最容易被忽视的类型。

# 测试债务的典型例子:遗留代码的脆弱测试
def test_calculate_price_vip_order():
    """这个测试测的是实现细节,不是业务逻辑"""
    order = MagicMock()
    order.total = 1500

    customer = MagicMock()
    customer.type = 'vip'
    customer.age = 65
    customer.registered_year = 2019

    result = calculate_price(order, customer)

    # 硬编码的期望值,没有业务意义
    assert result == 1125.0  # 这个数字怎么算出来的?

    # 问题:
    # 1. 测试依赖实现细节,重构后测试就废
    # 2. 业务规则变化(折扣调整)要改很多测试
    # 3. 无法表达"真实业务场景"
    # 4. 没有测试边界条件(负数、空订单等)

测试债务的特点是测试存在,但价值有限,甚至阻碍重构

类型四:文档债务

# 文档债务的典型例子:README 跟代码对不上

## 用户认证流程

> 上次更新:2021-03
> 最近更新代码:2023-08

1. 用户提交表单
2. 后端验证密码
3. 返回 JWT token
4. 前端存储 token

---

实际代码流程(2023-08 更新后):
1. 用户提交表单
2. 后端调用 SSO 服务(已移除密码验证)
3. 返回 OAuth token(已移除 JWT)
4. 前端存储在 httpOnly cookie(已变更存储方式)

文档债务的特点是文档和代码渐行渐远,最后文档变成误导

Tech Debt 的根源:人和组织的决策

回到本文的核心观点:tech debt 不是 AI 带来的,是人和组织的决策带来的。

根源一:时间压力

这是 tech debt 最常见的来源。

我经历过一个典型场景:

产品经理:"这个功能周五要上线,来得及吗?"

开发:"来不及,有两个方案:一个快但有 tech debt,一个慢但干净。"

产品经理:"先上快的,tech debt 以后再还。"

实际情况:周五上线后,下周产品经理解散了,那个"以后再还"再也没来。

这不是 AI 的问题,这是业务决策优先级的问题

时间压力的本质是:短期交付压力 > 长期代码质量。

# 典型的时间压力决策
def sprint_planning(stories, velocity):
    """时间压力决策的数学"""
    story_points = sum(s['points'] for s in stories)
    capacity = velocity * 0.8  # 留 20% buffer 是常识

    if story_points > capacity:
        # 决策时刻:砍需求还是欠 tech debt?
        # 实际情况:砍需求阻力大,欠 tech debt 阻力小
        # 因为"我们先上线,功能都有了,以后再优化"
        return stories  # 一个都不砍,直接欠债

    return stories

根源二:知识断层

Tech debt 的另一个重要来源是知识没有传递

# 知识断层的典型例子:三年前写的代码,没人敢动
class OldPaymentProcessor:
    """
    警告:这段代码是 2019 年写的,
    @zhangsan 说是支付逻辑,但没人知道为什么这样写
    2019-2022 年期间有 3 个人试图重构,都失败了
    当前状态:能跑,但不理解
    """

    def process(self, payment_data):
        # 为什么要转成字符串再转回来?
        # 为什么要 sleep 3 秒?
        # 为什么这里有个 try except pass?
        try:
            result = self._legacy_api_call(
                str(payment_data).encode('gbk')  # GBK...认真的吗?
            )
            time.sleep(3)  # 3 秒延迟是为什么?
        except:
            pass  # 吞掉所有异常是故意的吗?

        return self._parse_result(result)

知识断层的本质是:代码的上下文(为什么这样做)随着时间丢失,而记录上下文的成本高于写代码本身

根源三:技术选型失误

# 技术选型失误的典型例子:选了"最先进"的技术栈
# 2019 年:
from exciting_new_framework import GreatFeature  # 当时最火的新框架

# 2021 年:GreatFeature 停止维护
# 2023 年:安全漏洞,无人修复
# 2026 年:升级到新框架需要重写 80% 的代码

# 现在的状态:
# 1. 框架过时,没人了解
# 2. 招聘困难,市场上没有人才
# 3. 升级成本等于重写
# 4. 但业务已经重度依赖,改不动

技术选型失误的本质是:技术决策受到当时认知和潮流的影响,而技术生态变化快于人的认知更新

根源四:组织结构问题

Tech debt 有时候是组织结构导致的,不是技术问题。

康威定律:系统设计反映了组织的沟通结构

# 组织结构导致的 tech debt
"""
团队 A(负责用户模块)和团队 B(负责支付模块)
两个团队独立开发,通过 API 集成

问题:
1. 用户地址校验逻辑在用户模块,支付模块需要地址验证时调用用户模块 API
2. 但两个模块有独立的数据模型,地址数据结构不一致
3. 每次"用户修改地址"要同时更新两个地方
4. API 调用的额外延迟和错误处理增加了复杂度

根本原因:
团队边界和业务边界不一致。
如果"地址"是一个独立的领域实体,应该是一个团队负责。
但组织结构决定了它在两个地方被重复定义。
"""

# Tech debt 的来源不是代码,是组织的边界划分

AI 在 Tech Debt 中的角色

说了这么多 tech debt 的根源,再来看 AI。

AI 做了什么?

AI coding assistant 确实产生了一些 tech debt:

  1. 生成代码的质量不稳定:有的代码好,有的代码差,差异很大
  2. 生成代码跟现有代码风格不一致:需要大量 review 和调整
  3. 容易生成"看起来对"但运行不了的代码:语法正确但逻辑错误
  4. 生成过度复杂的解决方案:一个简单的需求可能被扩展成复杂的系统

AI 没做什么?

但 AI 没有做的是:

  1. 没有强制你上 Friday deadline:时间压力是人的决策
  2. 没有阻止你写测试:测试债务是团队的决策
  3. 没有替你做技术选型:选错技术栈是架构师的决策
  4. 没有让你的知识断层:知识丢失是组织管理的决策

Tech debt 的根源是人的决策,不是工具的选择。AI 只是放大了已有的问题,而不是创造了新问题。

一个真实案例

我见过一个真实的例子:

某团队从 2022 年开始全面使用 AI coding assistant。两年后,他们有大量的 tech debt。

技术负责人说:"都怪 AI,生成的代码质量太差。"

我让他们做了个分析:

  • 代码审查通过率:35%
  • 重构次数:平均每个功能 2.3 次
  • Bug 率:比之前高了 40%

听起来确实是 AI 的问题。

但深入看:
- 35% 的审查通过率——审查标准是什么?团队有没有建立 AI 生成代码的审查规范?
- 2.3 次重构——第一次提交的是 AI 生成的,还是人 Review 之后才提交的?
- Bug 率高 40%——跟 AI 生成相关的是多少,跟人的判断失误相关的又是多少?

分析结果是:
- 80% 的 tech debt 来自:产品 deadline 压力下的"先上线再说"决策
- 15% 来自:团队没有建立 AI 代码的审查流程
- 只有 5% 来自:AI 生成的代码本身有问题

把 80% 的问题归咎于 AI,是最方便的甩锅方式,但不是诚实的分析。

怎么系统性地管理 Tech Debt

既然 tech debt 的根源是人和组织的决策,那解决 tech debt 也需要从这两个层面入手。

第一层:看得见 Tech Debt

大多数团队的问题是:不知道自己的 tech debt 有多少。

def measure_tech_debt(project_path):
    """
    量化 tech debt 的基础指标
    """

    metrics = {
        # 代码债务指标
        "code_duplication_rate": calculate_duplication(project_path),
        "complexity_per_file": calculate_complexity(project_path),
        "comment_to_code_ratio": calculate_comment_ratio(project_path),
        "naming_violations": count_naming_issues(project_path),

        # 架构债务指标
        "cyclic_dependencies": count_cycles(project_path),
        "module_coupling": calculate_coupling(project_path),
        "architectural_violations": count_arch_violations(project_path),

        # 测试债务指标
        "test_coverage": calculate_coverage(project_path),
        "flaky_tests_rate": calculate_flaky_rate(project_path),
        "test_maintenance_burden": calculate_test_maintenance(project_path),

        # 文档债务指标
        "outdated_docs_rate": calculate_doc_currency(project_path),
        "api_documented_rate": calculate_api_docs(project_path),
    }

    return metrics

# 关键洞察:如果你不测量,就无法管理
# 很多团队不知道 tech debt 有多少,因为从来没有量化过

第二层:建立 Tech Debt 可见机制

def create_tech_debt_dashboard(metrics):
    """
    Tech Debt 可视化仪表板
    """

    # 红绿灯系统:让 tech debt 状态一目了然
    dashboard = {
        "code_debt": {
            "status": "red" if metrics["code_duplication_rate"] > 0.15 else "yellow",
            "score": metrics["code_duplication_rate"],
            "trend": "increasing",  # 债务在增长还是减少
            "top_5_files": get_worst_5_files(metrics),
        },
        "architecture_debt": {
            "status": "yellow",
            "critical_violations": metrics["cyclic_dependencies"],
            "refactoring_units": count_refactoring_units(metrics),
        },
        "test_debt": {
            "status": "red",
            "coverage": f"{metrics['test_coverage']}%",
            "flaky_tests": metrics["flaky_tests_rate"],
        }
    }

    return dashboard

# 关键洞察:让 tech debt 可见,让它成为优先级讨论的一部分
# 不要让 tech debt 只存在于开发者的脑海里

第三层:把 Tech Debt 纳入产品决策

这是最重要但最难的一步。

def feature_debt_tradeoff(user_story, tech_debt_impact):
    """
    把 tech debt 纳入产品决策
    """

    """
    当产品团队决定做功能 X 时,技术团队应该提供:
    1. 功能价值:做 vs 不做的区别
    2. 开发成本:这个功能需要多少时间
    3. Tech Debt 影响:这个功能会加重多少 tech debt
    4. Future Cost:现在不还债,以后还要额外多少成本

    示例输出:
    功能:实时通知系统
    开发成本:2 周
    Tech Debt 影响:中等(需要引入新的消息队列服务)
    Future Cost:如果 6 个月后再做,同样功能需要 4 周(因为要整合已有代码)
    Net Value:现在做 vs 以后做 = 节省 2 周 + 降低 6 个月的技术风险
    """

    # 技术负责人应该把这种分析带到产品规划会议上
    # 不要让产品团队以为 tech debt 是"免费的"

第四层:建立还债机制

def allocate_debt_repayment(sprint_capacity, tech_debt_dashboard):
    """
    在 sprint 中分配还债时间
    """

    """
    经验法则:20% 规则
    每个 sprint 预留 20% 的时间用于还 tech debt

    原因:
    - 如果不预留,tech debt 会无限积累
    - 20% 是足够可观的还债时间,又不影响交付
    - 持续的小规模还债比一次性大规模重构风险低

    分配优先级:
    1. 高流量、高变更频率的代码优先
    2. 影响系统稳定性的债务优先
    3. 拖累开发效率的债务优先
    """

    total_capacity = sprint_capacity
    feature_work = total_capacity * 0.8
    debt_repayment = total_capacity * 0.2

    # 债务还清优先级排序
    debt_priority = prioritize_debt(tech_debt_dashboard)

    return {
        "feature_capacity": feature_work,
        "debt_capacity": debt_repayment,
        "top_debt_to_address": debt_priority[:3]  # 这个 sprint 专注解决前 3 个
    }

Tech Debt 和 AI 的正确关系

说了这么多,最后梳理一下 tech debt 和 AI 的关系。

AI 加剧了已有的问题

如果一个团队:
- 有时间压力文化(tech debt 的根源一)
- 有知识断层问题(tech debt 的根源二)
- 技术选型有问题(tech debt 的根源三)
- 组织结构有摩擦(tech debt 的根源四)

那么 AI 会加剧这些问题,因为 AI 加速了代码生产,但没有解决上述根源。

AI 无法解决 tech debt 的根源

Tech debt 的根源是:
- 时间压力 → AI 无法改变组织的决策优先级
- 知识断层 → AI 可以生成代码,但无法传递"为什么这样写"的上下文
- 技术选型 → AI 可以生成代码,但无法替你做技术选型
- 组织结构 → AI 无法改变团队边界和汇报关系

AI 能在哪里帮上忙

AI 确实可以在某些环节帮上忙:

# AI 辅助的 tech debt 管理
def ai_assisted_debt_management():

    # 1. Tech Debt 检测
    # AI 可以分析代码库,识别潜在的 tech debt
    def detect_debt_patterns(codebase):
        """AI 检测常见的 tech debt 模式"""
        patterns = [
            "magic_numbers",  # 硬编码数字
            "copy_paste_code",  # 重复代码
            "complex_conditionals",  # 复杂条件
            "god_classes",  # 上帝类
            "circular_dependencies",  # 循环依赖
        ]
        return ai_analyze(codebase, patterns)

    # 2. 重构建议
    # AI 可以提供重构建议和代码示例
    def suggest_refactoring(debt_item):
        """AI 生成重构建议"""
        return ai_generate_refactor_plan(debt_item)

    # 3. 测试生成
    # AI 可以帮助补齐测试债务
    def generate_missing_tests(code_coverage):
        """AI 补全测试覆盖"""
        uncovered = find_uncovered_code(code_coverage)
        return ai_generate_tests(uncovered)

    # 4. 文档同步
    # AI 可以帮助更新过时的文档
    def sync_documentation(code_change, docs):
        """AI 同步文档和代码"""
        return ai_update_docs(code_change, docs)

但 AI 的帮助仅限于执行层面的辅助。策略、优先级、决策,还是需要人来做。

我的观点

写这篇文章,我的核心观点是:

Tech debt 是一个组织问题,不是工具问题。把 tech debt 归咎于 AI,是最方便的甩锅方式,但不是诚实的分析。

真正解决 tech debt,需要:
1. 诚实地量化它:让它可见,不要假装它不存在
2. 诚实地归因:搞清楚 tech debt 来自哪里,是时间压力?知识断层?技术选型?还是组织结构?
3. 诚实地纳入决策:把 tech debt 的成本带到产品规划中,让业务决策者知道 trade-off
4. 诚实地还债:预留时间,持续还债,而不是等到系统无法维护再推倒重来

AI 可以帮上一些忙,但 AI 不是答案。答案在于人和组织的决策。


如果你同意或不同意这个观点,欢迎交流。我的经验可能跟你的不一样,但 tech debt 不是 AI 才有的新问题,这个判断我觉得值得认真讨论。