写点什么

十年老代码库的生存指南:如何在“屎山”中优雅地工作?

  • 2025-02-12
    北京
  • 本文字数:2895 字

    阅读完需:约 9 分钟

大小:1.40M时长:08:10
十年老代码库的生存指南:如何在“屎山”中优雅地工作?

身为一名软件工程师,在大型现有代码库中工作是最让人头痛的情况之一。首先我们没法事先演练(任何开源项目在参与体验上都跟这不一样),个人项目也因为体量不大、从头开始而很难体现这种“历史包袱”的沉重感。顺带一提,我这里所说的大型现有代码库,是指那些:

 

  • 拥有几百万行代码(比如说 500 万行);

  • 有约 100 到 1000 名工程师在同一套代码库上工作;

  • 代码库的最早工作版本至少有十年历史的项目。

 

我自己在这类代码库上已经工作了十年。下面聊聊那些我真希望年轻时自己能够知晓的经验和心得。

最大的问题来自不一致性

 

我最深看到的一个致命问题,就是忽略掉代码库的其余部分,只考虑以最合理的方式实现当前功能。换句话说,刻意限制与现有代码库的接触点,以此保证自己的干净代码不受遗留垃圾的污染。对于主要在小型代码库上工作的工程师们来说,这是种很难改掉的习惯——但大家必须努力克服!事实上,为了保持一致性,我们不仅要拥抱遗留代码,还得尽量深入地研究遗留代码库。

 

为什么一致性在大型代码库中如此重要?因为它能保护我们免受令人讨厌的意外影响、减缓代码库陷入混乱的速度,并允许各位充分运用未来可能出现的改进功能。

 

假设我们正在为特定类型的用户构建 API 端点,大家可以在端点中旋转一些“如果当前用户不属于此类型,则返回 403 错误”的逻辑。但在动手之前,我们应该先查看代码库中的其他 API 端点在身份验证中发挥何种作用。如果他们使用到一些特定的帮助程序集,那我们也应该使用该帮助程序(哪怕它笨拙、难以集成甚至对于当前用例来说有点用力过猛)。总之,我们必须得控制住自己想要让代码库中的某个小角落变得更加简洁优雅的冲动。

 

这样做的主要原因,在于大型代码库中往往埋有很多暗雷。例如,你可能不知道代码库中其实存在“机器人”的概念,它跟普通用户类似但又不完全一样,需要特殊处理才能进行身份验证。我们可能不知道代码库中的内部支持工具允许工程师偶尔以用户的角色进行身份验证,而这需要特殊处理才能顺利通过。


总之,大型代码库里肯定还有无数我们难以、甚至根本不可能知晓的情况。现有功能则代表了一条能够穿越雷区的安全路径。如果已经有某个长期存在的 API 端点在以某种方式进行身份验证,那请务必按照同样的路径进行操作,这样我们至少能保证不会被那些暗雷给误伤到。

 

最重要的是,缺乏一致性正是长期以来困扰大型代码库的核心威胁,因为其导致项目无法进行任何普适性的改进。仍然以之前提到的身份验证为例,如果我们想要引入一种新的用户类型,高度一致的代码库使我们能够直接更新现有身份验证助手集以适应这种需求。


但对于不一致的代码库,不同 API 端点所执行的操作各不相同,我们必须去亲自更新并测试每个实现。实际上,这通常足以把变化扼杀在摇篮当中,或者至少会把更新难度最大的那 5%功能剔除出应用范围——这反过来又会进一步降低一致性,因为现在我们又多了一种只适用于大多数、但并非支持所有 API 端点的用户类型。

 

所以说,当我们决定认真在大型代码库中搞开发时,应当先深入研究现有技术、并尽可能遵循其基本逻辑。

除了一致性,还有什么重要问题?


一致性就是最重要的,但除此之外,我们也可以快速过一下其他要点:

 

我们需要对服务在实践中的使用方式(即用户用法)拥有深入了解。比如哪些端点的访问频率最高?哪些端点的重要性最高(即由付费客户使用且无法优雅降级的端点)?服务必须遵循哪些延迟保证,而哪些代码运行在热路径之中?大型代码库中的另一个常见错误,就是进行“微调”——因为这类调整往往会意外触及关注流程的热路径,进而引发大麻烦。

 

