你刚继承了一个遗留的 C++ 代码库,现在该怎么办?
继承一个庞大、老化的 C++ 代码库,与其说是一个纯技术任务,不如说是一场结合了架构、工具和组织历史的长期勘探。评论者强调,首先要让构建可复现,建立 CI、sanitizer 和基础测试,并在尝试重构或删除功能之前,小心梳理依赖和行为,警惕“电锯式”清理和抛弃多年积累领域知识的绿地式重写。关于是通过更严格的实践和静态分析逐步现代化 C++,还是用 Rust 或 Go 等内存安全语言逐步替换部分系统,讨论很热烈;大多数人都认为,正确选择取决于系统本身及其业务背景有多关键、多长寿、以及多混乱。
联系前任维护者
- 许多人认为“第 0 步”应该是和前任维护者交流:请他们喝咖啡/啤酒,获取他们脑中的模型、历史、坑点、组织政治。
- 也有人说,面对多年维护,“一次性交接”只是“杯水车薪”;真正有帮助的是持续可联系。
- 有些人建议先去试着摸代码,这样就能提出具体问题;另一些人则更喜欢带着空白心态进入,以免形成错误假设。
- 实际问题:前维护者可能已被裁、受 NDA 约束,或者干脆不在了;有时他们只以付费顾问的身份可用。
第一步技术工作:构建、CI、linter
- 强烈共识:先把可复现构建和 CI 搞定,最好放在容器/虚拟机里,这样在任何地方构建结果都一致。
- 以高等级开启编译器警告、sanitizer、静态分析(clang-tidy、cppcheck)以及 Valgrind 等工具;尽早修复最严重的问题。
- 许多人建议先加基础的冒烟/验收测试,再在高变更的“热点”区域写单元测试。
- 关于自动格式化存在争论:有人希望尽早引入,另一些人警告它可能破坏代码解析脚本并让
git blame变得杂乱,不过也有办法缓解这些问题。
重构、删除代码、重写
- 强烈警惕用“电锯式”方式删除功能或看起来像死代码的内容;Chesterton’s fence 和“spacebar heating”式依赖都是真实存在的。
- 有人建议积极裁掉真正的死代码(未关联的二进制、受支持的平台之外的内容),但对模糊不清的部分不要轻举妄动。
- 普遍认为重写风险很高,通常比渐进式重构更糟,尽管少数人报告过成功的大规模重写,但代价和时间都非常巨大。
- 建议:先做用于探索的“试探性重构”,借此了解结构,然后丢弃这些改动,再做更小、更安全的修改。
理解遗留代码
- 建议:每天读代码,用调试器单步执行,追踪主控制流,并在过程中记录文档。
- 提到的工具:UML 或自动绘图工具,用来查看类/继承图;代码理解工具(例如 Source Navigator、Structure101、cppdepend)。
- 随时间减少全局变量,显式传递依赖,以提升可测试性。
内存安全与语言选择
- 对于把“重写成内存安全语言”作为最终步骤的看法并不一致。
- 有人主张 Rust、Go、Java、Swift 等,或者采用带指南和静态分析的现代 C++ 更严格的“高完整性”子集。
- 也有人认为,如今通过 RAII、智能指针和工具,你已经可以把 C++ 做到“相当安全”,而且跨语言拆分会让调试更复杂。
职业与组织层面
- 几位评论者指出,尽管有安全方面的反对,C++ 在金融、嵌入式、游戏以及大型遗留产品等领域仍然很重要。
- 一个反复出现的主题是:代码质量与商业成功往往几乎没有相关性;许多盈利产品运行在混乱、脆弱的 C/C++ 代码库之上。
- 有些人建议,如果你被单独扔进一个庞大、无人拥有的遗留 C++ 系统,要么要求更好的报酬,要么重新考虑这份工作。