写点什么

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

  • 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:0711051
用户头像
李冬梅 加V:busulishang4668

发布了 1061 篇内容, 共 679.2 次阅读, 收获喜欢 1223 次。

关注

评论 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 · 江苏
回复
没有更多了

究竟什么样的数据库,才能承接RTA广告这个技术活!

平平无奇爱好科技

大模型的 Token 使用详解:限制与注意事项

木南曌

大模型

2024-03-27:用go语言,多维费用背包。 给你一个二进制字符串数组 strs 和两个整数 m 和 n, 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个

福大大架构师每日一题

福大大架构师每日一题

堪称教学神器的5款软件,每一款都值得推荐!

彭宏豪95

效率工具 在线教育 在线白板 办公软件 在线协作

业内首个!央广网APP率先完成鸿蒙原生应用全量版本开发,领跑鸿蒙化进程

最新动态

Jetson Xavier nx 全盘加密

一五

AI 边缘计算 jetson 全盘加密 jetson xavier nx

新版Redis不再“开源”,对使用者都有哪些影响?

平平无奇爱好科技

华为云GeminiDB发布新版本,全面支持Redis 6.2

平平无奇爱好科技

vivo 消息中间件测试环境项目多版本实践

vivo互联网技术

RocketMQ 消息中间件 多版本环境隔离

足球场上的黑科技:图神经网络优化角球

算AI

#人工智能

关于 ASP.NET Core 中的配置系统

雄鹿 @

ASP.NET Core

云计算与 AI 融合:Amazon Connect 开创客户服务智能时代

亚马逊云科技 (Amazon Web Services)

GPT账号被封,如何申诉找回

蓉蓉

ChatGPT GPT-4

Redis不再 “开源”

派大星

redis 开源协议

喜讯!云起无垠入围2023年度中国高科技高成长企业系列榜单

云起无垠

浅析KV存储之长尾时延问题,探寻行业更优解决方案!

平平无奇爱好科技

提升JavaScript代码质量的最佳实践

南城FE

JavaScript 前端 代码质量

在 ASP.NET Core 中使用 HttpClient

雄鹿 @

ASP.NET Core

华为云原生多模数据库GeminiDB架构与应用实践

平平无奇爱好科技

GeminiDB新特性:让Redis广告频控爱不释手的exHASH

平平无奇爱好科技

一文读懂兼顾隐私、高性能和可拓展的公链Partisia Blockchain

BlockChain先知

保护主机安全,我来buff加成

华为云开发者联盟

安全 开发 华为云 华为云开发者联盟 华为云云图说

【干货分享】华为云多模数据库GeminiDB架构与应用实践直播问答实录

平平无奇爱好科技

Partisia Blockchain:如何做到兼顾隐私、高性能和可拓展?

石头财经

关于 ASP.NET Core 中的选项模式

雄鹿 @

ASP.NET Core

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