我们绝不能像在小型项目中那样过度依赖自己在开发中测试代码的能力。任何大型项目都会随时间推移而积累下诸多状态(例如,您觉得 Gmail 需要支持多少种用户?)。到了特定的时间点,即使借助自动化手段,我们也不可能测试每一种状态组合。相反,我们能测的就只有关键路径,因此请谨慎编码并领先缓慢发布和持续监控来不断发现问题。

 

另外,请尽量不要引入新的依赖项。在大型代码库中,新增代码往往会永远存在。依赖项会带来持续的安全漏洞与更高的软件包更新成本,而这些成本几乎肯定会超过特定人员在公司内的任期。即使确有必要,也请确保选择广泛使用且稳定可靠的依赖项,或者是那些在必要时易于分叉的依赖项。

 

出于同样的理由,一旦有机会可以删减一部分代码,也请务必牢牢把握。这是大型代码库中最危险的工作之一,所以切忌半途而废:首先对代码进行梳理检测以识别生产中的调用者,并将其降至零,这样才能确保可以安全删除。但正如减重对于肥胖人群特别重要,在大型代码库中也没有什么能比安全删除代码更具现实价值。

 

将工作拆分成多个小型 PR 提交,并通过预加载引导其他团队的代码变更。这一点在小型项目中也有体现,而在大型代码库这边则可谓至关重要。这是因为我们往往会依赖于其他团队中不同领域专家做出的问题预测(毕竟大型项目太过复杂,没有单独哪方能够准确预测所有情况)。如果能够将风险区域的变更保持在较小且易于理解的范围之内,那么这些领域专家就更有可能注意到问题并避免引发事故。

为什么非得接手历史包袱?

 

最后,我想花点时间强调这些遗留代码库的意义。相信大家都听说过这样一个常见的观点:

 

为什么非要接手遗留下来的一团乱麻?花时间深入研究错综复杂的代码结构和业务逻辑根本就没有价值。面对庞大的现有代码库,我们的思路应该是拆分出一个个更小、更优雅的服务来进行精简,而不是身陷其中进一步令混乱变得更乱。

 

我认为这种说法完全错误。主要原因在于,一般来讲大型现有代码库会产生 90%的价值,任何大型科技企业、大部分创收活动(即实际产生经济收益以支付工程开发投入的工作)都来自大型现有代码库。虽然偶有例外,但多数科技巨头的主要业务价值仍然由这些大型现有代码库负责承载和实现。


我也见过那些小巧而优雅的服务能够为某些高收入产品的核心功能提供支持,但实际产品化代码(包括设置、用户管理、计费、企业报告等)仍然离不开大型现有代码库的功能范畴。

 

所以出于对现有业务运转方式的尊重,我们也不应该因为嫌弃而远离所谓“遗留的混乱”。毕竟这就是我们的职责所在,也是我们必须啃下的工程硬骨头。

 

另一个原因在于,如果缺少对大型现有代码库的充分理解,我们根本就不可能进行有效拆分。我见过大型代码库的成功拆分案例,但从未见过不擅长在大型代码库上交付功能的团队可以做到这一点。复杂的现实世界告诉我们,人是无法单靠第一性原理就重新设计出任何足够复杂的项目的(即真正能赚钱的项目)。正是无数极其偶然的细节,支撑着巨头们那年均数千万美元的收益。

总结

  • 大型代码库值得我们费心费力,因为我们的工资往往就从它们中来。

  • 到目前为止,最重要的就是保持一致性。

  • 永远不要在未对代码库中现有技术进行深入研究的情况下,盲目启动任何功能。

  • 如果不遵循现有模式,最好找个足够充分且有说服力的理由。

  • 务必理解代码库的生产足迹。

  • 不要指望测试能够覆盖到每一个用例——相反,要充分发挥监控的作用。

  • 把握所有机会、尽量删除代码,但在操作过程中要极度小心。

  • 尽量让其他领域专家能够轻松发现我们的错误。

 

原文链接:

https://www.seangoedecke.com/large-established-codebases/


2025-02-12 16:0711355
用户头像
李冬梅 加V:busulishang4668

发布了 1231 篇内容, 共 848.2 次阅读, 收获喜欢 1323 次。

