T3Code:从能跑到可复用的代码分层方法

上周评审一段支付回调逻辑,我的第一反应不是“有没有问题”,而是“这片代码还敢不敢改”。功能测试全绿,线上也跑了大半年,可当一个新需求进来——要在原订单价格基础上叠加积分抵扣——改起来却像抽积木:牵一发动全局。这种状态我见过太多次,也正因为如此,我最近在团队里开始推行一套叫 T3Code 的代码分层方法。它的核心思路很简单:把每一段代码按 T1、T2、T3 三个层级去评价。T1是能跑,T2是好改,T3是可复用、能支撑新需求低成本落地。这篇文章就把这套方法、一次完整重构案例和推进时的坑一次讲清楚,适合正在写业务代码、做技术评审,或想给项目安排一轮代码体检的朋友参考。

1. 为什么需要T3Code:能跑的代码并不等于好代码

先说一个我在多家团队都观察到的现象:项目上线前半年,开发速度很快,需求一个接一个。可到了半年后,同一个需求从两天变成五天,再到一周。这时候大家习惯性的归因是“业务变复杂了”“历史包袱重了”“人手不够了”,但我越来越觉得,真正的根因是代码本身的修改成本在指数级上涨。

1.1 T3Code 到底在解决什么问题

T3Code 不是在发明新理论,它只是把代码质量的判断标准压缩成三个可讨论的层级,让团队在评审、重构、排期时有一个统一的口径。这三个层级是这样定义的:

  • T1(交付层):功能正确,能通过测试,能上线运行。
  • T2(维护层):结构清晰,命名准确,注释解释的是“为什么”而不是“是什么”,团队里任何一个人都能在合理时间内定位并修改。
  • T3(资产层):从重复业务里抽出可复用能力,新的需求可以通过组合已有模块快速实现,而不是再复制粘贴一段“相似但不同的逻辑”。

我拿装修来打比方。T1是房子能住人;T2是水电图纸齐全,后期维修不用砸墙;T3是你把装修过程中沉淀的工艺标准、模块化组件固化下来,下一套房能直接复制。大部分项目的问题不是停在T1,而是团队对T1很满意,没人意识到“能住”和“好改、能复制”之间还有两个层级。

为什么这套方法有实际价值?因为一个团队里,不同人对“代码好不好”的判断差异太大了。有人觉得用了设计模式就是好,有人觉得注释多就是好,还有人觉得代码短就是好。T3Code 把讨论从主观审美拉到可验证的维度:这段代码被修改时的成本高不高?被第二个业务复用时需要动几处?新同事接手时要花多久?这些都是可以观察、可以量化的问题。

1.2 代码质量的三种常见误判

推行 T3Code 之前,我留意到团队里存在三种很容易把人带偏的“质量观”,如果不先澄清,后面的重构根本推不动。

  • 误判一:能跑就行。这是最普遍的。代码上线了、测试过了,就认为“质量没问题”。但“能跑”只满足了当前功能场景,下一轮需求来时,如果改动要牵动六个分支、三个全局状态,那这代码的质量隐患已经非常大了。能跑只是底线,不是质量。
  • 误判二:越高级越好。有些同学偏好复杂的工厂模式、责任链、事件驱动,几十行能解决的问题要铺出一个包。抽象本身不是错,错在没有对应真实变更点。如果没有多个业务同时变化,那这些抽象只是在增加读者负担。
  • 误判三:代码量越多越努力。我经常看到一段业务逻辑被复制到多个接口,命名还不太一样。写的人觉得“我完成了三个需求”,实际上只是把同一份混乱复制了三份。工作量不等于价值,重复代码的真实贡献是负的,因为它把修改成本乘以了三。

这三种误判共同导致一个结果:项目像滚雪球一样变重。团队每天在补漏洞,而不是建设能力。T3Code 要做的第一步,就是先打破这些惯性认知,让所有人重新用分层眼光看待手头的代码。

2. T3Code 的三层代码维度:T1交付、T2可维护、T3可演进

这一节我展开讲每一层的具体特征、判断方法和典型场景。你可以拿手头的代码对号入座,看看它目前停在哪个位置。

2.1 T1层:功能交付的底线

T1 层代码的特征是:当前需求完成,测试通过,接口行为符合预期。它可能长这样——一个 800 行函数,从上到下写着 A 条件、B 条件、C 条件,全局变量贯穿始终,但就是能跑。

