Bjarne Stroustrup 关于软件工程可持续性和时间原则Principle #1 – Time的总结“秘密是什么”What’s the Secret?提示没有万能方法no silver bullet软件开发中不存在一劳永逸的方法。真正的秘密在于理解时间与可持续性。原则 #1 – 时间Principle #1 – Time核心思想软件的预期寿命expected lifespan决定设计和工程策略。开发者需要问自己这段代码会被使用多久会被维护多久预期寿命范围示例预期寿命典型项目分钟 / 小时 / 天 / 周新手Novices的小型任务月 / 年初创企业Startups项目年 / 十年Google 3、Linux Kernel、Apache 等长期项目注不同项目的时间尺度差别很大工程策略也不同。时间与经验开发者经验决定能否应对长寿命项目。如果项目寿命从分钟到十年经验不足的开发者可能只能处理短期项目分钟、小时、天、周。对长期项目年、十年来说可维护性、模块化和可扩展性要求更高。可持续性目标Sustainability软件工程的目标不是仅仅写完程序而是让程序能够随时间安全演化。可持续性意味着你可以安全地修改代码change safely。可以在预期寿命内调整需要调整的部分。不同时间尺度有不同的要求短期天/周快速迭代、灵活修改。中期月/年模块化、可测试、可扩展。长期多年/十年良好的架构、文档、规范、团队经验传承。经验教训很多开发者从未在长期可持续项目上工作过。可持续性软件通常难以获得需要计划和工程策略。软件工程不仅仅是编程它是让程序在时间中保持可变性和可靠性的艺术。总结要点牢记代码预期寿命。理解长期寿命项目难以规划且稀有。可持续代码必须能够安全变化。可持续性在不同时间尺度下意味着不同的策略。小贴士对短期项目不必追求极致架构但对长期项目必须考虑模块化设计清晰接口自动化测试变更可控性可持续性是经验和规划的结合而非单靠技术手段。原则 #2 – 规模Scale核心思想随着时间推移软件会不断变化并增长。增长会带来问题硬件、软件、人员资源都可能成为瓶颈。问题来源重复工作和资源消耗的非线性增长。资源的尺度Resources硬件资源HardwareCPU、RAM、磁盘、网络软件资源Software地址空间、端口、库、依赖人力资源Human开发者、测试人员、维护人员随着项目增长这些资源可能成为限制因素需要提前设计可扩展的策略。传统弃用Traditional Deprecation标记旧版本为过时deprecated引入新版本这种方法简单但如果每个人都还在用旧版本会造成混乱。强制更新删除旧版本设置最后期限要求所有代码迁移。一次性大规模重构找一个勇敢的工程师修改 API 及所有调用者一次性提交。风险很大容易失败。更好的弃用策略Better Deprecation团队承担主要工作不依赖所有用户去做更新让负责模块的团队主导迁移和重构避免单点风险可扩展性原则Scaling Principle在成功的组织中所有重复性工作都应消耗子线性资源sub-linear resource尤其是人力资源意思是重复工作不应随着团队或项目线性增加否则会快速失控。案例每周合并会议Weekly Merge Meeting很多组织曾经需要定期开会讨论分支合并问题Git 分支较多时长期分支合并风险大频繁手动合并浪费人力解决方案Trunk-based development主干开发没有长期开发分支不必选择在哪个分支提交减少人为决策提高可扩展性时间与规模提前左移Time Scale: Shifting Left“左移”Shift-left意味着尽早在开发流程中进行设计、测试、代码审查提前发现问题降低后期成本流程示例设计Design开发Dev提交前测试Presubmit Test代码审查Code Review提交Submit提交后测试Post-submit TestCanary 发布小范围发布正式发布Release关键要点注意超线性成本Superlinear scaling costs任何必须由人重复完成的工作如果增长是线性或超线性就会迅速成为瓶颈。自动化和专家经验带来超线性收益自动化测试、CI/CD、工具链可以让重复任务耗费大幅下降。习惯做法可能无法扩展“正常”的手动操作在项目增长时会出现规模问题。总结原则 #1 时间关注代码寿命和可持续性原则 #2 规模关注随时间增长的资源消耗和重复性工作成功的长期项目需要可持续的代码设计合理的团队工作方式自动化与流程优化原则 #3 – 权衡Tradeoffs核心思想软件开发中总会存在冲突的目标和约束条件。不存在“银弹”Silver Bullet解决所有问题。必须基于证据Evidence-based做决策而非凭直觉或经验主义。主要内容基于证据做决策Make evidence-based decisions不盲目遵循某种方法或工具通过实际数据、性能测试、用户反馈、团队能力等因素做权衡追求可持续性Aim for sustainability决策应考虑长期影响代码、架构、团队流程都应可持续维护可持续性意味着代码能够安全地修改和扩展团队能够长期高效运作避免超线性增长No super-linear scaling尤其针对人力资源Human resources如果重复工作随着团队或项目规模增长而呈超线性增加就会迅速失控所以选择方案时需要考虑自动化工具流程优化减少人为重复工作根据情况重新评估Re-evaluate as needed决策不是一劳永逸随着需求、团队、技术或硬件环境的变化需要重新权衡例如某个架构在小团队时很好但团队扩大后可能效率低下某个算法适合小数据量但大数据量时性能不足总结思路权衡原则告诉我们不存在完美方案所有决策都有利弊依赖数据和实际情况而不是经验或习惯考虑长期可持续性和可扩展性持续观察和调整策略小提示可以把三个原则结合起来理解时间Time→ 代码生命周期长短规模Scale→ 项目增长和资源消耗权衡Tradeoffs→ 面对限制条件做出合理决策Google SWE 的“秘密”核心原则软件工程不仅仅是编程而是关于时间Time代码的生命周期、维护成本、未来扩展都要考虑时间因素影响设计决策短期优化 vs. 长期可维护性快速上线 vs. 可持续发展关注规模Scale当系统或团队增长时要注意超线性成本Super-linear scaling超线性工作量随着规模增长 线性增加非常耗人力/资源专家/专才的贡献在其领域可能呈超线性增益例如某个架构专家优化算法 → 整个系统收益巨大基于证据做决策Evidence-based decisions不要凭个人权威或习惯下决定决策要基于实际数据、测试结果或分析随时间和情况变化证据可能改变 → 必须不断重新评估Google SWE Book 的支柱Pillars支柱上面提到的时间、规模、证据原则Culture文化快乐的开发者协作高效的团队Policies Processes流程与政策如何使开发顺畅、高效Tools工具选择适当技术和工具降低重复劳动和复杂性有趣的观点“Clever” 的区别如果别人说代码“聪明/巧妙clever”是称赞 → 纯编程如果别人说代码“聪明/巧妙clever”是指责 → 软件工程含义编程要简单清晰软件工程更注重可维护性和长期可持续性Shifting Left左移策略成本 vs. 准确性Cost vs. FidelityEmerging Truths不断显现的事实早期发现问题左移 → 降低修复成本提高开发效率Titus Winters 强调软件开发要把测试、设计、审查尽早嵌入开发流程总结理解时间考虑代码长期影响规模注意系统增长带来的复杂性和成本证据基于数据而非权威做决策文化和工具支持开发者高效工作左移思维越早发现问题修复成本越低软件工程Software Engineering定义和理解软件工程Software Engineering定义与本质Dave Parnas (1970) 定义“多个人开发多版本程序”The multi-person development of multi-version programs强调团队协作强调版本管理程序不是一次写完就结束的说明软件工程区别于单人编程复杂性、长期维护、协作是核心Russ Cox (2018) 定义“当你在编程中加入时间和其他开发者时软件工程就出现了”Software engineering is what happens to programming when you add time and other programmers时间Time软件存在生命周期随着时间推移需要维护、扩展多人协作Other programmers团队开发带来的沟通、接口、责任分工问题与 Parnas 定义相呼应但更强调实践角度编程 vs. 软件工程编程Programming如果别人说代码“聪明/巧妙clever”是称赞特点解决问题、追求算法/逻辑巧妙性注重单次功能实现软件工程Software Engineering如果别人说代码“聪明/巧妙clever”是指责特点可维护、可扩展、团队协作、长期可持续注重系统性、规范、文档、协作理解要点软件工程是“编程 时间 团队协作”的产物目标是可持续、高质量、低维护成本的系统巧妙代码不一定是好代码可维护性和团队理解度更重要图示化理解你可以把这个理解画成一个层次图编程Programming ↑ 加入时间 多人协作 软件工程Software Engineering左下角单人编程右上角团队协作 长期维护 → 软件工程2⃣ Truth #1: Hyrum 定律Hyrum’s Law只要你的 API 有足够多的用户无论你在文档中承诺什么总有人会依赖你系统的所有可观察行为。关键点不能只依赖文档承诺代码行为必须谨慎对外暴露的所有行为都可能被依赖API 的稳定性和向后兼容性非常重要3⃣ Truth #2: Change Will Happen变化是必然的可持续代码必须能够安全地修改所有应该修改的东西以适应生命周期中的变化。关键点软件不是一次写完就完事可持续性要求代码能安全演进设计时要考虑扩展性、维护性、模块化4⃣ Truth #3: Shift Left提前介入降低成本早期发现和处理问题比后期修复更便宜。生命周期示意Design → Dev → Presubmit Test → Code Review → Submit → Post-submit Test → Canary → Release \text{Design → Dev → Presubmit Test → Code Review → Submit → Post-submit Test → Canary → Release}Design → Dev → Presubmit Test → Code Review → Submit → Post-submit Test → Canary → ReleaseShift Left 原则将安全性、缺陷检测、测试等活动尽可能提前到开发前期提前介入Shift Left → 降低缺陷成本例如“Shift Left on Security”早期发现安全漏洞而不是发布后修复连续集成Continuous Integration, CICI 系统让每次提交都触发测试和告警自动化保证Shift Left的实施可视为持续预警系统Alerting理解Defect Cost ≠ Speed, Fun, Productivity软件工程的目标是降低长期缺陷成本而不仅仅是快速编程或短期乐趣总结图示化编程 (Programming) - 单人功能实现 - 关注算法巧妙 - 例: 写一个排序函数 软件工程 (Software Engineering) - 团队开发 - 长期维护模块化可持续 - 关注可维护性、安全性、扩展性 - Hyrum定律任何暴露行为都会被依赖 - Shift Left越早发现问题越便宜 - CI 自动化保证持续预警CI is Alerting持续集成即告警1⃣ 不变式检查Invariant checksTests测试用来验证与逻辑正确性相关的不变式例如对象状态、算法输出、业务规则保证代码行为符合预期Monitoring监控基于应用健康状态的不变式例如响应时间、CPU 使用率、请求错误率预测系统是否会保持正常运行告警类型Paging/Alerting 因不合适条件触发 → brittle脆弱例如监控阈值设得不合理容易产生误报Paging/Alerting 间歇触发 → flaky不稳定例如告警触发不稳定或没有明确原因本质上可能监控的是同一个不变式测试和监控的目标都是保证系统健康只是形式不同理解CI 和 Monitoring 的核心是保持系统稳定只是关注角度不同。测试关注逻辑正确性监控关注健康状态。2⃣ CI 的告警价值最高价值的告警直接探测系统是否可用例如“网站是否在线” →ground-truth其他告警间接指标预测系统是否健康例如 CPU/内存占用、请求延迟预测网站是否会持续可用理解CI 不只是跑测试更是一个持续健康检查 告警机制。3⃣ CI 的组成与告警层次Unittests单元测试测试小粒度逻辑可快速反馈代码逻辑是否正确风险flaky / brittle间歇失败或脆弱End-to-end tests端到端测试测试系统整体流程保证 trunk 分支的稳定性确保提交不会破坏整体功能Monitored stats/metrics监控指标系统运行指标如请求数、延迟、错误率可以触发基于原因的告警End-to-end probers端到端探测器自动化探测应用的真实可用性模拟用户行为保证网站可用Error budgets错误预算系统允许的失败范围超过预算则触发告警理解CI 是持续监控 自动化告警 测试反馈的组合保证产品健康和稳定。4⃣ 优化目标“Product fitness”CI 和告警机制的最终目标是保证产品适用性和质量不只是测试是否通过而是确保系统整体健康总结图示化CI (Continuous Integration) Alerting ------------------------------------------------------ Tests | Monitoring 逻辑正确性检查 | 应用健康状态 Unittests | Metrics Stats End-to-end tests | End-to-end probers 保持 trunk green | Error budgets 告警目标: 提前发现缺陷 | 告警目标: 系统健康、可用 最终优化目标: Product FitnessProduct Fitness Proxies产品健康与代理指标1⃣ Product Fitness 的核心概念Product Fitness衡量产品是否真正可用、可发布和可持续的指标不只是功能是否正确还包括性能、稳定性、可用性通过**代理指标Proxies**来近似衡量例如单元测试覆盖率CI 构建是否通过集成测试、端到端测试结果Canary 发布的健康指标实时监控数据延迟、错误率等理解直接测量“产品是否适合用户使用”很难使用代理指标代替可以及时反馈问题。2⃣ Product Fitness 流程与验证层次从低成本、低保真到高成本、高保真的验证链条层次内容特点Code Review代码评审人工检查逻辑、风格、潜在问题低成本、早期发现问题Unit Tests CI单元测试 持续集成验证小粒度功能逻辑自动化、快速、低成本Integration Tests集成测试测试模块间交互保证模块协作正确End-to-End Tests端到端测试模拟用户行为高保真、覆盖系统整体流程Canary金丝雀发布部分用户灰度发布实时发现问题、限制风险Monitoring监控系统运行指标延迟、错误率等高保真、持续反馈End-to-end probes端到端探测器自动化用户行为模拟最接近真实用户体验理解每一层都是 Product Fitness 的代理指标组合使用提高质量和可靠性。3⃣ 成本与保真度权衡早期阶段Design → Dev → Presubmit Test → Code Review成本低保真度低仅验证逻辑、局部功能后期阶段Submit → Post-submit test → Canary → Release成本高运行复杂测试、灰度发布保真度高接近真实用户体验Product Fitness≈低成本测试高保真测试监控数据\text{Product Fitness} \approx \text{低成本测试} \text{高保真测试} \text{监控数据}Product Fitness≈低成本测试高保真测试监控数据理解CI/测试流程要平衡成本 vs. 保真度早期用低成本、快速反馈后期用高保真、实测数据保证质量。4⃣ SRE 策略与 Error Budgets错误预算Error Budget错误预算定义系统允许的失败范围如可接受的 99.9% 可用率超过预算则必须触发警报或暂停发布结合 Product Fitness 流程CI/单元测试、集成测试 → 提前发现逻辑错误端到端探测、Canary → 控制发布风险Monitoring → 持续追踪健康指标理解通过 Error Budget 和代理指标可以让工程团队持续优化产品健康而非仅依赖单次测试或经验。5⃣ 流程可视化示意低成本 / 低保真 → 高成本 / 高保真 --------------------------------------------------- Design → Dev → Presubmit Test → Code Review ↑ | 自动化 低成本验证 Integration Tests → End-to-End Tests → Canary → Release ↑ | 高保真验证 实时监控 Monitoring / End-to-End Probes ↑ | Product Fitness 代理指标总结Product Fitness 是对产品健康的整体衡量通过多层次的代理指标测试、CI、Canary、监控逐步验证早期低成本检测后期高保真验证Error Budget 用于量化可接受风险Truth #3: Shifting Left左移原则1⃣ 核心概念Shifting Left左移在软件工程流程中把验证、测试、质量保证等步骤尽可能提前靠近设计和开发阶段目标以更低成本发现问题原理早期发现问题比后期修复便宜得多本质是风险/成本与验证保真度的权衡成本∼问题发现的时间\text{成本} \sim \text{问题发现的时间}成本∼问题发现的时间风险∼问题越晚发现越严重\text{风险} \sim \text{问题越晚发现越严重}风险∼问题越晚发现越严重2⃣ 工程流程与通信SWE软件工程师Workflow流程的本质是团队内部沟通intra-team communication和产品健康Product Fitness如果流程需要 1:N 人际沟通一个人向多人汇报且随组织规模增加会导致超线性增长的成本和风险因此应尽量将验证和检查靠左早期减少后期的多方协调成本理解团队越大后期发现问题的沟通成本和修改成本增长越快左移原则可以避免这种超线性成本。3⃣ 左移原则的决策规则不要因为右侧已有机制而忽略左侧措施例已有 X 流程在后期检查某问题不意味着可以不做左侧的 Y 检查原因早期的 Y 检查成本低且风险可控不要因为右侧覆盖更多而放弃左侧例右侧的测试覆盖更多内容但左侧测试仍然便宜、可控左侧测试的目的是快速、低成本地捕获问题不追求完美理解左移原则强调“先做低成本、早期验证”右侧机制只是补充而不是替代。4⃣ 软件工程的本质软件工程 ≠ 编程编程关注单次任务完成软件工程关注长期可持续的多版本、多开发者协作Shifting Left 是软件工程的典型实践之一用于提高产品适用性Product Fitness控制风险和成本强调循证决策Evidence-based decisions5⃣ 可视化理解成本 / 风险 高 ───────────────────────────► | | 流程越靠右成本/风险越高 | 低 ───► Shifting Left ◄───────── 高保真验证右 早期验证左 后期验证右 低成本 / 低保真 高成本 / 高保真左侧设计/开发阶段单元测试、代码评审、静态分析低成本、快速反馈右侧提交/集成/生产阶段集成测试、端到端测试、Canary 发布高成本、高保真总结Shifting Left 早期做事低成本捕获问题核心目标在保证产品健康Product Fitness同时降低超线性成本工程实践提前测试、代码审查、静态分析避免依赖右侧机制来替代左侧检查用证据驱动决策而非经验主义