《务实的程序员:从匠人到大师(第2版)》

动态交互核心摘要

第一章:务实的哲学

这一章奠定了务实编程的基石——它是一种态度、风格和解决问题的方法。务实的程序员会超越眼前的问题,寻求更大的背景和蓝图。

主题 1: 这是你的人生

你的职业生涯由你掌控。务实的程序员主动塑造自己的职业道路,而不是被动接受。

💡 技巧 3: 你有选择权 (You Have Agency)。 如果工作环境不佳或项目无聊,尝试去改变它。如果无法改变,那就换个环境。在这个行业里,你拥有非凡的机会,要主动去把握。

主题 2: 猫吃了我的源代码

务实哲学的基石之一是为自己的行为负责。承担责任,不找借口,是建立团队信任的关键。

💡 技巧 4: 提供选项,而不是找借口 (Provide Options, Don’t Make Lame Excuses)。 当事情出错时,不要只是说“做不到”,而是解释“可以做什么”来补救,并提供备选方案。

主题 3: 软件熵

软件和物理世界一样会趋向无序,即“软件腐烂”或“技术债务”。项目心理学是导致腐烂的重要因素。“破窗效应”表明,一个未修复的小问题会迅速引发更多问题,导致整个系统的衰败。

💡 技巧 5: 不要容忍破窗 (Don’t Live with Broken Windows)。 无论是糟糕的设计、错误的决策还是低劣的代码,一旦发现就要立即修复。如果没时间彻底修复,也要先“用木板把它钉起来”,阻止进一步的损害。

主题 4: 石头汤与煮青蛙

这个主题讲述了两种关于变化的寓言。石头汤教我们如何成为变革的催化剂:从一个小的、可行的请求开始,展示成功,然后逐步吸引他人加入,最终达成宏伟目标。而煮青蛙则是一个警示:要警惕渐进式的衰败,持续关注大局,否则会在不知不觉中陷入困境。

💡 技巧 6: 成为变革的催化剂 (Be a Catalyst for Change)。
💡 技巧 7: 记住大局 (Remember the Big Picture)。

主题 5: 足够好的软件

追求完美可能导致项目延期甚至失败。通常,用户宁愿今天就用上一个有一些瑕疵的软件,也不愿为“完美”的版本等上一年。关键在于让用户参与决策,确定何时软件“足够好”。

💡 技巧 8: 将质量作为需求问题 (Make Quality a Requirements Issue)。 系统的范围和质量应该作为需求的一部分与用户讨论,找到平衡点。

主题 6: 你的知识组合

知识和经验是会过期的资产。你需要像管理金融投资组合一样管理你的知识:定期投资、多元化、管理风险、低买高卖、定期回顾和重新平衡。

💡 技巧 9: 定期投资你的知识组合 (Invest Regularly in Your Knowledge Portfolio)。 养成习惯,例如每年学习一门新语言,每月读一本技术书。
💡 技巧 10: 批判性地分析你读到和听到的一切 (Critically Analyze What You Read and Hear)。 学会“问五个为什么”,思考“谁会受益”,以及分析事情发生的背景。

主题 7: 沟通!

有效的沟通至关重要。你需要了解你的听众,知道你想说什么,选择合适的时机和风格。文档也是沟通的一种形式,应该内建于开发过程中,而不是事后添加。

💡 技巧 11: 把英语(或你的母语)当作另一门编程语言 (English is Just Another Programming Language)。
💡 技巧 12: 说什么和怎么说同样重要 (It’s Both What You Say and the Way You Say It)。
💡 技巧 13: 内建文档,而不是事后添加 (Build Documentation In, Don’t Bolt It On)。
第二章:务实的方法

本章介绍了一些通用的软件开发原则和技巧,它们是务实编程的核心实践。

主题 8: 好设计的本质

所有设计原则的本质都可以归结为一个简单的原则:ETC (Easier to Change),即“更容易修改”。

