先说一个我最近常被问到的场景:Code Review 时拿到一段 AI 生成的代码,功能能跑、单测能过,但你总觉得哪里不对——要么改不动,要么下次迭代时牵一发动全身。以前遇到这种代码,大家顶多嘴上说一句"不太好维护"就放过去了,但放过去之后,维护成本就在那儿等着你。这半年我陆陆续续试了不少办法,最后沉淀出一套给代码做"分级体检"的评估方法,代号就叫 t3code。这篇文章就是把 t3code 这个东西掰开揉碎讲清楚,包括它到底在解决什么问题、三个 T 分别指什么、具体怎么落地到团队工作流里,以及我在实际推行过程中踩过的坑。无论你是一个人维护老项目,还是带着小团队搞 AI 辅助开发,这套方法都能直接对着抄。
1. t3code 字号拆解:为什么代码评估需要"三级视角"
先解释一下 t3code 这个名字。t3 取自 three tiers,三个层级;code 就是字面意义上的代码。它本质上不是一个新的检查工具,而是一套评估代码质量的视角框架——把一段代码放到三个不同的时间坐标上去审视:当下能不能跑通、接下来三个月内谁接手都不踩坑、明年项目演进时这段代码会不会变成负债。这三个层级分别对应了 T1、T2、T3。
1.1 从一次"能跑就行"的 Code Review 说起
我印象很深的一次经历,是给一个内部工具加功能,我写完了一版,看起来完美。结果我的搭档接手时问了我一句:这段逻辑为什么放在这里?我当时愣住了。我确实也能说出理由,但理由追溯到源头,居然是我从某段 AI 生成代码里照搬下来的。那一刻我意识到一个问题:现在很多代码不是"写"出来的,而是"搬"和"拼"出来的。这种情况下,如果评估标准还停留在"能不能跑",那我们实际上是在批量制造自己都搞不清楚的代码块。
后来我做了个小实验,把过去两个月里,团队成员提交的代码按"是否由 AI 辅助生成"做了个粗略标记,再让三个人分别打分,评估维度是"未来一个月内如果让我改动,我需要多长时间上手"。结果很明显:那些"能跑但思路不清"的代码,平均上手时间是最短的三倍还多。这让我确定,需要把"可评估性"本身做成一个层次分明的体系。
1.2 t3code 的三大层级定义与边界
简单做一张表,把 t3code 的三个层级和它们各自守的阵地列清楚:
| 层级 | 关注的核心问题 | 典型评估手段 | 场景代表 |
|---|---|---|---|
| T1 | 代码功能是否正确、行为是否可验证 | 单测、集成测试、断言覆盖 | 接受一段新代码进入主干 |
| T2 | 代码是否能被别人连续维护 | 可读性打分、命名规范、复杂度分析 | 团队协作开发周期内 |
| T3 | 代码是否在帮助项目持续演进 | 模块边界、依赖方向、重构成本预估 | 跨版本迭代、架构调整期 |
T1 对应的是"现在"。T2 对应的是"接下来几个月的协作"。T3 对应的是"项目生命周期里的长期价值"。三者之间不是替代关系,而是递进关系。一个常见的误解是认为 T1 过了就等于代码没问题,这是典型的"以跑通为终点"思维。实际上 T1 只解决"它现在没坏",完全没回答"它为什么存在、它未来怎么变"。
1.3 AI 辅助编程时代,这套视角为什么更关键
以前代码是给人看的,编译器只是顺带检查一下。现在代码很大程度是先给大模型看、再给人看的——你贴进去一段逻辑,它基于你的上下文帮你补全。这意味着代码的"第一读者"变了,但"最终负责人"没变,仍然是你。如果评估标准不升级,你就等于让 AI 替你做了决策,你自己只负责背锅。t3code 的价值恰恰是把评估动作重新拉回人的手里:不判断代码是不是 AI 生成的,而是判断它经不经得起三层考验。
2. T1 层实操:功能正确性验证的硬指标与软校验
T1 层是我认为所有团队的起步层,它解决最基本的质量问题:代码的行为是否符合预期。这一层如果失守,后面 T2、T3 再好看都是空中楼阁。
2.1 可执行验证的硬指标清单
要验证代码功能正确性,单测通过是最低标准,但不是唯一标准。我在 t3code 里给 T1 层设了五条硬指标,你可以直接拿去对照:
- 核心路径覆盖:不是看覆盖率数字,而是看是否覆盖了 80% 以上的主流程分支。
- 异常路径覆盖:网络超时、参数为空、状态机冲突这三大类异常场景有没有测试。
- 无悬空断言:断言是否真正在验证结果,而不是"为了跑而跑"。
- 边界值验证:比如空数组、负数、极大值、重复提交这类输入有没有显式处理。
- 结果可重复性:代码是否能产出确定性结果,或者至少能解释非确定性来源。
这套硬指标里,最容易忽略的是"边界值验证"和"悬空断言"。我自己就犯过"覆盖率看着很高、实际没拦住 bug"的错。举个例子,我写过一个解析时间戳的函数,测试用例里只有一个正常时间格式,覆盖率几乎拉满,但上线后用户传了个"2024-02-30",解析直接异常。原因就是边界用例没加,覆盖率再高都是假象。
2.2 如何用特性开关和灰度验证补上真实环境缺口
即便 T1 硬指标全过,也只代表在测试环境下过了。真实环境里还有数据分布、流量特征、并发时序这些变数。所以我在 t3code 里额外要求一道"灰度验证"动作:凡是改动核心路径的代码,都要有这个开关能力,至少能方便地把线上流量切一部分过去观察。这里有个实操建议——如果你不想把特性开关做成重框架,最轻的做法是"环境变量+装饰器"模式:
# 一个轻量特性开关示例 import os FEATURE_OFF = os.getenv("T3CODE_SKIP_CALC", "0") == "1" def calculation(a, b): if FEATURE_OFF: return legacy_calculation(a, b) return new_calculation(a, b)代码本身不值钱,值钱的是你拥有"回退"的能力。灰度验证的目的不是保证不出错,而是保证出错时你可以快速撤场。我见过太多团队上线新算法后想回退,发现需要重启并且还得改代码参数,这就不叫灰度了,这叫赌博。真正到位的灰度,是你在写代码时就留好的逃生通道。
2.3 "能跑"与"验证充分"之间,如何确认边界
我碰到的另一个常见问题是"到底要测到什么程度才算充分"。这个问题没有标准答案,但 t3code 给了一个简单的判断:如果这段代码被删掉,有没有测试会报错?如果一个函数没有任何测试指向它,那它在 T1 层就算未通过,哪怕功能上它跑得再欢。这个判断标准很实用——它逼着你为每一段有效逻辑找到对应的"守卫"。当你能在几分钟内说出每段核心逻辑的守卫测试是哪一个,T1 层就算真正守住了。
3. T2 层实践:维护者友好度的可度量手段
T1 解决代码坏不坏的问题,T2 解决人烦不烦的问题。我个人认为 T2 是当下 AI 辅助编程时代最该被关注的层级,因为大量 AI 生成的代码表面上看完全合理,但读起来就是别扭——信息密度低、命名混沌、逻辑碎片化。这导致代码的"维护者体验"很糟糕。
3.1 认知负担评分:一个比"可读性好"更精确的度量
"可读性好"太主观了,今天我心情好就给你过,明天心情差就让你返工。t3code 在 T2 层引入了"认知负担评分"这个概念,它把可读性拆成几个相对客观的子项:
逻辑拆分度:一个函数是否把"判断"和"执行"完美分离。如果一段代码里既在判断数据合法性,又在执行业务计算,还兼任数据拼接,认知负担必然高。
命名信息量:变量名是否传达了业务含义。"data1"这种名字是零信息量,"pendingInvoiceList"就是高信息量。
跳转路径深度:阅读这段代码时,你需要在多少个文件之间来回跳转才能理解全貌。超过三个跳转深度,维护者大概率会崩溃。
注释与自解释比例:代码本身是否承载了大部分表达,还是需要大量注释来解释。这里有个反直觉的点:注释太多,往往说明代码本身名字起得不好。
我给团队做过一次演练:同一段业务逻辑,一份是精心命名的版本,一份是 AI 生成的变量名全是 tmp、item、res 的版本。让同一个人去改一个需求,命名好的版本只用了 6 分钟,另一个花了 22 分钟还在对着屏幕发呆。这就是认知负担的差距,它是实打实的时间成本。
3.2 协作检查清单:静态分析之外,还要看"改得动"
静态分析工具能查到命名规范、圈复杂度、重复代码,这些我都支持,但 t3code 在 T2 层更强调一个静态工具查不出来的维度——"改得动的缝隙"。所谓缝隙,是指一个函数对外呈现的这个逻辑单元,在需求变化时有没有接缝可以切入。举个实际例子:
// 一个需求:订单表新增一个展示字段 function getDisplayableFields(order) { // 这里是固定的字段映射 const fieldMap = { id: "订单号", username: "用户名", status: "状态" }; return Object.entries(fieldMap) .filter(([key]) => order[key] !== undefined) .map(([key, label]) => ({ key, label, value: order[key] })); }这段代码功能没问题,但要想加字段,必须改函数体内部。更好的设计是把 fieldMap 抽成构造函数的参数或独立配置项,让新的展示需求走配置通道而非代码修改。这就是"缝隙"的差别。没有缝隙的代码,每次需求变更都是对内部结构的冲击;有缝隙的代码,需求变更只是往配置里加一行。
我在 T2 层的检查清单上会加一条问题自查:"如果下个需求要改这里的字段列表,我是改配置还是改逻辑?"如果答案是"改逻辑",那 T2 就不算过关。
3.3 让新人入职即上手的验证方法:15 分钟测试
我还有一个实践性的 T2 验证方法,很便宜但很有用:15 分钟测试。找一个对这块代码完全陌生的人(新人实习生最好),给他一个真实的小需求,计时 15 分钟。如果他能在这段时间内定位到需要修改的文件并指出大致改法,说明 T2 层过关;如果他一直在问"这段代码是干嘛的""这个变量是什么",说明 T2 层失守。
这个方法我第一次测试时,结果不怎么好看——团队里两个核心模块的陌生上手时间都超了 15 分钟,最夸张的一个模块,新人看了 25 分钟还毫无头绪。后来对这些模块做重构,核心动作就是拆函数、改命名、补充关键注释。重测后新人基本都能在 8 到 12 分钟内完成定位。这个测试的成本几乎为零,但收获的直接反馈比任何代码评审都真实。
4. T3 层进阶:让代码参与架构演进的"连接性评估"
T3 层是 t3code 里最抽象但也是最有长期价值的一层,它判断的是一段代码在项目未来走向里的角色。
4.1 连接性图谱:代码之间是协作还是纠缠
我发明了一个很朴素的观察方法,给项目画"模块连接图"——不是正式的架构图,而是在一张白纸上把模块和模块之间的调用关系用线条画出来,线越多的地方颜色越深。画完之后你往往会发现,多数项目的多数代码集中在少数几个"超级模块"上,连接线密到像一团毛线。这样的代码就是典型的 T3 层不及格:它确实能工作,但它是整个架构里最容易被牵一动全身的区域。
t3code 的做法是,在开发规范里加一条约束:新增代码不允许反向依赖——下层模块不能依赖上层模块,业务模块不能反向依赖基础设施模块。这条约束看起来像是架构课上的常识,但实际执行时真的很难守住,因为 AI 生成代码时不认你的架构边界,你贴进去的上下文在哪,它就往哪靠。所以团队需要有意地设置"依赖检查"这一关,把它固化到 CI 里,靠系统拦截而不是靠人自觉。
4.2 量化重构成本,而不是等"烂到要重写"
很多时候项目里已经积累了大量 T3 层不合格的代码,想立刻重构又不现实。我的建议是先量化成本,再决定优先级。t3code 给了一个简单的量化公式:修改频次 × 理解成本 = 重构优先级。一个模块如果每个月都被需求碰到,且每次处理都需要很长时间理解,那它就该排到重构清单的高位;反之,如果一个模块虽然结构很烂,但它已经在稳定运行且未来半年都没需求,那你就别动它,让它继续跑。
这个公式帮我避免了很多"看着代码难受就动手"的错误。我有一次特别想重构一个老模块,它代码写得很丑,让我浑身难受,但量化之后发现它上一次被改动已经是半年前的事,未来也没有计划需求。我强忍着没动它,结果省出了大量时间投入新业务的开发。T3 层的核心不是追求架构的绝对优雅,而是把有限的维护精力投放在最影响演进速度的地方。
4.3 演进风险评估:从单点代码看到系统瓶颈
最后再讲一个 T3 层的实战技巧:给每个核心模块做"演进风险评估"。它关注四个问题:
- 单点故障风险:如果这个模块挂了,有多少业务会受影响?
- 变更放大系数:改一个需求,平均要动几个文件?
- 知识沉淀程度:模块里的关键决策,写进了设计文档还是只在某几个人的脑子里?
- 替换难度:如果这个模块要重写,预计需要多少人力?
这四个问题的答案决定了一个代码块的"战略地位"。我都建议把风险结论直接写进项目 README 或 ADR 文档里,让后面接手的人一眼就知道哪些模块是雷区。这不是为了吓唬人,而是让代码库成为有记忆的组织。真正成熟的工程团队,代码之外还有文档和决策记录支撑,代码本身只是整个知识体系的一个切面。
5. 把 t3code 装进工作流:从个人评估到团队机制
t3code 如果只是一个概念,那它没有任何意义。关键是要落地到工作流里,让评估动作用不了多少额外成本就能完成。
5.1 与代码评审流程结合的"三层章"机制
我给团队设计了一个叫"三层章"的评审机制:在每次 MR/PR 里,要求提交者自行标注 T1、T2、T3 三个层级的自评结论。不需要写长篇大论,只需要简单的确认:
- T1:测试已补全,核心与边界路径覆盖。
- T2:命名、模块边界、改动缝隙已自查,新人可理解。
- T3:依赖方向正确,未新增反向依赖,演进风险可控。
评审者基于这三项自评做抽查验证。大部分情况下,自评基本可信;但凡是自评和实际不符的,会被标记为"沟通成本异常"。试行一个月后,有个明显的效果:提交代码的人开始主动思考 T3 层的依赖方向问题了,因为"被挑出毛病"会让这次提交带上有记录可循的沟通成本。人并不会仅仅因为规范而改变,但会因为"抽象质量至少会被看见"而主动收敛。
5.2 无需重工具的轻量落地模板
很多团队一提到质量体系就以为要上一堆平台工具,实际没必要。t3code 落地可以不走任何重型工具,我用一个 .t3code.md 文件加三条 git 钩子脚本就搭起来了。简单说下做法:
第一步,在仓库根目录放一个架构边界声明文件,用最朴素的文字描述依赖方向:
# t3code 架构边界声明 - domain/ 不依赖 infrastructure/ - application/ 只依赖 domain/ - interface/ 可以依赖 application/ 但不得依赖 infrastructure/ 的实现第二步,写一个依赖方向检查脚本,放进 CI,逻辑简单到只有几十行。
第三步,利用 git hook 在提交时做基础的自检提示,比如是否包含 TODO、是否有 console.log 残留在核心路径。
这套东西加起来不用半天就能搭完。它的核心价值不在于脚本有多智能,而在于它把 t3code 的评估动作从"评审时靠人脑想到"变成了"流程本身提醒你做"。我从不在工具选择上追求复杂,因为工具越复杂,执行概率越低。真正能持续运行的质量机制,一定是融在日常动作里的轻量提醒。
5.3 单人项目与小型团队的简化版应用
如果你是一个人维护项目或者不到五人的小团队,整套 t3code 流程可以再简化。我的建议是只保留两件事:提交前单测必须过,核心模块的依赖边界必须画清楚且严格遵守。T2 层靠"隔一周没动这段代码,再看还能不能立刻看懂"来验证。T3 层靠每季度花半天时间画一次模块连接图来观察瓶颈。听起来很基础,但长期坚持的效果远比搭建豪华质量体系来得可靠。小团队的敌人从来不是质量规范不够,而是执行成本太高导致的半途而废。
6. 推行 t3code 的踩坑记录:这三件事几乎每个团队都会遇见
最后聊一些实际推行中一定会遇到、但文档里不会写的坑。
6.1 "新人友好"不是平均友好
第一坑是"面向所有级别设计统一标准"这个隐性冲动。我一开始制订规范时,总想着让经验最浅的同事也能看懂,结果写得太细,资深工程师觉得是在侮辱智商,新人依然看不懂核心矛盾在哪。后来我把 t3code 的文档拆成两份:一份是完整的、给所有人做参考的,一份是只有三页的执行摘要,只写"提交前我该检查什么"。事实证明,简短的执行摘要比厚厚的手册更能影响日常行为。团队执行力不取决于文档本身,取决于有没有足够低门槛的自检入口。
6.2 度量指标永远只能当体检报告,不能当 KPI
第二个坑是我自己掉进去的:想用 T1、T2、T3 的数据做绩效考核。很快就发现有成员为了数据好看,把函数拆得极度碎片化,结果可读性反而下降了——认知负担低了,但函数间跳转太频繁,整体维护体验变得更差。那之后我明确了一个底线:t3code 指标只能用来体检,不能用来考核。体检报告的意义是让人知道自己身体状况,如果体检指标直接跟奖金挂钩,大家就会想办法优化指标而不是优化健康本身。这条原则比任何细节都重要。
6.3 别被"历史包袱"绑架,也别急着推翻重来
第三个坑跟历史代码有关。推行规范时,老代码就像一面墙挡在那里,怎么处理都有争议。我的经验是不要把规范用来翻旧账——历史代码的 T2、T3 层改动只发生在"它恰好需要改动"的时候,绝不因为"它不合格"就主动重写。否则你会陷入一个非常尴尬的处境:规范推行半年,绝大部分精力都消耗在老旧代码改造上,新业务反而没推进。让旧代码继续跑,只在触碰它的需求和场景里局部消毒。这个策略听上去不够爽快,但它是唯一能让团队长期坚持推行新规范的方式。
6.4 隔一段时间重读自己代码的"冷静测试"
顺手再分享一个小技巧。推行 t3code 时间久了你会发现,自己也会退化——写的时候觉得很好懂的代码,两周后回来看就想不起为什么这么写了。所以我现在有个习惯:给自己设一个"冷静测试",凡是有争议或复杂度偏高的代码,写完之后放两天,再认认真真重读一遍,问自己三个问题:第一,我还能在三分钟内说清这段代码的设计意图吗?第二,这里的命名是否传达了这个意图?第三,如果要加一个新需求,我是改这里还是加旁边?任何一个问题回答困难,就说明这段代码在 T2 甚至 T3 层存在隐患。趁还没忘赶紧调整,远比两个星期后面对疑问去费力回想要划算得多。
t3code 这个名字听起来像某个复杂框架,但拆开来看,它无非是一套朴素的代码评估习惯:先确认代码确实能在所有实际场景下正确工作,再确认团队里的每个人都能顺畅维护它,最后确认它不是在给项目的未来挖坑。这三层问题,不管你的团队用不用 AI 生成代码、用的是哪种技术栈,都值得在每次提交代码时问一遍。我自己推行下来的体会是,真正改变代码质量的不是你装了多贵的工具、写了多厚的规范,而是你开始用一种有层次的方式审视每一段代码的那一刻。当你养成了这个习惯,t3code 就已经在工作了。