资讯动态

t3code实战:三条核心原则提升代码可维护性

发布时间:2026/10/9 12:16:12 来源:尧图企业网站定制
1. 项目缘起与核心定位第一次看到t3code这个名字我下意识地把它拆成了t3和code两截。在开发者圈子里这种命名方式其实挺常见——前缀往往代表某种技术栈、某个版本号或者干脆就是作者随手起的一个短标识后缀则直接点明用途。我翻了一圈社区里的讨论发现大家对这个词的联想基本集中在几个方向有人觉得它跟TypeScript有关有人猜是某个代码生成工具的代号还有人把它当成一套轻量级编码规范的简称。不管最初的含义是什么这个词本身已经成了一个不错的切入点让我可以聊聊围绕代码这件事一个普通开发者到底能折腾出多少实用的东西。我写这篇东西的出发点很简单市面上讲代码规范、讲工具链的文章太多了但大部分要么是官方文档的复述要么是堆砌一堆配置让人照抄。我想换个方式把我自己在实际项目里踩过的坑、试过的方案、最后沉淀下来的那套做法原原本本地讲一遍。这套做法我内部就叫它t3code——三个核心原则加一套落地流程不追求大而全只求能跑通、能维护、能交接。这篇文章适合谁看如果你是一个正在带小团队的技术负责人或者是一个独立开发者手头有几个中小型项目需要长期维护那你应该能从里面找到不少共鸣。如果你刚入行不久对代码组织还处于能跑就行的阶段那这篇文章可能会帮你省下不少后期重构的时间。我不打算讲什么高深的理论全是实操层面的东西你可以直接拿去用也可以根据自己项目的情况做调整。2. 为什么是三个原则而不是一堆规范2.1 规范越多执行越差我待过一家公司代码规范文档写了四十多页从命名风格到注释格式到提交信息模板事无巨细。结果呢新项目前两周大家还照着做第三周开始就有人图省事直接复制粘贴旧代码一个月后整个仓库的风格就彻底放飞了。这件事给我的教训特别深规范的数量和执行率之间存在一个明显的反比关系。你定的规则越多单条规则被记住的概率就越低最后大家干脆全部放弃。后来我自己带项目就给自己定了一条死规矩核心原则不超过三条。三条的好处是任何人听完都能记住不需要查文档。而且三条原则之间可以互相制衡不会出现遵守了A就违反了B的情况。t3code里的t3其实就是这个意思——three principles三个原则。至于具体是哪三个我下面会展开讲但你先记住这个逻辑少即是多能记住的规范才是好规范。2.2 原则要能指导决策而不是描述状态很多规范的问题在于它们描述的是好代码长什么样而不是遇到选择时该怎么选。比如代码要清晰易读这句话听起来很对但当你面对一个复杂的业务逻辑有两种写法摆在面前你该怎么判断哪种更清晰这种规范就是无效的因为它没法指导具体决策。我定的三个原则每一条都是一个决策框架。遇到分歧的时候拿这三条去套基本都能得出一个大家都能接受的结论。这比争论哪种写法更好看要高效得多。而且这三条原则是有优先级的当它们冲突的时候优先级高的那条说了算。这个优先级顺序本身也是经过好几次调整才定下来的后面我会讲为什么这么排。2.3 原则要能落地成检查项光有原则还不够你得有办法检查。我的做法是每条原则都对应一组可以自动检查的规则。比如命名要能自解释这条原则对应的检查项就是变量名长度是否在合理区间、是否包含无意义的缩写、是否与同文件内其他命名风格一致。这些检查项可以写成lint规则也可以做成代码审查时的检查清单。关键是检查项必须具体到是/否的程度不能有模糊地带。我见过太多团队把规范挂在墙上但代码审查的时候全靠 reviewer 的个人喜好。这种做法的结果就是同一个问题张三 review 的时候通过了李四 review 的时候被打回来搞得提交代码的人无所适从。有了明确的检查项reviewer 只需要对照清单打勾争议就少了很多。3. 第一条原则命名即文档3.1 为什么命名排在第一位如果让我从所有编码习惯里挑一个最重要的我会毫不犹豫地选命名。原因很简单代码里出现频率最高的元素就是各种名字——变量名、函数名、类名、文件名、参数名。这些名字构成了阅读代码时的第一印象。一个糟糕的命名会让读者在理解代码逻辑之前先花大量时间去猜测这个变量到底是干嘛的。我做过一个粗略的统计在一个中等规模的项目里开发者阅读代码的时间大约是编写代码时间的三到五倍。而阅读过程中超过一半的困惑都来自命名不当。换句话说你在命名上多花一分钟可能帮未来的自己和其他人省下十分钟。这个投入产出比比任何性能优化都要高。3.2 命名的三个层次我把命名分成三个层次来要求从低到高分别是准确、具体、自解释。准确是最低要求。变量名要能正确反映它存储的内容函数名要能正确反映它执行的操作。听起来很简单但实际项目中datainfotempresult这类词满天飞。这些词的问题在于它们太泛了读者看完之后只知道这里有个东西但不知道这个东西是什么。我的做法是在代码审查时看到这类词直接打回去要求重命名。一开始大家会觉得麻烦但坚持两周之后整个团队的命名质量会有明显提升。具体是第二层要求。比如一个函数叫processData它确实准确——它确实在处理数据。但处理这个词太模糊了是过滤、排序、聚合还是转换读者必须去看函数体才能知道。更好的命名是filterActiveUsers或者sortByCreateTime这样读者不看实现就能知道这个函数干什么。具体化的关键在于用动词精确描述操作用名词精确描述对象。自解释是最高要求。一个好的命名应该让读者不需要看上下文就能理解它的含义。比如userList和activeUserList后者就比前者更自解释因为它隐含了只包含活跃用户这个信息。再比如calculateTotalPrice和calculateTotalPriceWithTax后者明确说明了是否含税避免了调用方的猜测。自解释的命名往往会长一些但这点长度换来的是阅读效率的提升非常值得。3.3 命名的常见陷阱与规避方法第一个陷阱是缩写。很多开发者为了省几个字符把button写成btn把manager写成mgr把configuration写成cfg。这些缩写在你写代码的当下可能觉得很顺手但过两个月你自己回来看可能都要愣一下才能反应过来。我的建议是除了行业公认的缩写比如idurlhttp其他一律写全称。现在的编辑器都有自动补全多打几个字符的成本几乎可以忽略。第二个陷阱是拼音和英文混用。这个在中文开发者里特别常见比如getYongHuList这种。这种命名的问题在于它既不符合英文习惯也不符合中文习惯读起来非常别扭。我的做法是要么全英文要么全拼音绝对不混用。如果英文实在想不出合适的词用拼音也比混用好至少风格是统一的。第三个陷阱是数字后缀。比如user1user2user3这种命名在临时脚本里还能忍但在正式项目里就是灾难。读者看到user2的时候完全不知道它和user1的区别是什么。正确的做法是用有意义的限定词来区分比如adminUserguestUservipUser。3.4 命名检查的自动化方案光靠人工审查命名效率太低。我的做法是配置一套lint规则把常见的命名问题自动检测出来。比如用ESLint的id-length规则限制变量名长度用camelcase规则强制驼峰命名用自定义规则检测黑名单词汇如datainfotemp。这些规则可以在提交代码时自动运行不通过就拒绝提交。对于更复杂的命名问题比如是否自解释自动化工具很难判断。我的做法是维护一个团队内部的命名词典把项目中常用的业务概念和对应的标准命名列出来。新人在命名之前先查词典找不到再自己起名起完名之后补充到词典里。这样既保证了命名的一致性又降低了新人的学习成本。4. 第二条原则函数只做一件事4.1 单一职责的实操定义函数只做一件事这个说法相信很多人都听过。但什么叫一件事这个定义太模糊了。我见过有人把一个函数拆成五个小函数结果每个函数只有两行代码调用链长得像迷宫。这不是单一职责这是过度拆分。我的定义是这样的一个函数只做一件事意味着你能够用一句不含并且的话来描述它的功能。比如验证用户输入是一件事验证用户输入并且保存到数据库就是两件事。这个定义的好处是它给了你一个明确的判断标准。当你发现描述函数功能的时候需要用并且来连接那就说明这个函数该拆了。但这里有个前提拆出来的子函数必须是有意义的。如果拆出来的函数只是把原来的一行代码包了一层那这种拆分就没有价值。判断标准是拆出来的函数是否可以被独立测试、是否可以被复用、是否有明确的输入输出。如果三个答案都是否那就不值得拆。4.2 函数长度的合理区间关于函数长度业界有很多说法有人说不超过20行有人说不超过50行。我的经验是不要死守一个数字而是看这个函数是否容易理解。一个30行的函数如果逻辑是线性的、命名清晰的读起来可能比一个10行的嵌套函数更轻松。不过有一个信号值得警惕当你需要滚动屏幕才能看完一个函数的时候这个函数大概率太长了。我的做法是把函数长度控制在一屏之内大概40到60行。超过这个长度就考虑拆分。拆分的依据不是行数而是逻辑层次。比如一个函数里既有参数校验、又有业务处理、又有结果组装那就可以按这三个层次拆成三个函数。4.3 参数设计的讲究函数的参数列表是很多人容易忽略的地方。我见过一个函数有八个参数调用的时候要对着文档一个一个填填错一个就出bug。这种函数的设计就有问题。我的原则是参数不超过四个。超过四个的时候考虑两种方案一是把相关的参数合并成一个对象二是重新审视这个函数是不是做了太多事。合并成对象的做法特别适合那些总是成对出现的参数比如startDate和endDate把它们合并成一个dateRange对象既减少了参数数量又明确了这两个参数之间的关系。另外参数的类型要尽量具体。比如一个函数接收id参数如果这个id是用户id那就命名为userId类型也应该是UserId而不是string。这样调用方在传参的时候类型系统就能帮他检查出错误。这个做法在TypeScript项目里特别有效我强烈建议所有新项目都上TypeScript光是参数类型检查这一项就能省下大量调试时间。4.4 副作用的隔离与处理纯函数是理想状态但实际项目里完全不产生副作用的函数几乎不存在。读写数据库、调用接口、修改全局状态这些都是副作用。我的做法不是消灭副作用而是隔离副作用。具体来说我会把业务逻辑和副作用分开。业务逻辑写成纯函数输入输出明确方便测试。副作用则集中在一个薄层里比如一个repository层负责数据库操作一个service层负责接口调用。这样业务逻辑的测试不需要mock任何东西直接传参调函数看返回值就行。而副作用层的代码因为很薄测试起来也简单。这个做法还有一个好处就是当副作用需要替换的时候改动范围很小。比如从MySQL换成PostgreSQL只需要改repository层的实现业务逻辑完全不用动。这种架构上的清晰在项目初期可能感觉不到好处但到了中期需要换技术栈或者做重构的时候优势就非常明显了。5. 第三条原则错误要早暴露5.1 为什么错误处理比错误预防更重要很多开发者把大量精力花在预防错误上比如做各种参数校验、加各种边界判断。这当然没错但我的经验是错误处理比错误预防更重要。原因在于你永远无法预防所有错误。网络会断、磁盘会满、第三方接口会挂这些都不是你能控制的。与其试图预防一切不如确保错误发生的时候能被及时发现、快速定位。早暴露的核心意思是错误发生的位置要尽可能接近错误产生的位置。比如一个参数校验失败应该在函数入口就报错而不是等到这个参数被用到的时候才报错。前者你能立刻知道是调用方传错了后者你可能要排查半天才能找到源头。5.2 快速失败的具体做法快速失败fail fast是早暴露的具体实现。我的做法是在每个函数的入口处做参数校验不合法就直接抛异常。这个异常要包含足够的信息哪个参数不合法、期望什么类型、实际收到什么值。这样调用方看到异常信息立刻就知道问题出在哪里。对于异步操作快速失败同样适用。比如一个接口调用如果返回的状态码不是200应该立刻抛异常而不是继续往下走。我见过很多代码接口调用失败之后不报错继续用undefined往下算最后在一个完全不相干的地方报了一个莫名其妙的错误。这种调试体验非常糟糕。5.3 错误信息的编写规范错误信息是给谁看的很多人下意识觉得是给用户看的所以写得很委婉比如操作失败请稍后重试。但实际上错误信息的第一读者是开发者。用户看到的应该是友好的提示而开发者看到的应该是详细的错误信息。我的做法是错误信息里必须包含三个要素什么操作失败了、失败的原因是什么、可以怎么解决。比如读取配置文件失败文件不存在请检查路径是否正确。这样的错误信息开发者一看就知道该去检查文件路径。而用户看到的则是另一套文案比如系统配置加载失败请联系管理员。在代码层面我会区分两种错误一种是可预期的业务错误比如用户不存在这种错误用自定义的Error类来抛携带错误码和详细信息另一种是不可预期的系统错误比如数据库连接失败这种错误直接抛原始异常让上层去决定怎么处理。这个区分很重要因为业务错误通常需要展示给用户而系统错误通常需要记录日志并告警。5.4 日志与错误的配合错误要早暴露但暴露之后得有记录。我的做法是在错误抛出的地方不打日志在错误被捕获处理的地方打日志。这样做的原因是错误在抛出的时候往往还没有足够的上下文来判断严重程度。而到了捕获处理的地方已经知道这个错误对业务流程的影响了这时候打日志才能打出有价值的信息。日志的级别也要讲究。业务错误用warn级别系统错误用error级别。warn级别的日志不需要立刻处理但需要定期回顾看看是不是有频繁发生的业务异常。error级别的日志则需要立刻关注通常要配告警。我见过一些项目所有错误都打error级别结果告警天天响大家就麻木了真正的严重问题反而被淹没。6. 从原则到落地一套可执行的检查流程6.1 提交前的自检清单原则再好不执行也是白搭。我的做法是在代码提交之前让开发者自己过一遍检查清单。这个清单不用太长五到十条就够了对应三条原则的具体检查项。比如变量名是否准确、具体、自解释函数是否只做一件事能否用一句不含并且的话描述参数是否超过四个超过的话是否合并成了对象错误是否在发生位置附近被抛出错误信息是否包含操作、原因、解决方案三要素这个清单我会放在项目的README里也会做成提交模板的一部分。开发者提交代码的时候模板会自动带出这个清单他需要逐项确认。一开始大家会觉得繁琐但养成习惯之后整个过程也就多花一两分钟换来的是代码质量的明显提升。6.2 代码审查的聚焦点代码审查最怕的就是漫无目的地看。我的做法是审查者只关注三件事命名是否达标、函数职责是否单一、错误处理是否到位。其他方面比如代码风格、格式问题全部交给自动化工具去处理。这样审查者的精力集中在真正重要的事情上审查效率会高很多。审查意见的写法也有讲究。我要求审查者不说这里不好而是说这里违反了哪条原则建议怎么改。比如这个函数名processData不够具体建议改成filterActiveUsers因为它实际做的是过滤活跃用户。这样的意见提交者知道该怎么改也知道为什么要改下次遇到类似情况就能自己判断了。6.3 自动化工具的配置要点自动化工具是执行原则的保障。我的项目里通常会配置这几类工具格式化工具如Prettier负责统一代码风格lint工具如ESLint负责检查命名和潜在错误类型检查工具如TypeScript负责检查类型安全测试工具如Jest负责验证业务逻辑。这些工具的配置有一个原则规则要少而精。我见过有人把ESLint的所有规则都打开结果代码里全是红色波浪线开发者干脆把lint关掉了。我的做法是只开启与三条原则直接相关的规则其他规则一律关闭。这样lint报出来的问题都是真正需要关注的开发者也不会觉得被工具绑架。6.4 新人上手的引导流程新人加入项目的时候最怕的就是面对一堆规范不知道从哪下手。我的做法是给新人安排一个引导任务让他用t3code的原则去重构一个小模块。这个模块不用太复杂一两百行代码就够了。重构的过程中他会自然地理解三条原则的含义也会遇到各种具体问题这时候再给他讲规范效果比干巴巴地念文档好得多。引导任务完成之后我会让新人做一次分享讲讲他在重构过程中遇到了哪些问题、是怎么解决的。这个分享既是检验也是让老成员回顾原则的好机会。我试过几次效果都不错新人上手速度明显比放养式快很多。7. 常见问题与排查技巧实录7.1 命名相关的高频问题问题一想不出合适的英文名怎么办这是中文开发者最常遇到的问题。我的建议是先查团队内部的命名词典看看有没有现成的。如果没有可以用在线词典查但要注意查到的词是否准确。如果实在找不到合适的词用拼音也比用错误的英文好。另外可以养成收集命名的习惯看到好的命名就记下来时间长了就有自己的命名库了。问题二命名太长影响可读性怎么办长命名确实会影响可读性但前提是它真的长到影响阅读了。我见过有人把getUserById改成getUser理由是后者更短。但getUser的含义不明确是获取当前用户还是根据id获取用户这种为了短而牺牲明确性的做法得不偿失。我的判断标准是如果命名长度超过30个字符才考虑简化。30个字符以内的命名明确性优先。问题三团队命名风格不统一怎么办这个问题通常出现在多人协作的项目里。我的做法是在项目初期就定好命名风格写进README并且配置lint规则强制检查。如果项目已经进行到一半才发现风格不统一那就先统一新代码的风格旧代码在重构的时候逐步改。不要试图一次性改完所有旧代码那样风险太大。7.2 函数拆分相关的高频问题问题一拆出来的函数太多调用链太长怎么办这是过度拆分的典型症状。我的判断标准是如果一个函数的调用链超过三层就要考虑合并一些中间层。合并的原则是把那些只被调用一次、逻辑简单的函数合并回调用方。但要注意合并之后函数不能太长如果合并后超过60行那就说明拆分本身是合理的问题出在别的地方。问题二函数之间有共享状态怎么办共享状态是函数拆分的大敌。我的做法是把共享状态显式地作为参数传递而不是通过闭包或者全局变量来共享。这样每个函数的输入输出都是明确的测试起来也方便。如果参数太多就把共享状态打包成一个context对象作为第一个参数传递。这个做法在React的useReducer里很常见效果很好。问题三异步函数怎么保持单一职责异步函数确实容易变得复杂因为要处理各种回调、Promise链。我的做法是把异步操作和业务逻辑分开。异步操作放在一个薄层里只负责发起请求和返回结果。业务逻辑写成同步的纯函数接收异步操作的结果作为输入。这样业务逻辑的测试不需要处理异步简单很多。7.3 错误处理相关的高频问题问题一错误信息太详细会泄露敏感信息怎么办这是个好问题。我的做法是错误信息分两层一层是给开发者看的详细版包含堆栈、参数值等另一层是给用户看的简化版只包含必要的提示。在代码里抛错误的时候抛详细版在展示给用户之前转换成简化版。这个转换通常在一个统一的错误处理中间件里做不需要每个地方都写。问题二错误被吞掉了怎么办错误被吞掉是调试的噩梦。我的做法是在代码审查时特别关注catch块。如果catch块里只有一行console.log或者干脆是空的直接打回去要求处理。处理的方式可以是重新抛出、可以是转换成业务错误、可以是记录日志并返回默认值但绝对不能什么都不做。问题三错误日志太多找不到重点怎么办日志太多通常是因为打日志的位置不对。我的做法是只在错误被处理的地方打日志不在错误抛出的地方打。另外日志要分级业务错误用warn系统错误用error。定期回顾warn日志看看有没有频繁发生的业务异常需要优化。error日志则要配告警确保严重问题能被及时发现。7.4 工具配置相关的高频问题问题一lint规则太严开发者抵触怎么办规则太严确实会引起抵触。我的做法是只开启与核心原则相关的规则其他规则一律关闭。另外规则的严重级别可以调整有些规则设为warning不阻塞提交有些规则设为error阻塞提交。这样既保证了核心原则的执行又不会让开发者觉得被过度约束。问题二自动化工具运行太慢怎么办工具运行慢通常是因为检查的范围太大。我的做法是只检查变更的文件而不是整个项目。这个可以通过配置lint工具和测试工具来实现。另外可以把检查放在提交前的钩子里而不是每次保存都运行。这样既保证了检查的执行又不会影响开发效率。问题三工具配置在不同开发者机器上不一致怎么办这个问题通常是因为配置没有纳入版本管理。我的做法是把所有工具的配置文件都提交到仓库里包括lint配置、格式化配置、编辑器配置。另外用Docker或者类似的容器化方案来统一开发环境确保每个人用的工具版本一致。这样就能避免在我机器上能跑的问题。8. 一些个人体会这套t3code的做法我前后在三个项目里用过每次都会根据项目情况做一些调整。最大的感受是原则要少执行要严。三条原则听起来简单但真正坚持下来并不容易。特别是在项目赶进度的时候很容易就想这次先这样下次再改。但经验告诉我这种妥协一旦开始就会越来越多最后原则就形同虚设了。另一个感受是工具是辅助人才是核心。再好的lint规则也检查不出命名是否真正自解释再完善的检查清单也替代不了代码审查时的判断。所以我在团队里一直强调原则是给大家一个共同的判断框架而不是替代思考。遇到原则覆盖不到的情况大家讨论决定然后把结论补充到检查清单里。这样原则本身也在不断进化越来越贴合项目的实际情况。最后分享一个小技巧我会在项目里维护一个反例集把违反原则的代码片段收集起来配上说明和修改建议。新人入职的时候先看这个反例集比看正面示例学得快。因为人天生对错误更敏感看到反例的时候会想哦原来这样写是不行的印象比看正面示例深刻得多。这个反例集我一般放在项目的wiki里每次代码审查发现典型问题就补充进去时间长了就成了一本很实用的教材。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