关注

评论 2 条评论

发布
用户头像
这翻译的啥呀
2025-02-14 17:36 · 北京
回复
用户头像
这翻译一言难尽,随便找家机器翻译也不至于这样吧 You could put some “return 403 if current user isn’t of that type” logic in your endpoint -> 大家可以在端点中旋转一些“如果当前用户不属于此类型,则返回 403 错误”的逻辑 :-)
2025-02-12 17:44 · 江苏
回复
没有更多了

网络连接总超时?从四层模型上解析网络是怎么连接的

京东科技开发者

计算机网络 服务器 域名

终于知道为啥网页不让我复制粘贴了!

华为云开发者联盟

js 代码 button事件 复制粘贴 输入框

10种下载YouTube视频的方法-包含网站软件插件app等

Space空间

软件 网站 下载youtube视频

自媒体平台数据统计分析爬虫之【趣头条】模拟登陆分析详解及数据统计接口详解

ucsheep

接口 爬虫 趣头条 模拟登录

2021出海社交必看:产品、技术、运营指南

拍乐云Pano

音视频 RTC 社交APP出海 出海社交 社交泛娱乐

Spark性能调优-Shuffle调优及故障排除篇

五分钟学大数据

大数据 spark 3月日更

简单快速搭建,全新语聊方案

anyRTC开发者

ios android 音视频 WebRTC RTC

2021最新分享支付宝/美团/拼多多面经总结

比伯

Java 编程 架构 面试 程序人生

技术杂谈 | Flutter 的性能分析、工程架构与细节处理

有道技术团队

flutter

量化策略软件搭建,马丁策略交易软件开发

Python OpenCV 彩色图像与灰度图像的转换

梦想橡皮擦

3月日更

“数字云南”建设成效逐渐显现 区块链财政电子票据带来民生与环保效益

CECBC

区块链

用 WebRTC 打造一个音乐教育 App,要解决哪些音质难题?

阿里云CloudImagine

音视频 WebRTC 在线教育 RTC

直播预告 | 数据操作加速器,CloudQuery v1.3.5 发布

BinTools图尔兹

sql 编辑器 数据治理 数据安全 数据库管理工具

超详细!手把手带你快速入门 GitHub!

JackTian

git GitHub 开源

神策大数据技术直播系列课第二季,开讲啦

神策技术社区

大数据 性能优化 大前端 工程师 事件分析

别再说你不懂规则引起啦

比伯

Java 编程 程序员 架构 计算机

开抢| 华为开发者大会2021(Cloud)早鸟票来了!

华为云开发者联盟

华为 开发者

golang设置时区的多种方式

happlyfox

学习 3月日更 Go 语言

力扣(LeetCode)刷题,简单题(第14期)

不脱发的程序猿

面试 LeetCode 28天写作 算法攻关 3月日更

畅想数据湖

数据社

数据仓库 数据湖 ETL ELT

美女师姐说给你听!我成为蚂蚁安全工程师的初体验

DT极客

云原生数据库风起云涌,华为云GaussDB破浪前行

华为云开发者联盟

数据库 架构 云原生 华为云 GaussDB

2021年DevOps的四大趋势

禅道项目管理

DevOps 工具 趋势 Redis开发与运维

20天内看完这套GitHub标星18k+的Android资料,含泪整理面经

欢喜学安卓

android 程序员 面试 移动开发

2021年Android面试心得,大厂面经合集

欢喜学安卓

android 程序员 面试 移动开发

NA公链(Nirvana)解决的六大问题在众多公链中脱颖而出NAC公链

区块链第一资讯

音视频开发——通信直播协议和视频推流丨RTMP-RTSP

Linux服务器开发

音视频 WebRTC ffmpeg 直播推流 SRS流媒体服务器

IAP:物联网终端软件升级技术

华为云开发者联盟

IoT LiteOS iap 物联网终端 OTA

情指勤指挥调度平台搭建,公安局情报指挥系统

MoviePy - 中文文档(一个专业的python音视频编辑库)教程

ucsheep

Python 音视频 视频剪辑 Moviepy 视频合成

十年老代码库的生存指南:如何在“屎山”中优雅地工作?_技术选型_Sean Goedecke_InfoQ精选文章