这一层不是毫无价值。相反,很多场景下我们应该有意识地允许代码停在 T1,比如一次性数据迁移脚本、临时活动页、验证想法的原型。判断标准很简单:这段代码是否只运行一次?如果答案为“是”,T1完全足够,不要过度设计。

但当一段代码会被持续修改、长期运行、多人在上面迭代时,停在 T1 就很危险。它的标志性信号包括:

  • 修改一个优惠规则,需要同时改三处函数;
  • 函数内存在多个隐藏的前后依赖关系,比如先调用 A 才能调用 B;
  • 换一个人来看,半个小时后仍在追踪数据从哪来、改哪会影响哪里。

如果你发现自己每次改这种代码都要反复读上下文、生怕踩雷,那这段代码就是典型的 T1。它最大的问题不是 bug,而是修改成本不可控。

2.2 T2层:别人能接手、六周后还能看懂

T2 是我认为大多数长期项目的及格线。它要求代码不仅满足功能,还要具备一定程度的结构清晰度。我常用的判断方法叫“六周原则”:如果一段代码六周后由另一个人(或者六周后的你自己)来修改,能否在一个小时内定位到所有影响点?能,就说明起码到了 T2。

T2 层代码通常具有几个特征:

  • 函数做一件事,且函数名能说清这件事。不需要靠注释解释“这是在算优惠”,函数名本身就是注释。
  • 变量命名描述业务含义,而不是描述类型。list、data、temp这类名字在 T2 标准下是不合格的,因为它们在丢失业务信息。
  • 注释解释“为什么”,少解释“是什么”。代码本身已经说明它在做什么,注释更应该告诉你“为什么这里不能用别的方式”,比如某个边界条件是因为上游历史字段导致的,这类信息代码读不出来。
  • 依赖方向清晰。上层模块依赖下层模块,而不是所有模块互相依赖,一改全改。
  • 核心路径有单元测试保护。不需要 100% 覆盖,但最容易被反复修改的业务规则一定要有测试兜底。

从 T1 到 T2 的改造,做的不是宏大重构,而是小步整理:拆分长函数、重命名、消除隐藏副作用、把魔法数字提取成有业务含义的常量。这些动作不需要架构委员会审批,日常迭代顺手就能做。

2.3 T3层:业务资产与可复用能力

T3 层是我个人最喜欢的定义:代码不只是为当前需求服务,而成了下一轮业务增长的支撑点。它解决的不是“怎么把这个需求写完”,而是“下一个相似需求能不能用半天而不是一周做完”。

举例来说,团队一开始做 A 商品的价格计算,写了一个calcPriceA函数;后来做 B 商品,又复制粘贴改成calcPriceB;到 C 商品时,如果第三个人再复制一次,这段代码就永远陷入了复制维护的泥潭。T3 的做法是:在第二次或第三次看到重复逻辑时,抽象出一个统一的计价引擎,把不同的计算规则做成可配置的策略,让 A、B、C 各自传入规则参数。这个引擎就是资产:下一个 D 商品接入时,不用再读那三份几乎一致又略有差异的代码。

但 T3 不等于“越多人用越好,不管三七二十一先抽象一层”。我自己的经验是:至少出现两次真实复用需求,再讨论抽象;在第三次出现时,抽象已经刻不容缓。过早的 T3 会变成过度设计,过晚的 T3 会积累出大量重复改造成本。卡在这个节点做判断,才是 T3Code 方法论最有价值的地方。

3. 用T3Code的标准重构一段“能跑但危险”的代码

方法论说多了容易飘,我给你看一段我真实重构过的代码。为了不涉及线上业务,我改写成等价但脱敏的库存计价场景。

3.1 重构对象的真实背景

背景是一个商城服务的订单计价模块。原始代码是早期为了快速上线写的,功能完全正确——普通商品、会员价、优惠券、满减、积分抵扣都能跑通。但问题在结构:500 多行逻辑写在一个函数里,所有规则通过多个if分支和三层嵌套判断完成。

我当时接入一个新需求——周年庆大促需要在满减之后再叠加平台补贴。改动本身不大,但我去查现有逻辑时发现:优惠券、满减、积分的计算散落在不同分支里,中间还夹杂着订单金额的状态变更。改任何一个分支都可能影响其他规则的顺序,而顺序错了,金额算出来就是错的。

简化后的原始代码大概长这样:

