凌晨两点半生产环境告警把整个值班群炸醒。我爬起来看了一眼监控面板报错的服务是三年前上的旧模块提交代码的人早就离职了当时负责测它的同事也转岗了。屏幕上那行日志混着半截中文和全角括号旁边还挂着一句这里后面再优化的注释。我盯着它看了几秒心里冒出一个特别丧的念头技术债这东西就像一座公墓活着的时候没人愿意进去扫墓等它开始渗水、塌方、闹鬼夜里被叫醒来处理的永远是我们测试工程师。这篇文章就想聊聊在技术债缠身的项目里测试工程师到底该怎么活下来怎么从填坑的变成排雷的怎么在烂代码库里守住自己那点职业尊严。1. 技术债是怎么一步步把我们逼成守墓人的1.1 技术债的真面目不是 Bug是欠条技术债这个东西圈子里聊得很多但大部分定义都是开发视角的什么为短期交付牺牲长期质量代码腐化的利息之类的。站在测试的角度我想给一个更直白的描述——技术债是一个系统在健康状态下本该有、但为了赶工期没做的那部分东西。它可能是一段绕了三层的临时逻辑可能是一张没人维护的配置表可能是一条从未写过的异常处理分支也可能是一份早就应该补齐但一直有空再说的接口文档。它不是 Bug。Bug 是这道门明明是朝左开的你写成了朝右开而技术债是这道门为了赶进度先拿木板钉上了打算以后换铝合金但以后再也没人记得这回事。Bug 总有修完的一天技术债是永远还不清的。你每修一处可能会在别的地方又欠一笔。我平时喜欢用一个装修类比新房装修的时候为了省时间师傅把电线全部走明线住进去确实很方便网络也不会断。但三年后你想重新刷墙、改格局、装新风系统就会发现当初那套明线成了最大的障碍。这时候你只会骂当时的自己为什么不多留一周工期。而测试工程师就是那个每次想刷墙都被电线挡住的人还得负责跟家里人解释墙为什么刷不了。1.2 技术债的利息是怎么滚起来的技术债最可怕的地方其实不是债本身而是利息的复利效应。举一个几乎所有项目里都见过的例子某个核心服务早期为了快速上线没有做模块拆分所有逻辑都堆在一个巨型单元里模块之间靠全局变量通信。第一年还好十来个开发投入产出比勉强能接受。第二年加需求改一个配置项要重启三个模块部署工程师天天骂娘。第三年这个模块已经没人敢碰了注释里写满了勿动动则死人。在这个过程中测试工程师的感受是最直观的回归测试的用例量莫名其妙地逐年翻倍因为功能之间耦合越来越多每一次版本发布线上总会冒出一个不知道跟这次改动有什么关系的缺陷排查缺陷的时间越来越长因为代码里没有文档、没有日志规范、没有异常兜底新人上手的时间越来越久一个简单的功能要问五六个人才能摸清调用链路。这些利息不会记在任何一个 KPI 上但它会一点一点蚕食团队的质量。到了某个临界点系统就会从能跑但难维护变成一改就炸这时候所有人都知道技术债爆了但没人知道该怎么填只好继续举新债补旧债。1.3 为什么最后守墓人是我们你可能会问技术债明明是研发、架构、产品一起欠下的为什么最后背锅的总是测试这个问题我思考了很久结论其实很现实因为开发可以绕开烂代码测试绕不开。开发写新功能的时候虽然在烂地基上施工很憋屈但至少他可以挑一块自己熟悉的领地。测试不一样测试需要对整个系统负责。一个需求上线你得验证新功能本身还得验证它有没有影响老功能、数据迁移、并发边界、异常恢复、第三方依赖。这些地方恰恰是技术债藏得最深的地方。线上出了问题产品会说测试怎么没测出来开发会说本地跑没问题啊最后所有的压力都会落到测试头上。而且还有一个更隐蔽的原因测试活动本身就是技术债的曝光机。你跑一轮回归等于把系统身上所有腐烂的伤口都翻了一遍你写一份测试报告等于在给全团队做体检报告。那些久治不愈的病灶只有你天天看见。久而久之别人可以假装看不见你装不了。提示如果你在项目里也有同样的处境别急着内疚。技术债不是一个人欠的测试只是在替整个团队还利息。2. 守墓人上岗指南在烂代码库上重建测试秩序刚接手一个技术债严重的项目最忌讳的就是太想做好。我见过不少测试新人一上来就立大目标我要把自动化覆盖率干到百分之八十我要把所有历史缺陷清理掉我要搞定整个回归体系。结果不到两个月人走了活没干成。在烂项目里做测试第一课不是冲锋是排雷——先搞清楚自己面对的是什么再决定把力气花在哪里。2.1 先摸清墓地埋了什么做一次技术债全景盘点接手任何一个新项目我做的第一件事永远不是写用例而是盘点。技术债不是抽象的概念它具体分布在你需要维护的每个服务、每个界面、每张表里。你只有先把它们找出来才不会被它们突然冒出来的样子吓到。我的盘点是四步走第一步用代码扫描工具把所有服务过一遍SonarQube、ESLint、Checkstyle 都行目的不是看代码规范而是看复杂度。圈复杂度超过二十的模块、重复率超过百分之三十的文件、大段的 TODO 注释都是潜在的雷区。扫描结果不用太细能按服务聚合出哪里最烂就够了。第二步翻测试报告和线上告警记录。不是为了找具体 Bug而是找高频故障的模块。前一年的线上问题按服务维度聚合一下你就能画出哪个坟头冒烟最频繁的分布图。第三步找开发聊一轮。不需要组织正式会议就私下问两三个核心开发这项目里哪些模块你碰都不想碰为什么你会得到比任何静态检查都有价值的答案——每个烂代码的核心地带有自己的名字和故事。第四步把这些信息汇总成一张风险矩阵横轴是有问题的模块纵轴是故障频率、变更频率、业务影响。最后圈出一个高危区比如订单模块半年内变更四十次线上故障八个没有自动化测试覆盖。这就是你未来最需要投入精力的坟头。提示这张矩阵别只留给自己。整理成一页纸的文档发给团队哪怕只是简单地标红后续推动测试资源倾斜的时候会省很多口舌。2.2 测试分层策略不是所有债都值得还做完盘点接下来就是伤脑筋的地方到底怎么分配有限的测试精力。技术债严重的项目里人力永远是不够的——缺陷等着你测、新功能等着你测、历史功能还得防退化。这时候最忌讳的是平均用力。我的原则是用 ROI 决定投入而不是用覆盖面决定投入。核心链路优先。先找出系统里哪几条路径是用户天天走的、哪几个模块挂了会直接导致损失。这些路径就是你的生命线必须保证自动化覆盖。在订单系统里下单-支付-出单-退款这条链路就算代码再烂也要守住至于后台管理系统里某个一年用一次的列表导出功能先别急。稳定需求优先。自动化测试最怕对象频繁变。需求还在迭代、页面还在改、接口还在调的时候别急着写自动化脚本否则你写完脚本改需求改完需求改脚本最后脚本跟你之间的关系就是互相折磨。等到功能稳定了再补自动化效率要高十倍。可证明价值优先。如果你做的自动化用例跑了一百个一半是永远不过的失败用例另一半是这个功能我压根没人用的成功用例那这个自动化体系的老板感知就是浪费。所以宁可做成五十个用例全绿、每个都是核心链路也不要贪多。这个道理同样适用于手工测试先测改动点本身再测改动点的最邻近模块最后才测全量回归。很多测试同事有个坏习惯改动一个小接口就顺手把整个系统回归了一遍费时费力还没抓到重点。我曾经在一个支付项目里见过同事为了验证一个文案改动硬是把全链路回归跑了两个小时最后只发现一个跟文案毫无关系的环境噪音缺陷。她觉得自己很尽责但实际上浪费了最宝贵的时间窗口。真正合理的做法是先把改动点周围的风险面框出来再扩展到整体回归——这样哪怕时间不够核心风险也已经覆盖了。2.3 自动化测试在烂项目里的求生姿势回到一个真实的问题技术债缠身的项目自动化测试到底怎么落地很多团队都有过失败的尝试买了工具、配了框架、写了脚本最后全部烂尾。我总结了一下烂尾项目通常有两个共同点一是没有选对切入点二是没有接受灰头土脸地运行。先说切入点。如果整个系统的自动化覆盖率目前是零不要一上来就想着搭企业级框架。我的建议是直接从回归最痛、改动最频繁的场景下手。最典型的就是冒烟测试把核心链路的用例用自动化搭起来每次发版前跑一遍能挡住一半的低级回归问题。其次是接口层的自动化。相比 UI 自动化接口自动化在烂代码面前特别香第一它不依赖界面的频繁调整页面改了接口不一定改第二它跑得快可以用来做大规模回归第三它的断言可以直接和数据结构、响应码、数据库记录挂钩定位问题更快。所以即便你的 UI 自动化刚起步接口自动化也完全可以先跑起来。我在几个项目里都是这么干的——UI 层自动化负责守住体验接口层自动化负责守住院子两层一起用才能稳住阵脚。再说接受灰头土脸地运行。很多自动化项目死在太完美主义——脚本必须健壮、报告必须漂亮、覆盖率必须达标。但技术债重的项目里第一版自动化跑起来一定是千疮百孔的脚本时好时坏环境不稳定数据不干净。这时候别放弃先让它跑着哪怕只有一半用例全绿也比人工回归漏掉要害要强。后面再迭代脚本的稳定性一点一点把黄色区域变绿。提示自动化测试的初期目标不是全自动而是比人肉靠谱。你自己心里要有数第一版只要做到测试范围固定、可重复执行、结果可追溯这三个最低要求就够了。3. 和僵尸功能过日子遗留系统的测试生存技巧如果说技术债像公墓那遗留系统里那些没文档、没用例、没人在意的老功能就是躺在墓里偶尔还动一下的僵尸。你没法把它们全部挖出来重新安葬只能学会跟它们共存同时保证它们不会半夜爬起来咬你一口。3.1 探索性测试没有文档时的夜视仪技术债重的项目最典型的特征是文档缺失。需求文档找不到接口文档过期测试用例几十年没更新。这种环境下常规的按用例执行是不现实的——你连这条用例对应的功能还存不存在都没把握。这时候我建议把探索性测试当成主力。探索性测试的核心思想是把测试设计和测试执行合并起来一边探索一边学一边验证。听起来似乎很随意但它是有方法论支撑的核心是基于风险的探索。我习惯的做法是拿到一个功能或者一个模块先问三个问题这个功能给谁带来价值谁的什么任务非它不可它的输入输出边界在哪正常流程之外有哪些异常、边界、逆向的操作它与周边模块有哪些数据交换一个字段的格式、长度、非法值有没有可能在别处被消费问完这三个问题再结合测试直觉去构造场景。这个方法特别适合那种没人清楚整个系统长什么样的遗留环境你不需要一张精确的架构图只需要沿着数据流走几遍就能形成自己的活地图。当然探索性测试的产出不能只是我测了没问题。每一次探索都应该做记录至少记下测了哪些场景、哪些地方崩溃了、哪些异常行为出现了。这些记录会变成下一轮回归的种子也会让僵尸功能逐渐变得有文档。3.2 把故障注入搬进测试环境别让系统活得太安逸技术债重的系统最怕的不是功能坏而是坏得没有预兆。与其祈祷它在线上别炸不如在测试环境主动炸一炸。混沌工程这个名字听起来很玄乎其实落到测试日常就是故意制造故障看看系统会怎么反应。给测试同学的操作建议如下第一步挑一个看起来太稳的核心流程。比如用户下单-支付-出单-退款在测试环境跑通。第二步手动注入故障。故障可以很简单把下游的微服务停掉看看主服务超时逻辑扛不扛得住把数据库的连接池调小模拟高并发下的挤兑或者干脆给测试环境加几十毫秒网络延迟感受一下真实用户在网络抖动时的体验。第三步观察系统的表现。最理想的情况是系统有超时、有重试、有兜底最终把错误优雅地反馈给用户。最糟糕的情况是系统直接卡死、丢数据、连环崩溃。你需要记录的就是这些糟糕情况它们往往是技术债里最隐蔽的隐患。别担心这些故障注入会搞坏环境。测试环境本来就是用来折腾的你在测试环境里把系统折腾挂了总比它上了线在凌晨两点折腾生产环境强。我甚至觉得一个测试团队如果从来没有故意搞挂过环境那这个环境一定不够接近真实测试的有效性一定是打折的。3.3 技术债台账让债主们看得见账单前面说的都是怎么在烂代码里做测试但有一个问题绕不开你发现了债务、验证了债务然后呢如果只是把缺陷提交进 bug 库过两个迭代就没人记得了。所以我强烈建议每一个测试团队都建一本技术债台账。台账的字段不需要很复杂我常用的就这么几列字段说明示例模块问题归属订单服务债务类型文档缺失/架构腐化/测试真空/性能隐患测试真空风险等级高/中/低按影响和概率高当前表现现在是否已经造成问题偶发超时触发条件什么情况下会爆雷大促峰值预计修复成本可能需要多少开发量2 人日建议偿还方式重构/补测试/写文档/加监控补测试加监控这本台账不会替你还债但它有两个作用一是让技术债从模糊的担忧变成具体的事务二是你在跟产品、开发沟通的时候有了客观依据。比如下个迭代排期你说我强烈要求加两天测试时间因为这个模块补完测试之前风险太高和你说根据台账这个模块三个月里已经引发四次回归问题修复成本至少三万人日建议优先处理后者明显更有说服力。4. 让技术债暴露在阳光下度量与推动偿还你自己看得见技术债不代表团队看得见。测试工程师要是不想让守墓的工作白干就得学会用量化数据和推动机制让全团队不得不低头看看这些坟头。4.1 我盯着的四个测试度量指标先泼一盆冷水很多测试团队的度量指标在技术债严重的项目里是自欺欺人。比如发现缺陷数这个指标有个天然的问题——缺陷报得多到底是测试能力强还是代码质量差再比如用例执行数用例数量多、执行得快但测的都是边角料有什么意义我平时真正看重的指标只有四个既反映问题、又可操作第一个是线上缺陷逃逸率DRE线上发现的严重缺陷数除以总严重缺陷数。这个指标能直接说明回归防线漏没漏技术债越重这个数字越容易出现莫名其妙的变化。它不是用来追责的是用来对标测试投入的。第二个是高危模块的自动化覆盖率。别测整体覆盖率整体覆盖率百分之八十的数字有可能很好看但高危模块可能还是零。把覆盖率的定义框到高风险模块核心链路上数字才具备预警意义。第三个是缺陷从提交到关闭的平均时长。这个数字在烂项目里通常吓死人三个月都关不掉一个缺陷很常见。它反映了端到端研发效率以及技术债导致的修一个坏两个的恶性循环。第四个是回归风险指数——我自己习惯的叫法统计每次发布时回归用例中失败用例的占比。占比越高说明改动越容易触发回归也就是技术债利息最直接的体现。这四个指标不需要每天都看我习惯做成周报和月报按月画趋势。趋势比单点更重要只要曲线在向好的方向走哪怕慢一点都是技术债在逐渐被偿还的证据。4.2 一份技术债报告的正确打开方式很多测试同学对写报告有抵触觉得就是走形式。但我想说报告不是写给你的领导看的是写给假装不知道有债的团队看的。一份好的技术债报告要在第一屏就让所有角色找到自己的名字。我自己常用的报告结构是第一部分用三十个字以内的结论开头。比如订单模块仍处于高风险状态已连续三周出现回归缺陷建议下迭代优先补齐自动化测试。第二部分给数据。把逃逸率、覆盖率、缺陷平均关闭时长的趋势图贴上去图上要标重点事件比如5月大促后发现缺陷翻倍8月核心链路重构后覆盖率从 30% 升到 55%。第三部分列明细。把技术债台账里风险等级为高的项逐条写清楚每条都标注模块、风险原因、当前影响、建议动作。第四部分给承诺。写下接下来一个迭代测试团队会做什么比如本周将补齐下单链路接口自动化预计新增四十条用例。这样一份报告发到项目群里效果会比那种罗列三百条 Bug 的 Excel 强太多。为什么因为 Bug 列表只能证明你测出了很多问题而技术债报告能证明你知道问题的根源和它要花多少钱解决。后者才是测试工程师在团队里的价值。4.3 推动团队还债不靠吼靠机制看完了报告团队也认可了风险然后呢如果下一个迭代还是狂塞新功能那报告就白写了。推动技术债的偿还光靠个人意志是不够的得靠机制。这里分享几个我在项目里实际落地过的做法。第一个是质量门禁。发布前必须有测试通过的门槛高危模块这块没有达到要求就不准发布。这个门禁可以由测试工程师来守只要你把门禁指标定得合理——比如高危模块新增用例不足十条不予发布——开发会主动来找你讨论哪些用例有价值而不是一味拍桌子。第二个是还债占排期比例。和团队达成一个隐性的约定每个迭代技术债偿还类的工作量至少要占到总工作量的百分之十到二十。这个数字不需要死板但这个意识必须有。没有这个比例技术债在排期会议上永远排不上号。第三个是让开发为自己的代码付测试成本。我给团队带过一个习惯改动涉及高危模块的时候开发自己先写一条冒烟用例测试来做深度验证。别小看这一条用例它逼着开发去理解自己改动的边界防止我以为没影响结果影响了一片。第四个是用事故复盘倒逼偿还。每当线上出事故复盘会不要只停留在谁改了代码哪个环节没测到多问一句这是不是某个已知技术债的又一次利息支付。如果是把这个技术债的风险等级调高安排还款计划。让团队发现原来每一次线上事故都是在给旧债交利息。说这话的时候不要带情绪要让数字说话大家就会明白还债比继续欠债划算。5. 守墓人也要进化从测试执行者到质量守护者技术债严重的项目待久了人很容易废掉——每天被回归、排查、填坑消耗渐渐地就会觉得自己只是个点鼠标的。但我不这么看。恰恰是这种环境才逼着测试工程师的段位快速成长。守墓人当久了看惯了腐烂和死亡你会比谁都清楚一个系统真正怕什么。把这些经验转化为更深层的能力才是长期价值。5.1 像渗透测试工程师一样思考技术债重的系统最常见的死法不是正常的 Bug而是被意想不到的输入搞死。所以我很鼓励测试同事学一点渗透测试的思维方式。不需要你非得去挖漏洞、打演练只需要把攻击性测试的思想引入日常工作。什么叫攻击性测试简单说就是不再问这个功能应该怎么用而是问这个功能应该怎么被打。表单会不会被塞入超长字符串、特殊字符接口会不会收到伪造的高权限身份信息文件上传会不会被传一个可执行脚本权限控制能不能被越权绕过你会发现这些问题里面很大一部分本质上还是技术债因为早期的代码没有做输入校验、没有做访问控制、没有做异常兜底。用渗透测试的视角去测技术债系统往往能批量发现一类以前测不出来的问题。我还建议每个测试团队都定期做一轮红线演练挑出核心链路的接口用工具构造正常流量、异常流量、恶意流量三种情况观察系统的反应。你很快就会发现那些在正常用例里看起来没问题的接口在异常流量下有多脆弱。这个过程本身不需要很深的安全知识但能把你的测试思维从验证正确提升到挑战正确。5.2 让 AI 测试工程师当你的夜班保安这两年 AI 测试的话题热得不行什么智能用例生成、自动断言、缺陷预测都出来了。我不能说这些都在实际项目里成熟了但有几个场景AI 在技术债项目里是真好用足以让守墓人解放一部分精力。第一个是接口变更检测。AI 工具可以自动比对接口在不同版本之间的差异新版本接口字段变了、类型变了、默认值变了它能自动标出来。这项能力在缺失文档的遗留系统里特别有价值相当于帮你自动维护了一份活接口文档。第二个是智能回归筛选。当你有一大批自动化用例而每次发布人力有限时AI 可以根据代码变更影响面帮你筛出最可能受影响的一批用例先跑。我自己实测下来这个功能误报率有点高但漏报率低也就是说它筛出的用例不一定都是关键的但它不会落下关键的部分。这已经能显著减少全量回归的时间了。第三个是缺陷自动分类打标。AI 读缺陷描述、日志、堆栈自动把缺陷归类到模块、原因、优先级。这个功能初看是个小优化但在技术债多的项目里它让缺陷长期滞留 bug 库这个老大难改善了不少——至少每个缺陷都有了清晰的归属和热度不会烂在角落里发霉。要强调的是AI 辅助测试目前还不能替代测试工程师的判断它更适合当你的夜班保安——你睡觉的时候它盯着第二天早上你去处理它筛出来的重点。但保安替你挡不了所有的贼最终的验证、分析、决策还是要自己来。5.3 为简历打工资历成长与技术债的另类价值说句直白的话在技术债严重的项目里的经历写到简历上其实是很有价值的财富。网上那些热搜词——渗透测试工程师学习自动化测试工程师工作实战AI 测试工程师——背后反映的是行业对测试工程师综合能力的要求在上升。而你在烂项目里被迫学会的东西恰好在往这个方向靠。想想看你在腐烂的代码库上做风险盘点等于做了系统架构的逆向梳理这是架构理解力你在没文档的环境里做探索性测试等于练出了测试思维这是测试设计力你推动团队还债、建立度量体系等于学会了影响力这个词真正落地的方法你引入攻击性测试和 AI 辅助工具等于提前踩了踩下一波测试技术的坑。我在面试的时候经常听到候选人说我们项目技术债太多测试没法开展他的潜台词是这个项目埋没了我的能力。但如果换一个说法——我在一个技术债严重、风险极高的项目里通过风险盘点、分层测试、推动机制建设把线上逃逸率降了百分之四十——这就变成了你能力的证明。同一个经历前一种说法是抱怨后一种说法是战绩。资历这个东西不是看你在多干净的代码里写过多少用例而是看你在多烂的环境里建立过什么秩序。6. 写在最后守墓人的心理建设和一点私心啰嗦了这么多最后分享一下真实的心态问题。做测试工程师的人多少都有完美主义倾向看到技术债天天血压升高总觉得自己有责任把代码变干净。但我想说的是技术债不是一个人欠下的也不是你一个人能还清的。守墓人的职责不是把整座公墓打扫得一尘不染而是保证没有尸体跑出来害人。在这个前提下有几点私人心得想送给同行第一学会给自己关灯。不管项目里乱到什么程度该下班就下班。我见过太多测试同事因为线上告警、开发催测、缺陷追责把自己熬成了项目里的替补灭火队员。这个角色你一旦当久了反而没有人重视你的专业建议。第二记录自己的工作输出。不要觉得天天写报告很烦那是你的军功章。每一次风险提示被验证、每一次线上事故被你说中、每一个通过你的门禁拦下的问题都值得记下来。半年之后回看你就知道自己在这个公墓里的实际价值了。第三保持对好代码的敏感性。天天泡在烂代码里审美会被拉低的。我自己的习惯是每个季度至少看几个高质量开源项目的测试代码或者参与一些技术社区的代码评审。目的不是学到具体的技术而是提醒自己好系统的测试长什么样以及我们做测试的长期目标是什么。最后说一句可能有点丧但很真实的话技术债公墓里的守墓人从来不能阻止有人继续埋东西。你唯一能做的是让每一座坟都立好墓碑记录好死因定期巡视及时排雷然后在合适的时候推动一场有计划的搬迁。这不光是一份工作它教会我的其实是怎么在充满不确定性的系统里靠专业、数据和沟通守住一个又一个看似守不住的底线。