做架构师这几年,我复盘过不少项目。一开始我以为复盘就是把时间线捋一遍,把每个模块做了什么、用了什么技术写清楚,最后总结几条经验教训。后来我发现这根本不是复盘,至少不是架构师视角下的项目复盘——架构师复盘的核心对象不是“代码”,也不是“功能”,而是“决策”:当时为什么选了这条路,那条没选的路到底输在哪里,现在的证据是否支持当初的判断,以及下一个项目里这个决策能不能复用。这篇文章我就把自己沉淀下来的复盘方法、模板和踩过的坑一起写出来,尤其适合正在带项目、做技术选型、或者准备系统化梳理过往项目的工程师参考。
1. 复盘不等于写总结:架构师复盘到底在盘什么
1.1 普通复盘和架构复盘的三个根本差异
团队里最常见的复盘,是项目经理拉上所有人,对着迭代排期一页一页过:这个功能延期了,那个需求变更了,测试发现了多少个Bug,客户提了什么新要求。这种复盘的产出是一份会议纪要,核心是“对齐事实”和“追责改进”,它解决的是“事情有没有按计划发生”的问题。
架构师视角下的复盘不在这个层面。它关心的是“事情为什么变成现在这样”:当初定下的架构方案在面对真实流量时表现如何,技术选型时做的假设是否依然成立,系统的哪些部分已经悄悄成为演进路上的阻碍。把这两类复盘的差异拆开看,至少有三个根本区别。
第一,记录对象不同。普通复盘记录“发生了什么”,架构复盘记录“为什么选了这个方案”。同样是系统慢,普通复盘会写“接口A耗时从50ms涨到300ms,已优化索引,恢复至80ms”——这是结果。架构复盘要写的是:当初设计这个模块时为什么采用了现有结构,这个结构在什么规模下会失效,优化到底解决了表层的慢查询,还是修正了某个架构层面的错误假设。
第二,时间尺度不同。一次迭代的复盘只能覆盖几周内的变化,而架构决策的影响往往要以半年、一年甚至更长时间为周期才能显现。我今天回头去看两年前做的某个存储方案,如果只盯着当时的版本,会觉得自己做得挺对;但如果把两年间的数据量增长、查询模式变化、团队人员流动都放进去,才能看到那个方案的真实代价。
第三,产出物不同。普通复盘的产出是“修正行动方案”,比如补测试、加监控、修Bug。架构复盘的产出是“决策记录”和“演进输入”,它要回答的是:这个系统接下来往哪走,哪笔技术债该还、哪笔不该还,当初被否决的方案现在是否值得重新考虑。
1.2 复盘的信息四要素:需求、约束、决策、结果
做架构复盘久了,我总结出一个必须检查的信息框架,缺一个维度,复盘就不完整。
- 需求:业务到底要什么,用户场景是什么,未来三到六个月有没有明确的增长预期。
- 约束:当时有哪些硬性限制,包括时间、预算、团队技术栈、运维能力、合规要求。
- 决策:在这个需求和约束之下,最终选择了什么方案,备选方案有哪些。
- 结果:系统上线后实际表现如何,哪些预期被验证,哪些预期落空,出现了哪些意外。
很多人复盘时只记录“决策”和“结果”,把“需求”和“约束”丢得一干二净。这是最可惜的事。因为脱离需求和约束的决策根本没有复制性:你只看到别人当时选了某个中间件、某个分片策略,却不知道他为什么没选另一个,等你自己遇到类似场景时完全没法参考。
反过来讲,当我把“约束”写回复盘文档时,很多看似不合理的决策都变得合理了。比如有一个项目当时选了理论上更复杂的方案,周围人都觉得没必要,但复盘时翻出约束——团队里只有一个人熟悉该技术线,线上系统不允许超过两小时的切割窗口——这个决策立刻就有了清晰的逻辑。复盘的第一个任务,就是把这种决策逻辑找回来。
2. 从“选型结果”到“选择过程”:把决策链完整归档
2.1 为什么只记录最终方案是不够的
我见过太多项目的架构文档,打开一看只有一个方案,写得无比详细,仿佛这个方案是显而易见、没有其他可能性的。但这几乎一定是假象。真实世界里的架构决策,几乎没有哪次是在一个方案里定下来的,至少会有另一条被否决的路。
只记录最终方案有两个坏处。第一,后人无法判断这个方案的前置条件。架构方案的生命力在于它的前提假设,一旦前提变了,方案本身就必须被重新审视。比如你当时选了一张大宽表,因为团队只有三个人、QPS不高;等到业务涨十倍,这个方案不合理了,可文档里没有写“低QPS”这个前提,接手的人只能看着这张宽表发愁,不知道怎么改,也不敢改。
第二,被否决的方案其实非常宝贵。很多被否决的方案并不是“错误方案”,只是在当时约束下不合适。它们代表了另一条可行的演进路径。两年后约束变化了,当初被否决的方案很可能就是现在的正确解。如果文档里没有记录这条路径,团队就得重新做一遍分析,浪费大量时间。
2.2 一个可落地的决策记录模板
我的做法是给每个重要决策写一张轻量级的ADR(Architecture Decision Record,架构决策记录),不追求标准化的严谨,但字段必须固定。下面这个模板我用了很多年,基本够用,也不重:
【决策编号】ADR-00X 【日期】YYYY-MM-DD 【主题】一句话说明这个决策在解决什么 【状态】已接受 / 已废弃 / 被取代 / 待复审 【背景】 业务和技术现状是什么,为什么要做这个决策。 【约束】 时间、成本、团队能力、技术栈、合规等硬性限制。 【备选方案】 方案A:内容 + 优点 + 缺点 方案B:内容 + 优点 + 缺点 【决策】 最终选择了哪个方案。 【决策理由】 逐一说明为什么选择该方案、为什么否决其他方案。 【预期后果】 正面影响 / 负面影响 / 可能的债务。 【复审触发条件】 当出现什么情况时,本决策必须重新评估。最后一项“复审触发条件”是我后来加进去的,强烈建议每个人都加上。它能把“这个决策是否仍然成立”变成一种主动检查,而不是靠碰巧有人在文档里翻到。比如某个分库方案当初的约束是“单表数据量不超过5000万”,那触发条件就是“任一分表达到此规模时,重新评估路由策略”。有了这一条,技术债不会被无限期掩盖。
2.3 记录时机:决策当下和阶段收尾各做一次
有人会说,项目那么忙,哪有时间每个决策都写ADR?我的经验是,真正的决策数量远比你想象得少,一两个月的大项目,值得写成ADR的不会超过十个。大部分时间我们只是在执行既定方案,并不需要决策记录。
需要写ADR的场合只有四类:一是技术选型,比如引入新的中间件、数据库、框架;二是架构调整,比如拆服务、加缓存、改数据模型;三是影响外部接口的变更,比如改协议、改字段、变更依赖关系;四是耗费超过一天时间的方案争论。这四类事件一旦发生,就应该在讨论当天或次日写掉。共同特征是这个团队会长期使用该方案,并且会有不少人不完全了解它的来龙去脉——ADR就是替未来的同事准备的。
如果当时实在没写,我建议在项目每个里程碑收尾时补写,不要拖到全部结束。架构记忆会以惊人的速度衰减,一个月前的讨论,细节就已经开始模糊;三个月后连当时为什么吵都可能说不清楚。
3. 一次真实取舍案例:某订单系统存储选型的复盘拆解
前面讲的是方法论,这一节我完整走一遍实际复盘的过程,让你感受架构师复盘是怎么把模糊的回忆变成可推导的判断。下面这个案例来自某订单系统的演进,细节做了脱敏,但决策逻辑是原样保留的。
3.1 背景与约束:当时摆在桌面上的牌
该系统上线初期是典型的单库单表架构,订单表数据量在两年间从两千万涨到了接近两亿,日均订单量从三十万涨到三百万,数据库CPU在高峰时段经常逼近80%。业务部门开始频繁抱怨两个问题:一是订单查询接口越来越慢,二是后台报表跑不出来。技术团队被要求做一个数据层的架构升级。
在做选型前,我们先把约束摊开:
- 时间:业务不能停,系统切换窗口最多四小时;
- 团队:后端六人,没有人有过大规模分库分表的实际运维经验;
- 预算:可以接受一定的硬件成本上涨,但不能接受云成本翻倍;
- 查询模式:既有面向终端的单笔订单查询,也有面向运营的多条件组合查询,后者是慢请求的主要来源;
- 增长预期:业务方给出的预测是未来一年订单量可能再翻一倍,但不保证。
现在回头看,这五条约束基本决定了后面所有选择。很多架构复盘觉得当时决策“看不懂”,其实不是架构师头脑发热,而是约束没被记录。把约束摆出来,一切都有迹可循。
3.2 三个备选方案的量化对比
当时列了三个方向:在应用层引入分库分表中间件;使用某云厂商的分布式数据库产品做平滑迁移;保持单库结构,但做冷热数据分离和历史数据归档。
我先把三者的对比做成表格,这是复盘里最有用的一张表,因为后续发生的所有事都对应着表格里的某项风险:
| 对比维度 | 方案A:自建分库分表 | 方案B:云分布式数据库 | 方案C:冷热分离 |
|---|---|---|---|
| 应用改造成本 | 高,涉及SQL兼容性和路由逻辑 | 低,基本兼容原SQL | 低,主要是归档任务 |
| 运维门槛 | 高,需要自己处理数据节点调度 | 中,依赖云产品能力 | 低,现有人员可维护 |
| 扩展上限 | 高,可以持续扩容 | 较高,但受产品约束 | 中,单库瓶颈仍在 |
| 当前查询问题的解决效果 | 一般,组合查询仍需优化 | 较好,弹性扩展缓解压力 | 明显,冷数据不再拖慢热查询 |
| 一次性迁移风险 | 高,切割窗口内可能出问题 | 中,但产品未知坑不少 | 低,风险可控 |
3.3 当时的决策和后来的验证结果
最后的选择是:短期内先实施方案C做冷热分离,把超过六个月的历史订单迁移到归档区,同时把目标演进方向定在方案A,等团队补齐能力后再逐步落地。之所以没有直接选B,是因为“三个月内不切换核心库”是业务部门的硬约束,而云产品迁移的不确定性在这个时间窗口内风险太高。方案A是终局理想,但当时团队没人写过路由层,强行上手大概率把线上搞出事。
这个决策在当时看起来非常稳妥,复盘时我们却发现了两个此前没有充分验证判断:
第一,“查询慢”的真实原因被误判了。我们按照“数据量太大导致SQL慢”的直觉去设计,但冷热分离上线后,热库数据量减少了七成,查询性能的提升却只达到预期的一半。后来深挖才发现,报表慢的元凶有两个:一个历史遗留的多表JOIN写得太差,另一个是某条查询没有走索引。真正起作用的不是冷热分离,而是顺手做的索引优化。也就是说,我们为一个明确问题花了大精力做架构级改造,但问题的一大半其实只需要SQL层面的修复。
第二,方案A的启动条件早早就满足了,但没有执行的抓手。当初给ADR写了复审触发条件:“当团队具备分库分表维护能力或热库单表数据量达到1亿行时,重新评估方案A”。半年后这两个条件都达到了,可因为没有任何机制定期检查ADR,这个点被忽略了。直到复盘时翻开文档才发现,技术债已经又累积了一个季度。
3.4 复盘带来的三条修正
这次复盘给我们的输出非常直接。第一,把“查询优化优先级”提到“架构演进优先级”之前,规定任何存储层调整前必须先做全量慢SQL治理;第二,给所有ADR的“复审触发条件”接上实物:每季度由架构小组集中扫描一次,扫描结果直接进迭代排期;第三,把方案B的失效原因重新记录清楚,如果未来云成本降到预算线以内,这个方案要重新拿出来比一次。
这个案例最能说明架构师复盘的价值:它修正的不是“当初做得对不对”,而是“当初的决策地图里哪些路标已经变了”。没有这张地图,团队只能沿着旧路继续走,还觉得自己挺稳。
4. 踩坑与根因分析:架构复盘里最该深挖的部分
4.1 从“线上接口超时”挖到架构假设失灵的完整链路
架构复盘最怕只记表层原因,比如“某接口在高流量下超时,已通过增加缓存解决”。这不是复盘,是故障记录。真正的复盘要把问题链挖到底,直到触及某个架构设计时的假设。下面用一个缓存场景做示例。
现象是:某核心接口在促销高峰期出现大量超时,监控显示Redis命中率从94%跌到60%。直接原因不难找——热点商品的缓存Key在同一时间集中过期,瞬间穿透到数据库,连接池被打满。
很多团队到这里就收工了,但架构师复盘要继续问下去。为什么要设置统一的过期时间?因为当初设计缓存时,为了图方便给所有Key设置了一样的TTL。为什么没有对热点Key做特殊保护?因为缓存策略是跟着接口走的,没有按数据特征区分。为什么没有在架构设计阶段识别这个问题?因为在当时的架构评审里,“缓存”被认为是实现细节,不属于架构设计点。继续往下挖一层:为什么缓存会被当成实现细节?因为系统早期QPS很低,缓存是锦上添花,没有人认为需要给它单独建模。
你看,这个问题链的尽头是一个很朴素的架构假设:缓存策略可以静态地跟随接口,而不是动态地跟随数据。这个假设在小流量时完全成立,到了促销场景就失效了。复盘的价值就在于把这个假设捞出来,写进架构文档,否则下次换一个人设计新的缓存逻辑,还会踩同一个坑。
4.2 五问法在架构复盘中的正确用法
“五问法”很多人知道,但用起来容易变成在故障时间线上机械地问Why。架构复盘里的五问法,不应该问“这个操作是谁做的”,而要问“这个方案是在什么前提下成立的”。
我自己的套路是连续追问五层:表象现象是什么;直接技术原因是什么;这个原因对应的设计或代码是在什么假设下产生的;这个假设当时是否经过验证;今天来看这个假设是否仍然成立。回答完最后一层,你会发现几乎所有值得复盘的技术坑,最终都会落到某一个“曾经合理但已过时”的架构前提上。
有意思的是,很多线上故障其实在一年前的架构评审里就埋下了种子。当时没有人觉得那是问题,因为它需要“达到某个流量水位”才会被触发。架构复盘最值得做的事情,就是把这些埋下的种子找出来,而不是只收割已经长大的杂草。
4.3 监控缺失不是运维问题,而是架构问题
我再举一个特别容易被误判的类别:可观测性。几乎每次复盘,团队都会检讨“这次故障发现太晚,监控不够”。然后加几个指标,配几个告警,就算完了。我的看法是,如果监控缺失在多个项目里反复出现,它就不是运维的锅,而是架构设计时没有把可观测性当成一等公民。
可观测性在架构层面应该是什么地位?它应该和功能模块一样,有明确的设计目标:每个核心链路必须覆盖多少指标,哪些错误必须能定位到服务,哪些告警必须有SLO支撑。这些如果在架构阶段没有写入ADR,那么“监控不够”在后续每一个项目里都会成为标配问题。架构师复盘的一个重要动作,就是检查这套机制到底建了没有,而不是下次又把“加监控”写进待办。
5. 复盘的产出物:不是文档,是下一阶段的输入
5.1 用ADR目录形成“决策索引”
单独的ADR只是零散笔记,把ADR组织起来才是一个体系。我的习惯是在代码仓库里建一个docs/adr目录,文件按编号递增命名,每份ADR包含状态字段。目录页自动列出所有ADR的编号、主题和状态,让人一眼看到哪些决策还在生效、哪些已经废弃、哪些等待复审。
在项目进入稳定期后,我会定期看一眼目录里的“失效决策”和“待复审决策”数量。这个数字本身不是指标,但连续增加时,基本能说明系统正在偏离当初的架构设想。这时候就需要再开一次架构评审,而不是靠群里零星的抱怨来判断。
5.2 一张“架构演进地图”取代漫无边际的技术债清单
复盘产出最怕变成一份几十条的长清单,每条都很重要,却没有优先级。作为架构师,我会把复盘中识别的问题归成三档,画成一张简单的演进地图。
第一档是“近期要还的债”,通常是有明确触发条件、且已经影响线上稳定性的问题,比如前面案例里慢SQL治理。第二档是“中期演进方向”,对应的是约束可能变化后需要重新评估的决策,比如分库分表方案。第三档是“持续观察项”,没有明确的触发条件,但每隔一个季度要复核一次。这张地图每季度更新一次,由架构小组维护,而不是存在某个人脑子里。
有了这张地图,复盘会上的所有讨论就有了归宿:每条反馈要么变成地图上的一个条目,要么明确不进地图并说明原因。最怕的是复盘会开得热热闹闹,结束后每个人记忆里的结论都不一样。
5.3 复盘会议怎么开才不流于形式
我不建议把这种复盘做成“成果汇报会”。架构复盘会的本质是决策评审,建议直接叫“架构决策评审会”,议程就三件事:过一遍决策记录清单,挑两到三个失败的或存疑的决策深入讨论,确认演进地图上的下一批条目。时间固定在每个迭代结束后的半天,不超过三小时,参加者限定在能对架构决策负责的人,不需要全体开发都在场。
会议输出必须有可执行的结果。我的最低标准是:离开会场时,至少有一个条目在演进地图上从“待讨论”变成“已排期”,并且明确负责人。如果一场评审会结束,地图一个条目都没动,那这场会大概率就是常规的聊天会,不开也罢。
6. 我在复盘中最容易忽略的三件事
6.1 成功决策也可能建立在错误前提上
很多复盘只管失败案例,成功项目往往是走个过场,大家互相道贺就散了。这是最危险的。一个成功决策的背后,可能存着某个完全没有验证、只是运气好踩对的假设。比如你选了一个技术栈,上线后一切正常,你可能以为是自己判断准确;实际上可能是当时的业务流量根本没到触发问题的水位,问题只是还没暴露。
我现在复盘成功项目时,会刻意问一句:如果当初这个前提是错的,今天的系统会在哪个环节爆发?这个问题的答案如果连架构师自己都说不出来,说明成功里有很大的运气成分。把这种“尚未被验证的假设”写进ADR的复审触发条件,是成功复盘最重要的价值。
6.2 只复盘自己负责的子系统,会漏掉边界问题
架构师视角很容易偏向自己画的那条系统边界——自己负责的服务、自己的数据流、自己的技术栈。但很多真正的架构风险恰恰藏在边界上:两个服务之间的接口契约变更了,下游却没跟上;跨团队的调用链出现超时,各自复盘时都认为自己没有问题;数据流经过一个中间件时出了问题,所有相关方都假设中间件是稳定的黑盒。
我后来强制自己每次复盘拉上至少一个下游和一个上游的负责人,每个人只回答一个问题:“这次改动对你们边界的影响是什么”。大部分边界问题在各自的复盘里都不会出现,只有跨边界复盘时才会浮出来。
6.3 复盘结论必须可验证,否则等于没复盘
最没有价值的复盘结论是“下一步要加强稳定性”“考虑优化缓存策略”“适当提升可观测性”这类话。这类结论没法验收,下一次复盘时没有人能判断它到底完成了没有。
我现在要求自己写复盘结论时,每一条都必须带上可验证的度量方式。举例来说,“提升可观测性”应该改写成“为核心下单链路增加p99延迟看板,并配置错误率超过0.5%时的告警”;“优化热点缓存”应该改写成“为促销场景的热点Key增加独立TTL与过期时间抖动,下次大促时缓存命中率不得低于90%”。改写完,这条结论才算真正的输入,才有可能在下一次复盘时被严肃对待。
我自己后来也定了一个小规矩:每个阶段复盘必须留下至少一条可以被下次验证或证伪的判断。凡是写不出这类判断的复盘,我宁可不开,因为那只是自我感动的回顾,不会对下一个项目产生任何影响。架构师的复盘,说到底不是为了证明过去做对了什么,而是为了让未来的决策不再重复踩同样的坑。