def calc_order_amount(user, order): amount = order.total_price if order.has_coupon: amount = apply_coupon(amount, order.coupon) if user.level == "vip": amount *= 0.9 if amount >= 300: amount -= 30 if user.points > 0: points_discount = user.points * 0.01 if points_discount > amount * 0.2: points_discount = amount * 0.2 amount -= points_discount user.points = user.points - int(points_discount / 0.01) order.pay_amount = max(amount, 0) return order

这段代码单独看每一步都对。但你想加“平台补贴”这个新规则时就会发现:它该在满减之前还是之后?优惠券能不能和补贴叠加?积分抵扣是按补贴前金额算还是补贴后金额算?这些问题的答案在代码里根本找不到,因为规则和计算步骤纠缠在一起。

3.2 一步步拆解:从T1到T2再到T3的完整改造

我做的第一件事不是写代码,而是把现有规则“画”成一张变更点清单。我列出的影响点包括:优惠券计算、会员折扣、满减条件、积分抵扣、支付金额汇总。然后我意识到,这里的本质不是“用什么设计模式”,而是把每一步价格修改定义成独立的、可组合的规则。

于是,我从 T1 先跨到 T2,做了第一步:把长函数拆成多个小函数,每个函数只负责一种规则,并且保证单一方向的数据流动。

def apply_member_discount(amount, user): return round(amount * 0.9, 2) if user.level == "vip" else amount def apply_full_reduction(amount): return amount - 30 if amount >= 300 else amount

到这一步,代码已经从“500 行泥潭”变成“一堆清晰的小积木”。但仅仅拆函数还不够,因为各规则的执行顺序仍然写死在主流程里,新规则进来还是要改主流程。想要真正进入 T3,就得把这堆积木变成可配置的策略链。

这是我重构后的核心结构(简化示意):

class PriceRuleChain: def __init__(self, rules): self.rules = rules def apply(self, amount, user, order): for rule in self.rules: amount = rule.apply(amount, user, order) return max(amount, 0) # 使用方通过配置组合规则顺序 rules = [ CouponRule(), MemberDiscountRule(), FullReductionRule(threshold=300, reduction=30), PointsRule(max_ratio=0.2), PlatformSubsidyRule(amount=10), ] order.pay_amount = PriceRuleChain(rules).apply( order.total_price, user, order )

改造后,新增平台补贴只需要写一个PlatformSubsidyRule类,在配置里插入位置,主流程一行不用动。每个规则类内部可以单独做单元测试,即使顺序调错,也只影响该规则的输入输出,不会出现改一行崩三处的情况。

3.3 重构效果量化对比

很多同学会问:这么重构,投入产出比到底值不值?我当时的实际量化结果如下:

对比项重构前重构后
新增一个计价规则的耗时约 1 天(要梳理顺序、排查边界)约 2 小时(写新规则并配置)
核心逻辑单元测试覆盖几乎没有每类规则独立测试,共 40 个用例
新同事理解计算链路的耗时约 1 天,且容易漏分支约 30 分钟,配置即文档
修改一个已有规则的影响范围需要全局排查只影响单个规则类
下个大促叠加 3 个新活动的评估悲观估计 1 周+半天出方案

这组数据不是精确实验,但方向很说明问题:重构的短期成本是一天,但长期节省的是每周、每月的修改时间。对于长期模块来说,从 T1 提升到 T3 绝对是划算的投资。

4. T3Code 在代码评审中的落地方法

好的方法论如果落不进评审,很快就成了墙上的口号。我建议在团队代码评审中用 T3Code 替代那种“整体不错,注意下命名”的泛泛反馈。

4.1 评审时的三个提问,替代泛泛的“写得不错”

在评审一段代码时,我不直接做“好/不好”的判断,而是依次问三个问题:

第一个问题:如果下个月要改这个功能,需要动哪几处代码?如果答案是“很多处,而且分散在不同文件”,那么这段代码就要打 T1 或者 T2 待提升。好的结构应该让一个业务变化集中在一个小范围内完成。

第二个问题:一个新同事读这段代码,最容易在哪个地方走神?走神往往是命名不清、职责混杂或者隐含顺序导致的。我会把这个位置指出来,和作者一起确认是不是存在可以消除的复杂度。

第三个问题:如果有第二个业务需要复用这个能力,你会复制粘贴还是直接调用?如果必须复制,说明当前代码还没有资产化。这时评审里就能聊出一条明确的 T3 改进路径,而不是只丢下一句“抽象不够”。

