摘要
1) 一句话总结 本文总结了通过严格的代码审查、系统设计前置、逐步重构老项目以及定期偿还技术债务等最佳实践,来有效提升团队代码质量与开发效率的实战经验。
2) 关键要点
- 代码审查与自动化:坚持在代码合并前进行审查,并引入 CI、Lint 工具和自动化测试(单元与集成测试)来辅助检查命名、格式及功能破坏问题。
- PR 管理与紧急处理:要求拆分小 PR 以降低审查难度;对于因工期紧急上线的低质量代码,必须创建 Ticket 跟踪改进,其优先级等同于正式功能。
- 系统设计前置:编码前需输出简单的设计文档和结构图,并通过设计评审会议对齐团队思路,避免后期代码偏离规范或被推翻重写。
- 老项目渐进式重构:拒绝一刀切推翻重写,采取先补充自动化测试(尤其是集成测试),再逐个模块替换的策略(如建立新旧项目共用的组件库以实现平滑过渡)。
- 技术债务常态化管理:将技术债务转化为 Ticket,并在每个 Sprint 中固定分配约 20% 的时间用于偿还。
- Sprint 周期分配创新:采用两周迭代模式,第一周专注产品需求开发与测试部署,第二周集中处理 Bug 修复、技术债务、新技术测试及公共组件开发。
- 确立并执行最佳实践:架构设计完成后,由设计者先实现基础模块作为团队参考标准;在代码审查阶段果断拒绝不符合最佳实践的代码。
3) 风险与缺口
- 重构执行阻力:重构往往面临工期不足、容易复发以及个人能力受限等现实挑战。
- 人工审查局限性:纯靠肉眼审查难以发现格式规范问题,且面对大 PR 或缺乏自动化测试覆盖的代码时,审查压力大且合并风险高。
- 重写引发抵触:在缺乏前期设计的情况下,直接要求开发者推翻重写已实现的功能,容易引发强烈的抵触情绪。
- 老项目迁移风险:老项目整体替换风险极高,若无自动化测试兜底,任何修改都可能引入严重问题。
正文

烂代码是一个有趣而值得深思的话题。在 Martin Fowler 的经典著作《重构:改善既有代码的设计》中,他专门提到了代码异味(code smells),并指出:“代码异味是一种表象,通常对应系统中更深层次的问题。”对此我深有体会。
如果你的系统充斥着烂代码,就要反思其中的深层次原因:需求过于频繁变动?项目进度压力大?团队技能不过关?缺乏设计?或许这些因素都有所贡献。Martin Fowler 给出的解决方案是重构,这无疑是很好的建议,但执行起来却充满挑战:
-
我想重构,但没有工期怎么办?
-
重构完成后,会不会没多久又回到老样子?
-
我个人没有足够的能力去重构!
这些问题不仅仅是技术层面,还涉及软件工程和项目管理。我刚开始写代码时,也写过很多烂代码。后来见识过好的代码风格,逐渐提升了自己代码的质量,直到带团队时才真正意识到如何解决烂代码问题。我现在带领的团队代码质量非常出色,即便有新手程序员,也能确保烂代码不混入,产出效率显著高于其他团队,一个功能实现速度往往是其他团队的 1.5 到 2 倍,甚至更快。
代码审查:严格执行与自动化工具的结合
最初,我尝试通过严格的代码审查来提升团队代码质量。所有更新必须经过代码审查才能合并到主干。要注意的是,审查应在合并前,而不是事后,这样才能避免修改的优先级被降低,影响质量。
严格的代码审查确实有效,但也面临不少挑战:
-
代码命名和格式不符合规范,靠肉眼很难完全审查到。
-
PR(Pull Request)太大,审查压力很大。
-
工期紧急时,质量不高的代码如何处理?
-
自动化测试覆盖不足,合并时心里没底。
-
代码结构混乱,但功能实现没问题,该不该批准?
-
原本的项目就是“屎山”代码,新代码难以彻底改善。
为此,我逐步引入了工具化和自动化手段来辅助代码审查:
-
源代码管理和代码审查工具,清楚展示代码改动。
-
CI(持续集成),每次提交代码都通过 CI 运行自动化测试。
-
Lint 工具,自动检查命名、格式等问题。
-
自动化测试,包括单元测试和集成测试,确保每次更新不会破坏现有功能。
此外,拆小 PR 也是关键,越小的 PR 越容易审查,质量越有保障。而对于那些急着上线的低质量 PR,我要求必须创建 Ticket 跟踪后续改进,这些改进任务的优先级等同于其他正式功能,以确保问题尽快被解决。
系统设计:先磨刀再砍柴
我还遇到过这样的情况:开发很快实现了某个功能,代码质量也不算太差,但由于没有遵循最佳实践,导致整体可读性和可维护性差。推翻重写往往会让开发者产生强烈抵触情绪,简单修改也意义不大。
后来,我找到了一个更好的方法:在写代码前,先做系统设计。通过简单的设计文档和结构图,在设计评审会议中对齐团队思路,这样在代码实现时就不会出现大偏差,也避免了重写的困境。这种做法不仅是“磨刀不误砍柴工”,还让团队成员在设计评审中学习如何做好设计和遵循最佳实践。
技术债务与老项目的重构
对于那些需要长期维护的老项目,我的策略是逐步重构而非一刀切推翻重写。首先补充自动化测试,尤其是集成测试,以确保后续的任何修改都不会引入严重问题。然后逐个模块替换,降低整体迁移的风险。例如,我最近用 NextJS 重构一个重要的前端项目,不是先写完新项目再整体替换,而是先建立组件库,让新旧项目共用组件,一步步逐渐替换和测试,最终实现平滑过渡。
技术债务也需定期偿还
技术债务并不完全是坏事,但必须有计划地偿还。我的策略是将技术债务任务创建成 Ticket,并在每个 Sprint 中分配约 20% 的时间去处理。这样可以有效地避免技术债务积累到必须大规模“清仓”的地步。
我们最近还探索了一种新的开发模式:在两周的 Sprint 中,第一周专注于产品需求开发并部署到测试环境,第二周主要修复新功能 Bug,并有足够的时间进行技术任务,例如偿还技术债务、测试新技术栈和开发公共组件。这种模式既能持续交付产品功能,也能保证技术任务的推进,得到了产品经理的认可。
最佳实践:为新手提供清晰指引
新项目或已有项目要有明确的最佳实践供新手参考,并确保团队成员共同遵守。如果是我自己设计的架构,我会在架构设计后先实现一些基础模块,形成良好的开发实践,这样其他开发者可以“照葫芦画瓢”。对于不符合最佳实践的代码,必须在代码审查阶段果断拒绝。
总结
-
代码审查:一定要先审查再合并。
-
系统设计:坚持先做系统设计再进行评审。
-
自动化测试:代码必须有自动化测试覆盖。
-
技术债务:定期偿还,保持在良性水平。
-
老项目重构:逐步替换,降低风险。
-
最佳实践:为团队提供清晰的开发标准,共同遵守。
这些方法帮助我的团队有效摆脱了烂代码的困扰,提升了整体开发效率和代码质量。
注:本文是2年多前在微博 https://www.weibo.com/1727858283/OAlQiEHhr 首发的,用 Canvas 帮忙润色了一下重发的,效果挺不错的。