
架构治理和技术债务管理这两个词听起来很“管理层”但实际上一旦团队过了三十个人、系统被拆成几十个模块它们就会直接决定你每天写代码的效率。我见过不少系统在早期依赖很少、改动成本很低的时候一路狂奔等某个大项目排期进来才发现一次变更能牵出十几处隐藏依赖一张慢查询能拖垮整条核心链路这时候再谈技术债代价通常已经翻了倍。这篇内容算是把我的实操经验做一次沉淀架构治理到底治理什么、技术债务怎么量化成可排期的活儿、演进机制怎么落地到 CI 和评审流程里以及推进过程中那些绕不开的阻力。适合正在带业务系统演进的架构师和技术负责人也适合每天被历史代码折磨、想找突破口的核心开发。文章里尽量少讲虚的概念基本都是可以直接拿回去用的框架、模板和避坑建议。1. 架构治理到底治什么先想清楚边界再动手1.1 治理不是管人是给系统立一套“游戏规则”先厘清一个关键问题架构治理和“管人”“管项目”是两回事。它不是去审批谁该干什么而是给整个系统的技术演进建立一套边界规则并持续验证边界有没有被遵守。你可以把系统想象成一座城市架构师是城市规划师治理就是交通规则和红绿灯。没有规则的城市车想开哪儿就开哪儿短期看着自由一旦车多起来就是全城瘫痪反过来如果每条路都限速10公里、每个路口都要盖章通行城市也运转不起来。治理的目标不是限制变化而是让变化变得可预期、低成本这才是它能长期存在的原因。在实际团队里架构治理和技术管理经常被混着用。以我的理解技术管理解决的是“这个版本怎么按期交付”架构治理解决的是“系统能不能一直以合理的成本演化出下一步”。前者是战术后者是战略。如果战略不落地就算每个迭代都赶上进度系统每一次“赶时间先绕过规则”的妥协也都会成为下一次改动的阻力。治理的产出不是一堆流程文档而是三样东西被全员认同的边界、自动化可验证的检查、结构化的决策记录。这三样缺一样治理都会慢慢变成墙上的装饰画。1.2 治理的五个核心对象我习惯把架构治理拆成五个核心对象来抓分别是原则、标准、流程、指标、归属。这五个词听起来抽象但落到日常工作里都是很具体的东西。原则是最高层的约定比如“核心链路的跨模块调用必须走接口”“新增中间件必须通过架构评审”“不允许方向依赖反向”。原则不用多五到八条已经很多重点是能被所有人记住。标准把原则翻译成可执行的东西技术栈清单、接口版本策略、日志格式、错误码规范。标准不能靠记忆要落到脚手架和模板里新服务一初始化就自动符合标准。流程解决“标准没覆盖的情况怎么办”。最常见的就是架构决策评审涉及跨模块、跨团队、引入新组件、改变数据流方向的变更走轻量评审其他变更按标准自动放行。指标提供反馈信号依赖健康度、循环依赖数、模块违规数、变更失败率。指标不在多而在于能触发行动否则只是一串数字。归属解决“最终谁对某一块架构负责”。模块边界没有 owner治理就没有抓手。后面我会专门讲很多债务长期无人认领根因往往是归属缺失。拿一个我亲身经历的例子支付系统早期没有原则约束订单模块直接调用账户模块的内部 DAO账务团队毫不知情。等账户模块为了合规要拆库时一查依赖图下游直接依赖账务数据库表的服务有 14 个。这就是典型的“规则缺失期埋雷演进期爆雷”。事后我们补的第一个治理动作不是砍掉所有直接依赖而是先划清楚“谁拥有账务数据边界”再把依赖逐步迁移到接口上。没有归属任何治理动作都会陷入踢皮球。1.3 治理粒度的取舍太松放任太紧窒息这套规则如果粗细把握不好一定会从“救系统”变成“拖后腿”。我见过最快的治理崩塌是从一个 20 人的“架构治理委员会”开始的每个版本都要评每份设计文档都要看结果评审会排队到两周之后开发团队忍无可忍绕过流程直接上线治理从此变成一纸空文。真正的做法是对“边界”严格对“边界内部”放手。比如接口契约必须兼容、不允许出现循环依赖、不允许反向依赖这几条红线零容忍至于模块内部用什么设计模式、类怎么命名尽量交给业务团队自己定。治理对象红线级别验证方式例子模块边界零容忍CI 依赖检查domain 层不依赖 controller数据契约零容忍契约测试API 字段只能新增不能删改技术栈选型重点评审架构评审 ADR引入消息队列必须过会实现细节不治理团队自定类命名、设计模式另一个办法是按风险分级处理变化。不是所有变化都需要评审常规改动走自动检查加公示涉及多团队的边界变化才走 30 分钟评审会涉及全系统技术栈迁移这类大决策才上升到月度架构评审。治理粒度对了规则才能长期活下来。记住一句话治理的产出不是审批意见而是让团队在边界内可以放心大胆干活的安全感。2. 技术债务怎么管先把隐性成本“摆到台面上”2.1 债务也分好坏先给技术债分门别类技术债这个词被用滥了以至于很多人一听到“还债”就头疼。其实不是所有债都是坏事有的债是主动选择的比如为了赶一个重要上线暂时放弃了某个模块的重构这种属于“有意识的债”只要记录清楚、排期偿还并不致命。真正可怕的是“无意识的债”代码写完就忘边界坏了没人知道依赖老化了没人升结果每天都在为当初的无意识选择付利息。债务类型典型表现主要危害偿还方式设计债领域模型混乱、模块职责重叠改动成本扩散模块梳理 分层治理代码债重复代码、圈复杂度高修 bug 容易引入新 bug重构 消除重复测试债覆盖率低、用例不稳定回归风险大补测试 稳定测试环境基础设施债依赖版本老化、部署脚本复杂交付慢、易故障升级依赖 自动化流程债没有门禁、没有评审恶化的入口关卡全部敞开补检查卡点我个人的经验是团队首先要把“有意识的债”和“无意识的债”分开管理。有意识的债进台账排优先级按计划还无意识的债需要立刻止损比如把某个模块的循环依赖列为红线存量可以限期消化但增量一个都不许有。分完类债务才谈得上管理否则只会变成一笔糊涂账。2.2 量化方法一用代码指标找趋势量化债务的第一步不是追求一个“完美的分数”而是建立基线、看趋势。常用指标包括可维护性指数Maintainability Index、圈复杂度、模块平均扇出依赖数、循环依赖计数、重复代码率、单测覆盖率等。这些指标单独看意义都不大但放一起看趋势就很有价值。比如新代码的圈复杂度是不是在逐月上升新模块的依赖扇出是不是从平均 20 涨到了 40这些都是系统走向腐化的早期信号。我当时给一个交易链路定的规则很简单新增代码圈复杂度超过 12 不让合并存量代码每个季度整体降低 10%每两周看一次依赖图任何新出现的环必须当天解决。这套标准不追求极致执行起来也容易因为门禁是自动的开发同学不用记规则。必须提醒一句指标可以被刷。比如有人会把一个大类拆成多个互相调用的类来绕过复杂度检查或者把循环依赖包在一个“万能工具类”里。所以指标要配人工抽查再配运行时的真实调用链数据单靠静态指标一定会被钻空子。2.3 量化方法二从交付事实看代价代码指标是“体检报告”交付事实是“病历”一个人平时体检数据可能还行但反复生病就说明真实健康状况不理想。对模块而言交付事实就是变更失败率、缺陷密度、从提交到发布的上线时长、功能交付速度。这些指标最能打动业务方因为它直接对应钱和时间。我做过一次对比统计A 模块因为累积了多年技术债发布后热修复频率是平均值的 4 倍B 模块经过重构同样的功能变更只需半天就能走完从提交流到上线的全流程。同样一个需求在两个模块里交付速度能差出 3 倍以上。这就是技术债最直观的样子。所以我建议每个季度给核心模块出一份这样的对比谁的变更失败率高谁的平均修复时长大谁的上线前置时间在拉长。不需要复杂算法一张 Excel 透视表就能让问题浮出水面。2.4 一本能“催账”的技术债台账债务要可管理就必须有一本台账。但很多团队的技术债台账建了三个月就躺尸原因是它只是一个“记录本”没有“账”的属性。真正的台账应该包括每一项债务的成因、影响、利息和还款计划让人一看就知道这笔债现在值多少、不还会怎样。债项编号位置债务描述影响预计偿还成本每月“利息”决策TD-2024-013订单中心查询服务订单查询使用全表扫描核心链路 P99 延迟超 500ms2 人日每周告警 3 次下迭代修复TD-2024-021账户模块反向依赖未解耦拆库被阻塞5 人日每季度阻塞 1 个需求排入下季度台账里最关键的是“利息”这一列。因为很多债看起来不紧急但利息一直在累积。只要把“每周告警 3 次、每次处理半小时”算成工时再乘以影响周期很容易说服团队和老板优先处理。另外台账一定要有 owner每一项债务都对应一个明确的负责人否则到了季度末债务清单只会在邮箱里吃灰。2.5 把债务翻译成业务听得懂的语言讲技术债如果只对开发团队说那还好办但如果要向上争取资源就必须翻译成业务语言。不要说“这段代码很烂需要重构”要说“订单模块的变更失败率已经从 2% 升到 6%按现在的规模估算每个季度因为返工和线上问题损失的交付产能相当于一个小组两周的完整工作量”。不要说“架构腐化”要说“未来半年我们有三条业务线要依赖这个模块扩展如果不做结构调整每条线的交付周期都会从两周被拉到一个月”。这套翻译不是话术包装而是债务真正对应的成本。我在推进公司级治理时最有效的汇报材料就是一张图左边是各个模块的缺陷密度和变更失败率右边是它们对应的业务目标和季度交付承诺。信息一摆出来优先级自然就清楚了技术团队不再需要靠“卖惨”抢资源。3. 让架构演进成为日常机制从评审到自动化守门员3.1 用 ADR 留住每次决策的“为什么”架构演进和修 bug 最大的区别是架构决策会活很多年但决策的上下文通常只存在于两三个人的脑子里。等人一走后人就只看到一个奇怪的接口、一个特殊的缓存策略没人知道为什么。所以我强烈建议用 ADRArchitecture Decision Record架构决策记录来管理每一次决策而且一定要在代码仓库里留档跟随代码一起演进。一份合格的 ADR 不需要很长五个部分就够标题、状态、背景、决策、后果。背景要写清楚当时的约束和可选方案后果要写清楚这个决策带来的好处和代价。我见过一个团队的 ADR 模板其实是很好的# ADR-023 订单详情页采用本地缓存优先 - 状态已采纳2024-05-16 - 背景订单详情读 QPS 3 万热点商品占 70%Redis 往返延迟约 1-2ms整体响应不稳定 - 决策引入 Caffeine 本地缓存作为一级缓存Redis 作为二级缓存未命中走 DB - 后果P99 从 45ms 降到 15ms本地缓存带来多副本一致性问题通过版本号加短过期兜底 - 替代方案全部走 Redis延迟不可控、只加 DB 索引收益有限ADR 的关键不在格式而在使用习惯决策评审通过后由提案人在当天把 ADR 提交到代码库作为一次 PR。后续代码评审时如果改动涉及 ADR 覆盖的边界CI 机器人可以自动提醒“这块变更需要关联 ADR-023”。只有这样ADR 才会被当成代码的一部分来维护而不是归档到维基里慢慢腐烂。3.2 架构评审会要克制才能长期开下去架构评审会是治理最重要的“人肉环节”也是最容易被开垮的环节。开垮有两种方式一种是流于形式大家到了会议室才开始看材料评不出有效意见另一种是变成权力场评审委员会成员太多、立场各异一个技术方案被来回挑战三轮决策始终悬而不决。我的原则是评审会人员要精简范围要聚焦。建议五到七人包括被评审模块的 owner、边界上下游代表、平台基础能力负责人再加一个记录员。每次会议只过一到两个决策决策人必须在场不能只派代表。所有评审材料提前 48 小时发出会上默认大家都已经读过开场直接用 5 分钟过背景剩下时间全部用来讨论真正的风险点。常规变更最迟三个工作日必须给出结论采纳、否决或挂起。挂起必须写明需要补什么信息不能无限期悬着。还有一个容易忽略的点评审会需要留一条“紧急通道”。如果线上事故或者关键项目必须马上做一个跨边界决策可以先执行后评审24 小时内补 ADR 和评审记录。没有这条通道治理就会被视为“挡路的流程”团队会想办法绕过你一旦绕过成为习惯正式流程也就名存实亡了。3.3 把架构规则写进 CI用测试当守门员治理如果全靠人盯一定盯不过来尤其当系统已经有几十个模块的时候。我推动治理时最重视的一件事就是把架构规则变成自动化的适配函数Fitness Function让测试来当守门员。所谓适配函数简单来说就是“用自动化检查验证架构是否偏离既定方向”的测试它跑在 CI 里违反就红通过就绿。常用工具在不同语言栈里都有对应实现Java 体系用 ArchUnit.NET 用 NetArchTest前端可以用 dependency-cruiser 配合 ESLint 规则。下面这个 Java 例子我用了很多年效果很稳定核心逻辑就是两条domain 层不能依赖 web 层系统不允许出现循环依赖。AnalyzeClasses(packages com.example) public class ArchitectureRulesTest { Test void domainShouldNotDependOnWebLayer() { classes().that().resideInAPackage(..domain..) .should().notDependOnClassesThat() .resideInAnyPackage(..web..) .check(new ClassFileImporter().importPackages(com.example)); } Test void noCyclicDependencies() { slices().matching(com.example.(*)..) .should().beFreeOfCycles() .check(new ClassFileImporter().importPackages(com.example)); } }这类测试代码看起来简单但价值非常大。它相当于把“架构红线”变成了编译器级别的约束开发同学在本地就能跑不用等人来审批。不过我要提醒一个坑规则千万不能一上来就上 50 条。我见过一个团队在 CI 里塞了上百条架构规则结果每天有几十次误报开发同学被噪音淹没后产生“狼来了”效应直接看都懒得看。正确做法是从三条最痛的红线开始确认误报率低于 5% 再逐步加。3.4 老系统演进绞杀者模式的三个阶段架构要健康发展始终绕不开老系统怎么改的问题。对真正大型的遗留系统我一般不建议“推倒重来”而是用绞杀者模式Strangler Fig Pattern逐步替换。核心思路是老系统不是一个需要一次性拆除的大楼而是在新草地上种一棵新树让老藤蔓慢慢被绞杀覆盖。具体到落地可以分成三个阶段。第一阶段是代码级隔离在旧的单体里划出边界包把不属于这个边界的外部依赖全部用接口挡住配上 ArchUnit 规则保证新代码不会穿墙。第二阶段是进程级隔离把划出来的模块变成独立部署单元共享数据库但拥有独立服务通过 API 对外通信这时候已经能独立发版和扩容。第三阶段是数据级隔离拆分数据库把表归属彻底理清改走事件或异步同步。三个阶段不能跳步。我见过团队急着拆库结果发现几十条 SQL 直接跨库 join数据同步方案也没想好上线当晚就回滚。所以每个阶段都有明确的完成标准第一阶段必须 0 新增违规第二阶段必须达到独立部署无回归第三阶段必须通过数据一致性演练。完成了再走下一步宁可慢一点也不要在迁移过程中把系统搞瘫。4. 推进治理的真实阻力与应对策略4.1 别让治理变成开发眼里的“官僚主义”几乎所有治理项目都会遇到同一个声音这不是给我们上枷锁吗这个问题一半是沟通问题一半是设计问题。沟通上要反复强调治理的目的不是“管控”而是“让开发在边界内自由奔跑”。设计上要区分“必须合规”和“建议优化”。比如不能把密钥提交到代码库、不能做不兼容的接口变更、不能引入未经评审的中间件这些是必须卡死的红线而圈复杂度是否偏高、某个模块是否需要拆分则只做提示不阻断合并。检查项类型处理方式泄露密钥强制阻断CI 直接 fail接口不兼容变更强制阻断契约测试 fail新增未审批中间件强制阻断架构检查 fail圈复杂度超标建议优化PR 评论提示不阻断模块疑似要拆分建议优化记录到债务台账这个区分非常关键。强制项少了治理没有约束力强制项多了开发每天被卡怨气会非常大。我的经验是强制项永远只保留“会引发线上事故或导致后续无法演进”的规则其余全部归为建议。治理者也要有姿态你不是裁判而是给团队修路的人。4.2 时间预算永远不够就把还债嵌进日常“我也想重构但业务排期太满了”这个理由听着耳熟吧。与其反复打鸡血不如在机制上把还债嵌进日常节奏。两种模式我都用过效果不错。第一种是“迭代容量预留”每个迭代固定预留 10% 到 20% 的产能给架构改进不允许被业务需求挤占花不完也要花完。第二种是“顺手还债”原则凡是改动过的模块必须顺手把附近的债还一部分至少不能新增债务。有个通俗说法叫“离开营地的法则”——离开时要比来时更干净。举个例子如果这次需求改到了订单查询那块代码顺手要做两件事一是把附近一段重复逻辑抽掉二是给这个接口补一条关键用例。每笔顺手还的债都不大但长期积累下来效果比一年一次“大重构”要扎实得多。大重构容易引发大回归还容易因为工期压力中途放弃小幅高频的还债反而安全。4.3 老系统的“不能动”魔咒怎么破老系统最大的问题不是代码差而是没人知道动了会出什么事。没有测试、没有文档、隐藏依赖又多所以每一次改动都像是在雷区里走路。破这个魔咒的办法是先给老系统装“安全网”再谈改造。安全网分三层第一层是可观测性把关键链路的日志、指标、trace 全部补齐至少要知道改坏了什么第二层是契约测试把老系统对外暴露的关键接口固化成契约任何人改动都不能破坏这些契约第三层是自动化回归哪怕没有单元测试也要先把冒烟测试和核心链路压测补上。安全网铺好之后再针对老系统做“微创手术”。比如选择一条新的业务需求在旧系统旁边搭一个新的替代模块把新逻辑全部进新模块新模块和旧模块通过适配层互通。业务验证通过后再把流量逐步从旧模块切到新模块最后删除旧代码。整个过程符合绞杀者模式的思路风险被控制在小范围内也更容易获得业务方支持。4.4 治理不能一年只做一张 PPT很多公司的架构治理最终沦为一年两次的架构评审会做一张大 PPT 汇报一下系统现状然后就没有然后了。这种“PPT 式治理”几乎没有任何作用因为问题不在汇报而在日常没有持续校正机制。真正有效的治理必须是一种稳定的节奏。我建议的节奏是每天自动化检查跑在 CI 里违规项自动进入队列每周架构师花半小时过一下 Top 债务项确认状态、更新优先级每月开一次架构演进评审会看指标变化、决策进展每季度做一次复盘对照季度目标回答三个问题系统交付速度变了吗变更失败率降了吗顶层的架构风险有没有减少治理只有在这种固定节奏里才会从一件事变成一种制度最终变成团队默认的做事方式。5. 常见问题与排查技巧实录5.1 架构评审会为什么总是拖成两周评审会拖期最常见的三个原因一是评审范围没有区分大小芝麻大的改动也要排队二是材料提前量不够评审人现场才翻文档三是没有明确的时限承诺。排查时先看会议的组织方式再看是否缺少紧急通道。建议这样改先定义“需要评审的变更清单”不在这份清单里的直接走公示制评审材料必须提前 48 小时发如果没有重大异议48 小时内给出结论紧急情况走 24 小时补审通道。这套规则实施后评审会拖期的问题基本可以消失九成以上。5.2 ADR 写了一大堆为什么没人看ADR 没人看的核心原因是它成了“事后补文档”而不是“决策过程中的一部分”。排查一下团队的 ADR 是不是都堆在一个没人访问的维基页面里如果是问题就很明显了。正确的做法是把 ADR 放在代码仓库的 /docs/adr 目录中和 PR 绑定代码评审时由机器人自动关联 ADR变更影响到的模块如果涉及已采纳决策提交信息里要求带上 ADR 编号。另外ADR 要有“过期回收”机制已被新决策取代的 ADR要立刻把状态从“已采纳”改成“已废弃”否则一堆过期的 ADR 比没有 ADR 更可怕因为它会让团队默认“写了也没人当真”。5.3 自动化守门员“狼来了”之后怎么修复误报太多开发同学就会选择关闭告警或者直接忽略红点。排查时先看两件事规则本身是否过于宽泛比如一个依赖检查规则覆盖了所有类但业务上有些包本来就该相互依赖再看是否存在“存量豁免单”。我的修复方案是把存量问题全部进豁免清单每条豁免记录必须绑定债务编号、owner 和过期时间新代码必须跑新规则存量问题到期未还则转为强制阻断。从两三条规则起步每周看一次误报率确认低于 5% 再逐渐放宽到更多规则。宁可少管几条也不能让团队养成“看到红点也无所谓”的习惯。5.4 技术债台账建了三个月为什么过期无人认领如果台账建了没人管首先要问的不是“怎么督促大家认领”而是“模块 ownership 是不是清楚的”。没有 owner 的模块债务永远是孤儿债。排查方式很简单把系统里的每个核心模块列出来问一圈团队“这个模块能不能找到唯一负责人”凡是模糊不清的本身就是最高优先级的结构风险。处理方式分两步第一步先把归属明确到人和小组哪怕只是名义上的第二步才谈认领债务。从经验看只要 owner 明确每个季度让 owner 在复盘会上说一句“我负责的模块这季度还了哪些债”台账的活跃度会明显改善。5.5 轻量起步模板3个月跑通最小治理循环如果你所在的团队治理基础几乎是零别急着追求全面。我建议按以下时间线推进用三个月跑通一个最小治理循环时间动作产出第 1 周建立 ADR 目录接入依赖分析工具决策有地方记录依赖图能自动生成第 2 周挑出 3 条最痛的架构红线写成 CI 测试红线开始被自动守护第 3-4 周建立技术债台账给 Top 5 债务定 owner债务从吐槽变成任务第 2 个月跑通第一次月度架构评审会形成决策、评审、记录闭环第 3 个月把核心指标接入团队周报治理进入日常节奏每一步都不大但合在一起就是一个完整的治理闭环决策有记录边界有检查债务有台账指标有反馈。三个月后你回头再看系统不一定变得多“干净”但“谁能改哪里、为什么这么定、哪里最痛、怎么优先还债”这些问题团队里每个人都说得清楚这比任何理想中的完美架构都更值钱。最后说一点我这些年的体会。真正让架构保持长期健康的从来不是一套复杂的治理体系而是两个简单的习惯一是每次架构决策都留下可追溯的“为什么”二是每个被划出来的边界都有自动化验证兜底。把这两件事坚持做上两三个季度你会看到一种变化系统不再是一堆靠“前人经验”才能理解的代码而是一张有边界、有方向、随时敢改的地图。技术债不会消失但会变成一张可排期、可跟踪、可偿还的清单而不是半夜被叫起来处理事故的惊吓。如果你正在为架构失控发愁别急着搞大而全的治理平台先从一个 ADR、一条 CI 规则、一本十行的债务台账开始三到六个月后回来看效果会比任何宏伟规划都实在。