这三个问题把评审从“挑毛病”变成“找变更点和复用点”。作者听了不会抵触,因为他知道你在帮他想下一轮迭代的路径,而不是指责他上一轮写得太差。

4.2 如何给同事提T3级反馈而不破坏协作

T3Code 推进时最容易翻车的地方,不是标准定得不对,而是反馈方式太生硬。直接说“你这段代码是 T1,太烂了”,等于把技术讨论变成人身否定。

我常用的表达方式是“行为描述+影响说明+替代建议”三步:

  • 行为描述:先描述事实,比如“这个函数里优惠券和积分抵扣的逻辑挤在一个 100 行分支里”。
  • 影响说明:再说明它会带来什么问题,比如“如果下个迭代要在它们之间插入一个平台补贴,判断顺序必须重新理,很容易漏分支”。
  • 替代建议:最后给一个可行方案,比如“我们可以先把这两个规则拆成独立函数,再用规则列表依次执行”。

实际操作中,我会配合一个简化的评审记录格式,帮助团队形成习惯:

代码位置当前层级问题信号建议动作期望提升到
calc_order_amountT1规则与流程耦合拆规则类并配置顺序T3
is_stock_enoughT2命名过于泛化改为is_stock_available_for_skuT2
一次性脚本T1无复用场景保持即可不提升

这种结构化的反馈,能让作者快速理解“问题出在哪、为什么、下一步做什么”,也让评审讨论不再靠情绪和感觉。

5. 推进T3Code时最常踩的坑和我的应对

再好的方法论,落地时都会遇到对抗。我在团队里推 T3Code 这几个月,踩过三个比较有代表性的坑。

5.1 过度抽象的陷阱

一开始我们热情很高,看到两处相似代码就急着抽公共方法,结果抽出来一个参数多到离谱的“万能函数”:process_data(data, type, config1, config2, flag1, flag2...)。调用方确实省了重复代码,但任何人想看懂这个函数,都得先理解六个参数之间的组合关系。抽象不仅没让代码变简单,反而多了一层理解负担。

我的应对原则是:至少两次真实复用才抽象,第三次出现时立即抽象。如果只出现过一次,给它写一个清晰的复制版本没问题;如果出现了三次,复制就是技术债。另外,每个抽象都必须对应业务上的一个真实变化点。否则,这种抽象就是没有灵魂的漂亮结构。

5.2 “T3评审标准”变成打压工具

第二个坑比较微妙。团队里有的资深同学学会了 T3Code 之后,开始在评审里拿 T3 标准要求每一段代码,甚至看到新人的一次性脚本也要求“设计成规则引擎”。结果新人不敢提交代码,觉得怎么都达不到标准。

我突然意识到,T3Code 被误解成了“最高标准就是唯一标准”,而它本来的定位是分层判断,有选择地确定目标层级。后来我在团队里明确了一条规则:评审时要区分“必须 T2”和“鼓励 T3”。核心链路、多人长期维护的模块,T2 是底线,T3 视复用需求而定;一次性脚本、原型代码,T1 完全可接受。评价代码质量前,先看它所在上下文的生命周期和复用价值。

5.3 自己实践后的几点心得

最后分享几个个人心得,都是踩坑换来的。

第一,T3Code 不适合一次性推动全项目重构。我建议先挑一个你们团队最常改、最头痛的模块,打上当前层级标签,再用一个迭代的时间做局部改造。等大家尝到了“改需求不用加班”的甜头,自然会往下推进。

第二,T3 的抽象要配置化、文档化,不然就是空中楼阁。我们团队最终把规则链改成了 JSON 配置,配置即业务文档。测试用例写清楚每种规则组合的前置条件和期望结果,后续新同学不需要问老员工“你们这里顺序是怎样的”,看配置和测试就明白了。

第三,衡量代码好不好的最终标准,永远是团队修改它的平均成本。T1、T2、T3 说到底只是用来看清这个成本的辅助标签。如果有一天大家都不需要这套标签,也能凭直觉把代码结构改顺,那就是它真正成功的时候。

我个人现在写代码时会习惯性问自己一句:“一个月后的我,修改这里需要多久?”这个问题比任何规范都管用。你也不妨从手头最常改动的那段核心逻辑开始,给它打一个层级标签,然后想一想:下个需求进来时,你希望面对的是哪一层代码。如果你的答案不是恰好在当前那一层,那现在就是开始动手的最好时机。