💡 技巧 14: 好的设计比坏的设计更容易变更 (Good Design Is Easier to Change Than Bad Design)。 无论是解耦、单一职责原则还是命名规范,其目的都是为了让未来的修改变得更简单。

主题 9: DRY——重复的危害

DRY 原则(Don't Repeat Yourself)是务实编程最重要的工具之一。它不仅仅指代码的复制粘贴,更关乎知识和意图的重复。

💡 技巧 15: DRY——不要重复自己 (Don’t Repeat Yourself)。 系统中的每一项知识都必须具有单一、明确、权威的表示。

主题 10: 正交性

正交性意味着系统中的组件是相互独立、解耦的。修改一个组件不应影响其他不相关的组件。正交的系统能提高生产力并降低风险。

💡 技巧 17: 消除无关事物之间的影响 (Eliminate Effects Between Unrelated Things)。

主题 11: 可逆性

世界在不断变化,关键决策并非一成不变。我们应该构建灵活、适应性强的软件,避免做出无法挽回的决定。例如,将数据库抽象为一个持久化服务,而不是将代码与特定供应商绑定。

💡 技巧 18: 没有最终决策 (There Are No Final Decisions)。

主题 12: 曳光弹

对于充满未知的项目,与其进行过度设计,不如采用“曳光弹”开发方法。快速构建一个能贯穿系统所有架构层的、简陋但完整的端到端功能骨架。这能让你获得早期反馈,验证架构,并为团队提供一个可以逐步填充的工作框架。曳光弹代码是产品代码的一部分,不是一次性的原型。

💡 技巧 20: 使用曳光弹找到目标 (Use Tracer Bullets to Find the Target)。

主题 13: 原型与便利贴

原型是为了学习和探索风险而创建的一次性代码或模型。它可以用来测试架构、算法、界面或想法。原型的价值在于其过程中的学习,而不是产出的代码本身。

💡 技巧 21: 通过原型来学习 (Prototype to Learn)。

主题 14: 领域语言

编程语言影响我们思考问题的方式。如果能使用贴近问题领域的词汇、语法和语义来编程,代码会变得更清晰、更易于理解。这可以通过创建内部DSL(领域特定语言)或使用外部DSL(如YAML、JSON)来实现。

💡 技巧 22: 贴近问题领域编程 (Program Close to the Problem Domain)。

主题 15: 估算

估算是一项重要的技能,能帮助我们避免意外。估算不是一个精确的数字,而是一个带有上下文和准确性预期的范围。对于项目排期,迭代式开发是获得准确估算的最好方法,随着项目的进行不断修正估算。

💡 技巧 23: 通过估算避免意外 (Estimate to Avoid Surprises)。
💡 技巧 24: 随同代码迭代排期 (Iterate the Schedule with the Code)。
第三章:基本工具

工具能放大你的才能。一个务实的程序员应该精通一套基本工具,并让它们成为自己能力的延伸。

主题 16: 纯文本的力量

知识是我们的原材料,而存储知识的最佳格式是纯文本。它能避免技术过时,利用现有工具,并简化测试。

💡 技巧 25: 将知识用纯文本保存 (Keep Knowledge in Plain Text)。

主题 17: Shell 游戏

命令行 Shell 是程序员的工作台。精通 Shell 可以让你自动化常见任务,组合各种工具,发挥出 GUI 无法比拟的强大威力。

💡 技巧 26: 发挥 Shell 的威力 (Use the Power of Command Shells)。

主题 18: 强力编辑

编辑器是你使用最频繁的工具。达到“编辑器流畅”的境界,意味着你不再需要思考编辑的机械动作,思想可以直接流淌为代码。

💡 技巧 27: 达到编辑器流畅 (Achieve Editor Fluency)。

主题 19: 版本控制

版本控制系统是项目的“时间机器”,是巨大的撤销键。所有东西——代码、文档、配置文件、脚本——都应该纳入版本控制。它不仅仅是备份,更是团队协作、构建和发布流程的核心枢纽。

💡 技巧 28: 永远使用版本控制 (Always Use Version Control)。

主题 20: 调试

调试是一种解决问题的过程,而不是互相指责。要抱着解决问题的心态,而不是恐慌。在修复一个 bug 之前,先编写一个能够复现它的测试。

💡 技巧 29: 要修正问题,而不是指责 (Fix the Problem, Not the Blame)。
💡 技巧 31: 在修正代码前,先有会失败的测试 (Failing Test Before Fixing Code)。
💡 技巧 32: 阅读该死的错误消息 (Read the Damn Error Message)。

主题 21 & 22: 文本处理 & 工程日志

学习一门文本处理语言(如 Python、Ruby)可以极大地提高生产力。同时,保持一本工程日志(纸质为佳),记录你的工作、想法和学到的东西,这比记忆更可靠。

💡 技巧 35: 学习一门文本处理语言 (Learn a Text Manipulation Language)。
第四章:务实的偏执

我们无法写出完美的软件。接受这个事实,并采取防御性编程,不仅要防范外部错误,也要防范我们自己的错误。

主题 23: 契约式设计 (DBC)

DBC 是一种通过为软件模块定义精确的、可验证的接口规范来确保程序正确性的技术。它包含前置条件、后置条件和类不变量。

💡 技巧 37: 通过契约进行设计 (Design with Contracts)。 明确代码的权利和责任,让代码更懒惰:严格要求输入,并承诺尽可能少的输出。

主题 24: 消亡的程序不撒谎

一个崩溃的程序造成的损害,通常远小于一个带病运行的程序。当代码发现“不可能”发生的事情时,程序已经不再可靠,应该尽快终止它。

💡 技巧 38: 尽早崩溃 (Crash Early)。

主题 25: 断言式编程

当你认为“这绝不可能发生”时,就用代码来检查它。断言是检查那些理论上不应发生的情况的有效工具。不要在生产环境中关闭断言。

💡 技巧 39: 使用断言避免“不可能” (Use Assertions to Prevent the Impossible)。

主题 26: 如何平衡资源

资源管理(内存、文件句柄、数据库连接等)遵循一个简单的模式:分配、使用、释放。谁分配资源,谁就负责释放资源。

💡 技巧 40: 完成你开始的任务 (Finish What You Start)。

主题 27: 不要超越你的前灯

我们对未来的预测能力有限,就像车灯只能照亮前方一小段路。开发时要始终采取小的、可验证的步骤,并根据反馈进行调整。你的反馈速率就是你的速度极限。

💡 技巧 42: 永远采取小步骤 (Take Small Steps—Always)。
后续章节核心要点

第五章:可弯曲,不折断(Bend, or Break)

  • 解耦 (Decoupling): 紧密耦合的代码难以修改。通过“告知,不要询问”(Tell, Don't Ask)原则和避免方法链式调用来减少耦合。
  • 转换式编程 (Transforming Programming): 将程序视为一系列数据转换的管道,而不是一堆相互作用的对象。这能减少状态的持有,降低耦合。
  • 继承税 (Inheritance Tax): 继承是面向对象语言中最强的耦合。优先使用接口、委托或混入 (Mixins) 来实现多态和代码共享。
  • 配置 (Configuration): 将易变的细节(如数据库凭证、税率、特性开关)外部化到配置文件中,使应用无需重新编译即可适应变化。
💡 技巧 44: 解耦的代码更易于修改 (Decoupled Code Is Easier to Change)。
💡 技巧 51: 不要支付继承税 (Don’t Pay Inheritance Tax)。
💡 技巧 55: 使用外部配置参数化应用 (Parameterize Your App Using External Configuration)。

第六章:并发(Concurrency)

  • 打破时序耦合 (Breaking Temporal Coupling): 通过分析工作流,识别可以并行执行的活动,让你的设计能够容纳并发。
  • 共享状态是错误状态 (Shared State Is Incorrect State): 并发问题的根源在于可变的共享状态。并发访问需要通过信号量、互斥锁等机制来保护。
  • Actor 与进程 (Actors and Processes): 使用 Actor 模型可以实现无共享状态的并发。每个 Actor 都是一个独立的计算单元,通过异步消息进行通信。
  • 黑板 (Blackboards): 对于复杂的工作流,可以使用黑板系统来协调。各个独立的“专家”代理从黑板上获取数据,处理后将新数据放回,共同推动问题的解决。
💡 技巧 57: 共享状态是错误状态 (Shared State Is Incorrect State)。
💡 技巧 59: 使用 Actor 实现无共享状态的并发 (Use Actors For Concurrency Without Shared State)。

第七章:编码时(While You Are Coding)

  • 听从你的蜥蜴脑 (Listen to Your Lizard Brain): 关注你的直觉和潜意识。如果你对某段代码感到不安或觉得它写起来异常困难,停下来思考一下,这可能是你的经验在发出警告。
  • 通过巧合编程 (Programming by Coincidence): 不要依赖巧合或未明确定义的行为。要刻意地编程,清楚地知道代码为什么能工作。
  • 重构 (Refactoring): 编码更像是园艺而不是建筑。代码需要持续地照料和修整。当你学到新东西或发现可以改进的地方时,尽早重构,频繁重构。
  • 为测试而编码 (Test to Code): 测试的主要好处不是找到 bug,而是在你思考和编写测试时获得的设计反馈。测试是你代码的第一个用户。
  • 属性测试 (Property-Based Testing): 除了编写基于示例的单元测试,还可以使用属性测试。定义代码应遵守的属性(不变量、契约),然后让框架生成大量随机数据来验证这些属性。
  • 命名 (Naming Things): 好的命名至关重要。它揭示了你的意图。当事物的角色或意义发生变化时,要及时重命名。
💡 技巧 62: 不要通过巧合编程 (Don’t Program by Coincidence)。
💡 技巧 65: 尽早重构,频繁重构 (Refactor Early, Refactor Often)。
💡 技巧 67: 测试是你代码的第一个用户 (A Test Is the First User of Your Code)。

第八、九、十章:项目与责任

  • 需求之坑 (The Requirements Pit): 没有人确切知道自己想要什么。需求是在反馈循环中学习到的。你的工作是帮助人们理解他们想要什么。
  • 敏捷的本质 (The Essence of Agility): 敏捷不是一个名词,而是你做事的方式。它的核心是:1. 弄清你在哪里。2. 朝你想去的地方迈出最小且有意义的一步。3. 评估结果,并修正。
  • 务实的启动工具包 (Pragmatic Starter Kit): 每个项目都应建立在三个支柱上:版本控制、回归测试和完全自动化。
  • 取悦用户 (Delight Your Users): 你的目标不仅仅是交付代码,而是通过软件解决用户的业务问题,超出他们的预期。
  • 在你的作品上签名 (Sign Your Work): 为你的工作感到自豪。你的名字应该成为质量的标志。
  • 道德责任 (Moral Compass): 作为未来的构建者,我们有责任思考代码带来的影响。“首先,不要造成伤害”,以及“不要为流氓提供便利”。
💡 技巧 77: 需求是在反馈循环中学习到的 (Requirements Are Learned in a Feedback Loop)。
💡 技巧 83: 敏捷不是名词,而是你做事的方式 (Agile Is Not a Noun; Agile Is How You Do Things)。
💡 技巧 97: 在你的作品上签名 (Sign Your Work)。
💡 技巧 99: 不要为流氓提供便利 (Don’t Enable Scumbags)。

务实之道:程序员的行事原则

原文

源链接

附件

中文epub (2.0M)

下载