The Pragmatic Programmer: your journey to mastery ( 2nd Edition )
References
- Book "The Pragmatic Programmer : your journey to mastery, 2nd Edition"
- ZH Ver. :《 程序员修炼之道:通往务实的最高境界(第二版)》
第一版前言
- Tip 1 : 关注你的技艺 ( Care About Your Craft )
- 如果你不关心怎么做好, 为什么还要花时间去开发软件呢?
- Tip 2 : 思考! 思考你的工作 ( Think! About Your Work )
- 关掉辅助驾驶, 有自己掌控, 持续不断地评估所做的工作
章 1 : 务实的哲学 ( A Pragmatic Philosophy )
1. 人生是你的 ( It's Your Life )
- Tip 3 : 你有权选择 ( You Have Agency )
- 人生是自己的. 把握住人生, 让它如你所愿
- 工作内容无聊没有意思? 团队一团糟? "改变这个组织, 或者换一个组织"
- Does your work environment suck? Is your job boring? Try to fix it. But don't try forever. As Martin Fowler says, "you can change your organization or change your organization."
- 担心自己掌握的技术过时? 安排时间学习看起来有趣的新技术
- 这是一种自我投资, 只有为此加班才是合理的
- If technology seems to be passing you by, make time (in your own time) to study new stuff that looks interesting.
- You’re investing in yourself, so doing it while you’re off-the-clock is only reasonable.
- 不要期待事情自己变好, 而是考虑一下 : 自己行动起来
- Be proactive and take them.
- 工作内容无聊没有意思? 团队一团糟? "改变这个组织, 或者换一个组织"
- 人生是自己的. 把握住人生, 让它如你所愿
2. 我的源码被猫吃了 ( The Cat Ate My Source Code )
- Tip 4 : 提供选择, 别找借口 ( Provide Options, Don't Make Lame Excuses )
- 提供选择而不是去找理由. 不要只说做不到; 解释一下都能做到些什么
- 英文原文 :
- Don't blame someone or something else, or make up an excuse.
- Don't blame all the problems on a vendor, a programming language, management, or your coworkers.
- Any and all of these may play a role, but it is up to you to provide solutions, not excuses.
- 英文原文 :
- 提供选择而不是去找理由. 不要只说做不到; 解释一下都能做到些什么
3. 软件的熵 ( Software Entropy )
- Tip 5 : 不要放任破窗 ( Don't Live with Broken Windows )
- 只要看到 不好的设计 / 错误的决策 / 糟糕的代码, 就赶紧去纠正
4. 石头做的汤和煮熟的青蛙 ( Stone Soup and Boiled Frogs )
- Tip 6 : 做推动变更的催化剂 ( Be Catalyst for Change )
- 你无法强迫人们去改变, 但可以展示美好未来, 并帮助他们参与创造
- Tip 7 : 牢记全景 ( Remember the Big Picture )
- 不要过度沉浸于细枝末节, 一面觉察不到周围正在发生的事情
5. 够用即可的软件 ( Good-Enough Software )
- Tip 8 : 将质量要求视为需求问题 ( Make Quality a Requirements Issue )
- 让用户参与对项目真实质量的确定
6. 知识组合 ( Knowledge Portfolio )
- Tip 9 : 对知识组合做定期投资 ( Invest Regularly in Your Knownledge Portfolio )
- 养成学习的习惯
- Tip 10 : 批判性地分析你读到和听到东西 ( Critically Analyze What You Read and Hear )
- 不要受供应商、媒体炒作或教条的影响, 根据自身和项目的实际情况来分析信息
7. 交流! ( Communicate )
- Tip 11 : 英语就是另一门编程语言 ( English is Just Another Programming Language )
- 将英语是做一门编程语言. 写文档和变成一样要遵守 DRY 原则、ETC、自动化等
- 让它看起来不错
- 想法很重要. 但听众还希望有个好看的包装
- 太多的开发人员 ( 包括他们的经理 ) 在编写书面文档时, 只关注内容, 我们认为这不对
- 随便一个厨师 ( 或者是美食频道的主持人 ) 都会告诉你, 仅仅是糟糕的外观就能毁掉你在厨房里埋头苦干几小时的成果
- 回应别人
- 当你问别人一个问题时, 如果他们不回答, 你会觉得他们不礼貌
- 那么当别人发电子邮件或备忘录给你, 问你一些信息, 请你做一些事情时, 你有多少次没有回应? 日常生活忙忙碌碌, 忘点事情太常见了
- 一定要记得回复邮件, 就算简单地度偶依据 "我稍后答复你" 都好
- 随时知会别人, 能让人更容易原谅你偶然的疏忽, 让人觉得你并没有忘记他们
- 让它看起来不错
- 将英语是做一门编程语言. 写文档和变成一样要遵守 DRY 原则、ETC、自动化等
- Tip 12 : 说什么和怎么说同样重要 ( It's Both What You Say and the Way You Say It )
- 如果无法有效交流, 任何伟大的想法都是没有意义的
- Tip 13 : 把文档嵌进去, 而不要拴在表面 ( Build Documentation In, Bon't Bolt It On )
- 与代码隔离的文档, 很难保持正确并及时更新
章 2 : 务实的方法 ( A Pragmatic Approach )
8. 优秀设计的精髓 ( The Essence of Good Design )
- Tip 14 : 优秀的设计比糟糕的设计更容易变更 ( Good Design Is Easier to Change than Bad Design )
- 适合使用者的事物, 都已经过良好设计. 对代码来说, 这意味着必须适应变化
- ETC 原则 : Easier To Change 更容易变更
- 它是一种价值观念, 不是一条规则
9. DRY -- 邪恶的重复 ( The Evils of Duplication )
- Tip 15 : DRY -- 不要重复自己 ( Don't Repeat Yourself )
- 系统中的每一条知识, 都必须有单一且无歧义的权威陈述
- 注释中的重复
- 不要用注释来解释代码做了什么, 因为这样的话, 变化的代码迟早会跟注释的解释不一致
- 将命名和排版做好即可, 注释只用来解释做什么/为什么. 如果需要了解细节, 源码里应有尽有!
- 数据中的重复
- 一个对象既有 start 也有 end 还有 length 字段, 这没必要! 因为 length 可以根据 start 和 end 计算出来
- 如果以后有性能要求, 必须缓存 length, 那时才这么做, 不必过早优化
- 经验 : 一个模块提供的所有服务都应该通过统一的约定来提供, 该约定不应表露出其内部实现是基于存储还是基于计算的
- 表征的重复
- 注释中的重复
- 系统中的每一条知识, 都必须有单一且无歧义的权威陈述
- Tip 16 : 让复用变得更容易 ( Make It Easier to Reuse )
- 只要复用方便, 人们就会去做. 创建一个支持复用的环境
10. 正交性 ( Orthogonality )
- Tip 17 : 消除不相关事物之间的影响 ( Eliminate Effects Between Unreleated Things )
- 设计的组件, 需要自成一体、独立自主, 有单一的清晰定义的意图
- 正交性 : 象征着 独立性 或 解耦性 ( 从几何学中借用来的术语 )
- 对于两个或多个事物, 其中一个的改变不影响其他任何一个, 则这些事物是正交的
- 测试正交性的方法 : 如果一个特别功能背后的需求发生显著改变, 有多少模块会受影响?
- 不要依赖那些你无法控制的东西
- 当耦合是好的时, 我们称之为内聚
- 正交性 : 象征着 独立性 或 解耦性 ( 从几何学中借用来的术语 )
- 设计的组件, 需要自成一体、独立自主, 有单一的清晰定义的意图
11. 可逆性 ( Reversibility )
- Tip 18 : 不设最终决定 ( There Are No Final Decisions )
- 不要把决定刻在石头上, 而要将其视为写在沙滩上的东西, 时刻准备应变
- Tip 19 : 放弃追逐时尚 ( Forgo Following Fads )
- 尼尔·福特说过 : "昨日之最佳实践, 即明日之反模式." 要基于基本原则去选择架构, 而不应盲从于流行
12. 曳光弹 ( Tracker Bullets )
- Tip 20 : 使用曳光弹找到目标 ( Use Tracer Bullets to Find the Target )
- 通过不断尝试并看清着弹点, 曳光弹可确保你最终击中目标
- 曳光弹式开发 -- 在真是条件下针对 "移动目标" (多变的需求) 进行即时反馈
- 创建一个简单的工程, 加一行 "hello world!", 并确保其能编译和运行
- 然后, 我们再去找整个应用程序中不确定的部分, 添加上让他们跑起来的骨架
- 曳光弹式开发 的 特点 -- 能跑起来
- 曳光弹式开发 -- 在真是条件下针对 "移动目标" (多变的需求) 进行即时反馈
- 通过不断尝试并看清着弹点, 曳光弹可确保你最终击中目标
13. 原型与便签 ( Prototype and Post-it Notes )
- Tip 21 : 用原型学习 ( Prototype to Learn )
- 制作原型旨在学习经验,其价值不在于过程中产生的代码, 而在于得到的教训
- 汽车制造商可能会为一款新城的设计制造许多不同的 "原型"
- 每个原型都是为了测试一个特定的方面 -- 空气动力学、造型、结构特征等
- 原型开发 特点 -- 不必制作真正 (能运行) 的东西
- 原型开发 目标 -- 找出有风险或不确定的因素
- 原型的意义 : 为了学习经验 -- 不在于产生的代码, 而在于吸取的教训
- 如果发现自己处在不能放弃细节的环境中, 可能不是在 "制作原型", 而是进行 "曳光弹式开发"
- 可以忽略的细节 : 正确性 / 完整性 / 健壮性 / 格式
- 开始任何基于代码的原型开发前, 确保每个人都理解, 正在编写的是一次性代码
- 原型可能有着欺骗性的外表, 对哪些不知道这只是原型的人产生吸引力
- 必须非常清楚地表明改代码是用完即弃的, 它并不完整也不可能做到完整
- ( 明确目的, 别做多余的无用功 )
- 制作原型旨在学习经验,其价值不在于过程中产生的代码, 而在于得到的教训
14. 领域语言 ( Domain Languages )
- Tip 22 : 靠近问题域编程 ( Program Close to Problem Domain )
- 用问题领域的语言来做设计和编程
- 这里说的不是 "面向领域编程", 详情见原书
- 用问题领域的语言来做设计和编程
15. 估算 ( Estinmating )
- Tip 23 : 通过估算来避免意外 ( Estimate to Avoid Surprises )
- 开始之前做估算, 能提前发现潜在问题
- 例如, 对内存/硬盘/带宽/耗时/项目排期的估算
- 计划评审技术 PERT -- Program Evaluation Review Techininque
- 每个 PERT 任务都有一个乐观的、一个最可能的、一个悲观的估算
- 开始之前做估算, 能提前发现潜在问题
- Tip 24 : 根据代码不断迭代进度表 ( Iterate the Schedule with the Code )
- 利用实施过程中获得的经验来精细化项目的时间尺度
章 3 : 基础工具 ( The Basic Tools )
16. 纯文本的威力 ( The Power of Plain Text )
- Tip 25 : 将知识用纯文本保存 ( Keep Knowledge in Plain Text )
- 纯文本不会过时. 它能够让你的工作事半功倍, 并能简化调试和测试工作
- GUI 工具的好处在于 WYSIWYG -- What you see is what you get.
- GUI 工具的弱势在于 WYSIAYG -- What you see is all you get.
- 纯文本不会过时. 它能够让你的工作事半功倍, 并能简化调试和测试工作
17. Shell 游戏 ( Shell Games )
- Tip 26 : 发挥 Shell 命令的威力 ( Use the Power of Command Lines )
- 当图形化界面无法胜任时, 使用 Shell
18. 加强编辑能力 ( Power Editing )
- Tip 27 : 游刃有余地使用编辑器 ( Achieve Editor Fluency )
- 既然编辑器是至关重要的工具, 不妨了解一下如何用它更快更准确地实现需求
19. 版本控制 ( Version Control )
- Tip 28 : 永远使用版本控制 ( Always Use Version Control )
- 版本控制为你的工作创造了一个时间机器, 可以用它重返过去
20. 调试 ( Debugging )
- Tip 29 : 去解决问题, 而不是责备 ( Fix the Problem, Not the Blame )
- Bug 到底来自你的失误还是别人的失误真的不重要 -- 它终究是你的问题, 需要你来修复
- Tip 30 : 不要恐慌 ( Don't Panic )
- 不管是对银河系搭车客, 还是对开发者来说, 都应这样
- 不要在 "但那不可能发生" 的思路上浪费哪怕一个神经元, 因为很明显它会发生, 而且已经发生了!
- 注意不要短视, 不要仅仅去纠正你所看到的症状, 永远要去发掘问题的 根本原因
- 不管是对银河系搭车客, 还是对开发者来说, 都应这样
- Tip 31 : 修代码前先让代码在测试中失败 ( Failing Test Before Fixing Code )
- 在你修 Bug 前, 先创建一个聚焦于该 Bug 的测试
- Tip 32 : 读一下那些该死的出错信息 ( Read the Damn Error Messages )
- 大多数异常都能告诉失败之物与失败之处. 如果足够幸运, 你甚至能得到具体的参数值
- Tip 33 : "select" 没出问题 ( "select" Isn't Broken )
- 在操作系统或编译器中发现 Bug 非常罕见, 甚至在第三方产品或库中也是如此. Bug 大多出现在应用程序中
- 怀疑操作系统/编译器/第三方库/中间件/数据库有问题? 还不如怀疑自己的应用程序有问题…
- 在操作系统或编译器中发现 Bug 非常罕见, 甚至在第三方产品或库中也是如此. Bug 大多出现在应用程序中
- Tip 34 : 不要假设, 要证明 ( Don't Assume It, Prove It )
- 在真实环境中证实你的假设 -- 要依赖真实的数据及边界条件
- 除了 bugfix, 还需要 确认为什么没有更早地发现这个错误?
- 是否需要修改单元测试或其它测试, 以让这些测试能够捕获到它
- 除了 bugfix, 还需要 确认为什么没有更早地发现这个错误?
- 在真实环境中证实你的假设 -- 要依赖真实的数据及边界条件
21. 文本处理 ( Text Manipulation )
- Tip 35 : 学习一门文本处理语言 ( Learn a Text Manipulation Language )
- 既然每天都要花大量的时间与文本打交道, 何不让计算机帮你分担一二?
22. 工程日记 ( Engineering Daybooks )
- 就是 工作日志/备忘录/想法速记/……
- 好处
- 备忘录 : 比记忆可靠
- Todo List : 保存与当前任务无关的内容, 继续专注手头上的事
- 反观诸己 : 作用像是橡皮鸭, 换旁观者的角度来观察自己, 反省
章 4 : 务实的偏执 ( Pragmatic Paranoia )
- Tip 36 : 你无法写出完美的软件 ( You Can't Write Perfect Software )
- 软件不可能是完美的. 对于在所难免的错误, 要保护代码和用户免受其影响
- 务实的程序员更进一步, 他们连自己也不相信
- 软件不可能是完美的. 对于在所难免的错误, 要保护代码和用户免受其影响
23. 契约式设计 ( DBC -- Design By Contract )
- Tip 37 : 通过契约进行设计 ( Design By Contracts )
- 代码是否不多不少刚好完成它宣称要做的事情, 可以使用契约加以校验和文档化
- Design By Contract
- 前置条件 : 一个例程永远不应该在前置条件被违反的时候被调用
- 后置条件 : 保证例程完成时世界的状态 ( 例如不允许无限循环 )
- 类的不变式 : 从调用者角度来看, 类会确保该条件始终为真
- 强调编写 "懒惰" 的代码 ( 相关 : 正交性 建议编写 "害羞" 的代码 )
- 开始之前, 对要接收的东西要求严格一点, 并且尽可能少地对回报做出承诺
- 如果你定的契约是可以接受任何东西, 并且承诺回报整个世界, 那么你就有很多代码要写
- Design By Contract
- 代码是否不多不少刚好完成它宣称要做的事情, 可以使用契约加以校验和文档化
24. 死掉的程序不会说谎 ( Dead Programs Tell No Lies )
- Tip 38 : 尽早崩溃 ( Crash Early )
- 彻底死掉的程序通常比有缺陷的程序造成的损害要小
- 与其 try-catch 之后 ( 可能会记一下日志, 再包装成别的异常 ) 重新抛出, 可能不如直接让异常抛出来
- "防御式编程是在浪费时间, 让它崩溃!"
- 彻底死掉的程序通常比有缺陷的程序造成的损害要小
25. 断言式编程 ( Assertive Programming )
- Tip 39 : 使用断言去预防不可能的事情 ( User Assertions to Prevent the Impossible )
- 如果一件事情不可能发生, 那么就用断言来确保其的确不会发生. 断言在校验你的假设, 要使用断言在不确定的世界中将你的代码保护起来
26. 如何保持资源的平衡 ( How to Balance Resources )
- Tip 40 : 有始有终 ( Finish What You Start )
- 只要有可能, 对资源进行分配的函数或对象就有责任去释放该资源
- Tip 41 : 在局部行动 ( Act Locally )
- 将易变的变量维持在一个范围内, 打开资源的过程要短暂且明显可见
27. 不要冲出前灯范围 ( Don't Outrun Your Headlights )
- Tip 42 : 小步前进 -- 由始至终 ( Take Small Steps -- Always )
- 永远小步前进, 不断检查反馈, 并且在推进前先做调整
- 我们不是应该为将来的维护做设计吗? 没错, 不过要适可而止: 别超过你能看见的范围
- 越是必须预测未来会怎样, 就越有可能犯错. 与其浪费精力为不确定的未来设计, 还不如将代码设计成可替换的
- 当你想要丢弃你的代码, 或将其换成更合适的时, 要让这一切无比容易, 这有助于提高内聚性、解耦和 DRY, 从而实现更好的总体设计
- 永远小步前进, 不断检查反馈, 并且在推进前先做调整
- Tip 43 : 避免占卜 ( Avoid Fortune-Telling )
- 只在你能看到的范围内做计划
章 5 : 宁弯不折 ( Bend, or Break )
28. 解耦 ( Decoupling )
- Tip 44 : 解耦代码让改变更容易 ( Decoupled Code Is Easier to Change )
- 耦合使事物紧紧绑定在一起, 以至于很难只改变其中之一
- 注意留心一些耦合的 "症状"
- …… ( 没深刻感受的前两条略过 )
- 开发人员害怕修改代码, 因为他们不确定会造成什么影响
- 会议要求每个人都必须参加, 因为没有人能确定谁会受到变化的影响
- 注意留心一些耦合的 "症状"
- 耦合使事物紧紧绑定在一起, 以至于很难只改变其中之一
- Tip 45 : 只管命令不要询问 ( TDA -- Tell, Don't Ask )
- 不要从对象中取出值, 在加以变换后再塞回去, 让对象自己来完成这些工作
- 直接向 "服务" 要我们想要的
- 得墨忒耳定律 ( LoD - Law of Demeter ) :
- 亦被称作 "最少知识原则" ( Principle of Least Knowledge )
- 定义在 C 类中的方法只应该调用 :
- C 类其它实例的方法
- 它的参数
- 他所创建出来的对象的方法, 包括在栈上和堆上的对象
全局变量
- 不要从对象中取出值, 在加以变换后再塞回去, 让对象自己来完成这些工作
- Tip 46 : 不要链式调用方法 ( Don't Chain Method Calls )
- 当访问某事物时, 使用的点号不要超过一个
- 例外 : 如果你链式调用的东西真的不太可能改变
- 实践中, 应用程序中的任何内容, 都应该被认为是可能发生改变的
- 当访问某事物时, 使用的点号不要超过一个
- Tip 47 : 避免全局数据 ( Avoid Global Data )
- 最好给每个方法增加一个额外的参数
- 全局可访问数据是应用程序组件之间耦合的潜在来源
- 每一块全局数据就好像让应用程序中的每个方法都突然获得了一个额外的参数
- 全局变量带来耦合的最明显原因 : 可能会潜在地影响到系统中的所有代码
- 当然, 在实践中影响相当有限; 问题在于你必须找到每一处需要修改的地方
- 当然 "重用" 可能不总是创建代码时主要考虑的问题; 但它仍然是一个可以探求的目标
- 全局数据包括外部资源 : DB / 数据存储 / File System / 服务 API
- 确保始终将这些资源包装在你所控制的代码之中
- 全局可访问数据是应用程序组件之间耦合的潜在来源
- 最好给每个方法增加一个额外的参数
- Tip 48 : 如果全局唯一非常重要, 那么将它包装到 API 中 ( If It's Important Enough to Be Global, Wrap It in an API )
- …… 但是, 仅限于你真的非常希望它是全局的
- 耦合的代码很难变更, 所以要避免一个地方的修改对其它地方产生副作用
- 让代码害羞一点 : 让它只处理直接知道的事情
- …… 但是, 仅限于你真的非常希望它是全局的
29. 在现实世界中抛球杂耍 ( Juggling the Real World )
- 事件 : 响应事件的应用程序, 使用 4 种策略来帮助来避免紧密耦合
- 1. 有限状态机 ( FSM - Finite State Machine )
- 其实并不困难
- 2. 观察者模式 ( Observer Pattern )
- 观察者根据其兴趣被注册到观察对象上, 这通常由传递一个带调用的函数引用来实现
- 当事件发生时, 被观察对象遍历它的观察者列表, 并调用每个传递给它的函数; 事件作为调用参数提供给函数
- 因为每个观察者都必须与被观察对象注册在一起, 所以引入了耦合
- 在经典的实现中, 回调是由被观察对象以同步的方式内联处理的, 因此可能造成性能瓶颈
- 观察者根据其兴趣被注册到观察对象上, 这通常由传递一个带调用的函数引用来实现
- 3. 发布/订阅 ( Pubsub - Publish / Subscribe )
- 推广了观察者模式, 同时解决了耦合和性能问题
- 4. 响应式编程与流 ( Responsive Programming & Stream )
- 事件流 : 例如, 生成一个包含用户 ID 的被观察事件, 用它来取代那个被观察的对象
- 1. 有限状态机 ( FSM - Finite State Machine )
30. 变换式编程 ( Tramsforming Programming )
- Tip 49 : 编程讲的是代码, 而程序谈的是数据 ( Programming Is About Code, But Programs Is About Data )
- 所有的程序都在变换数据 -- 将输入转换为输出. 开始用变换式方法来设计吧!
- 举例 : 列出目录树中拥有行数最多的 5 个文件
- 使用命令和管道来实现
find . -type f | xargs wc -l | sort -n | tail -5
- 使用命令和管道来实现
- 除了命令行或编程语言中的管道特性, 还有 Java 的 Stream 等
- 举例 : 列出目录树中拥有行数最多的 5 个文件
- 所有的程序都在变换数据 -- 将输入转换为输出. 开始用变换式方法来设计吧!
- Tip 50 : 不要国积状态, 传递下去 ( Don't Hoard State; Pass It Around )
- 不要把数据保持在函数或模块的内部, 拿出来传递下去
- 详见原书程序例子
- stream / pipe 的错误处理怎么做 : 在变换的内部或外部做
- 变换到 "变换式编程"
- 将代码看作一系列 (嵌套的) 变换, 可以为编程打开思路
- 这需要一段时间来适应, 但是一旦你养成这个习惯, 将发现代码变得更简洁, 函数变得更短, 而而删除也变得更平顺
- 详见原书程序例子
- 不要把数据保持在函数或模块的内部, 拿出来传递下去
31. 继承税 ( Inheritance Tax )
- Tip 51 : 不要付继承税 ( Don't Pay Inheritance Tax )
- 考虑一下能更好满足需求的替代方案, 比如接口、委托或 mixin
- 使用继承的两个 (荒谬的) 原因:
- 1. 不喜欢拍键盘 -- 通过继承, 将基类的公共功能添加待子类中
- 2. 喜欢类型 -- 表达类之间的关系
- 其实这两种形式的继承都有问题!
- 1. 继承 就是 耦合 -- 不仅子类耦合到祖先类, 而且使用子类的代码也耦合到所有祖先类
- 2. 继承 定义 新类型 -- 像科学家一样, 将事物分门别类
- 类之间的细微差别逐层叠加, 复杂性增加, 程序更脆弱, 因为一个变化影响太多代码层
- 更好的替代方案
- 接口与协议 ( interface & agreement )
- 委托 ( delegate )
- mixin 与特征 ( mixin & trait? )
- 使用继承的两个 (荒谬的) 原因:
- 考虑一下能更好满足需求的替代方案, 比如接口、委托或 mixin
- Tip 52 : 尽量用接口来表达多态 ( Prefer Interfaces to Express Polymorphism )
- 无需继承引入的藕合, 接口就能明确描述多态性
- 接口与协议之所以如此强大, 是因为可以将它们用作类型, 而实现适当的接口的任何类都将与该类型兼容 ( 例如 List<Locatable> )
- 无需继承引入的藕合, 接口就能明确描述多态性
- Tip 53 : 用委托提供服务 : "有一个" 胜过 "是一个" ( Delegate to Services: Has-A trumps Is-A )
- 不要从服务中继承, 应该包含服务
- TIp 54 : 利用 mixin 共享功能 ( Use Mixins to Share Functionality )
- mixin 不必承担继承税就可以给类添加功能, 而与接口结合可以让多态不再令人痛苦
- 感觉 PHP 的 trait 特征 (特性) 就是一种 mixin
- mixin 不必承担继承税就可以给类添加功能, 而与接口结合可以让多态不再令人痛苦
32. 配置 ( Configuraion )
- Tip 55 : 使用外部配置参数化应用程序 ( Parameterize Your App Using External Configuration )
- 如果代码对一些在应用程序发布后还有可能改变的值有所依赖, 那么就在应用外部维护这些值
- 如果没有外部配置, 代码的适应性和灵活性就会大打折扣, 这是一件坏事吗?
- 在现实世界中, 不适应环境的物种会死亡
- 不要做得太过 ( 走极端 ), 保持灵活性, 根据实际情况的反馈来不断调整
- 如果没有外部配置, 代码的适应性和灵活性就会大打折扣, 这是一件坏事吗?
- 如果代码对一些在应用程序发布后还有可能改变的值有所依赖, 那么就在应用外部维护这些值
章 6 : 并发 ( Concurrency )
33. 打破时域耦合 ( Break Temporal Coupling )
- Tip 56 : 通过分析工作流来提高并发性 ( Analyze Workflows to Improve Concurrency )
- 利用用户工作流中的并发性
- 如果代码给几件事情强加一个顺序进来, 而这个顺序对解决手头问题而言并非必需, 就会发生 时序耦合 ( Temporal Coupling )
- 利用用户工作流中的并发性
34. 共享状态是不正确的状态 ( Shared State is Incorrect State )
- Tip 57 : 共享状态是不正确的状态 ( Shared State is Incorrect State )
- 共享状态会带来无穷的麻烦, 而且往往只有重启才能解决
- 不单单指全局变量; 任何时候, 只要两个或多个代码块持有对同一个可变数据块的引用, 就已经共享了状态 -- 共享状态是不正确的状态
- 信号量 是一个在同一时间只能让一个人持有的东西
- 共享状态会带来无穷的麻烦, 而且往往只有重启才能解决
- Tip 58 : 随机故障通常是并发问题 ( Random Failures Are Often Concurrency Issues )
- 或许时间和上下文的变化能暴露并发 Bug, 但并发 Bug 无法始终保持一致, 也很难重现
- 时间 有两个重要的方面 : 并发性 ( 在同一时刻发生的多件事情 ) 以及次序 ( 事情在时间轴上的相对位置 )
- 或许时间和上下文的变化能暴露并发 Bug, 但并发 Bug 无法始终保持一致, 也很难重现
35. 角色与进程 ( Actors and Processes )
- Tip 59 : 用角色实现并发性时不必共享状态 ( Use Actors For Concurrency Without Shared State )
- 使用角色来管理并发状态, 可以避免显式的同步
- 角色模型
- 角色 : 一个独立的虚拟处理单元, 具有自己的本地 (且私有的) 状态.
- 每个角色都有一个信箱, 当消息出现在信箱中且角色处于空闲状态时, 角色被激活并开始处理消息
- 处理完该条消息后, 它将继续处理信箱中的其他消息, 如果信箱是空的, 则返回休眠状态
- 在处理消息的过程中, 一个角色可以创建其他角色, 可以向其他认识的角色发送消息
- 也可以创建一个新的状态, 用来在处理下一条消息时作为当前状态
- 进程 : 通常代表一种更通用的虚拟处理机, 它一般由操作系统实现, 可以让并发处理更容易
- 进程也能 ( 根据约定 ) 被约束为以角色的形式运转, 我们再这里说的就是这类进程
- 在角色模型中, 不需要为处理并发性编写任何特定代码, 因为没有共享状态
- 对于业务从头到尾的逻辑, 也没有必要以 "做这个, 做那个" 的方式, 将其显式地写在代码里, 因为角色会给予收到的消息自己处理这些事情
- 角色 : 一个独立的虚拟处理单元, 具有自己的本地 (且私有的) 状态.
- Erlang 把角色称为进程, 但不是常规的操作系统进程
- Erlang 的进程是轻量级的 ( 可以在一台机器上运行数百万个进程 ), 其通过发送消息进行通信
- 每一个进程都与其他进程相互隔离, 进程之间没有状态共享
- 此外 Erlang 的运行时实现了一个监督形同, 管理者进程的生命期, 在出现故障时能重启一个进程或一组进程
- 还提供热加载机制 : 可以在不停止正在运行的系统的情况下, 替换该系统中的代码, 号称有 9 个 9 的可用性
- 角色模型
- 使用角色来管理并发状态, 可以避免显式的同步
36. 黑板 ( Blackborads )
- Tip 60 : 使用黑板来协调工作流 ( Use Blackboards to Coordinate Workflow )
- 使用黑板来协调不相关的事实和代理人, 能同时保持参与者之间的独立性和孤立性
- 消息传递系统 就是一种黑板 ( 例如 Kafka / NATS (?) )
- 不但能传递消息, 还能持久化 ( 以时间日志的形式 )
- 消息传递系统 就是一种黑板 ( 例如 Kafka / NATS (?) )
- 使用黑板来协调不相关的事实和代理人, 能同时保持参与者之间的独立性和孤立性
章 7 : 当你编码时 ( While You Are Coding )
37. 听从蜥蜴脑 ( Listen to Your Lizard Brain )
- Tip 61 : 倾听你内心的蜥蜴 ( Listen to Your Inner Lizard )
- 当编程举步维艰时, 其实是潜意识在告诉你有什么地方不对劲
- 害怕空白页
- 开始一个新项目 ( 甚至是现有项目中的一个新模块 ) 有可能让人不安, 因此我们中的许多人宁愿推迟迈出第一步
- 造成这种情况的两个原因 :
- 1. 直觉试图告诉你在感知面下潜藏着某种形式的疑虑
- 你已经尝试过很多东西, 并已了解哪些是有效的, 哪些是无效的
- 一直积累的经验和智慧, 是否在试图告诉些什么?
- 2. 可能只是担心自己会犯错 : "冒名顶替症候群"
- ( 症状是怀疑自身能力, 以为自己的成功都来自外界因素 )
- 认为这个项目超出了能力范围 -- 看不到通往终点的路, 只好继续远行, 直到最后被迫承认自己迷路
- 1. 直觉试图告诉你在感知面下潜藏着某种形式的疑虑
- 注意你对代码的疑虑 -- 这可能超过了本应具有的难度
- 也许结构或设计是错误的, 也许你解决了错误的问题
- 也许你只是创造了一个只会招来 bug 的蚂蚁农场
- ( 这部分 "软技能" 内容, 详见原书, 不再摘录了 )
- 害怕空白页
- 当编程举步维艰时, 其实是潜意识在告诉你有什么地方不对劲
38. 巧合式编程 ( Programming by Coincidence )
- Tip 62 : 不要依赖巧合编程 ( Don't Program by Coincidence )
- 只能依赖可靠的事物. 注意偶然事件的复杂性, 不要混淆快乐的巧合与有目的的计划
- 因为靠运气和意外来获得成功是行不通的, 编程应该深思熟虑
- 人类天生就善于发现模式和归咎原因, 即使那些东西只是巧合
- 不要假设, 要证明 ( Don't assume it, prove it. )
- 找到恰好能用的答案和找到正确的答案不是一回事
- ( 但是为了应付工期紧迫的任务, 可能敷衍了事 )
- 只能依赖可靠的事物. 注意偶然事件的复杂性, 不要混淆快乐的巧合与有目的的计划
39. 算法速度 ( Algorithm Speed )
- Tip 63 : 评估算法的级别 ( Estimate the Order of Your Algorithms )
- 在开始编程前, 对这件事情大概会花多长时间要有概念
- Estimate the resources that algorithms use : time, processor, memory and so on.
- 如果你有一个 O(n^2) 的算法, 试试寻找一个分治的方法把它降到 O(n*lg(n))
- 在开始编程前, 对这件事情大概会花多长时间要有概念
- Tip 64 : 对估算做测试 ( Test Your Estimates )
- 针对算法的数学分析无法说明所有问题, 尝试在目标环境中测试一下执行代码的耗时
- 最好的不会永远最好 ( Best isn't always best. )
- 针对算法的数学分析无法说明所有问题, 尝试在目标环境中测试一下执行代码的耗时
40. 重构 ( Refactoring )
- Tip 65 : 尽早重构, 经常重构 ( Refactor Early, Refactor Often )
- 像除草和翻整花园那样, 只要有需要就对代码进行重新编写、修订和架构, 以便找到问题的根源并加以修复
- 软件开发最常见的隐喻是 "建筑的构建"
- 建筑师绘制蓝图 -> 承包商施工装修 -> 住户搬来居住
- 但是, "园艺" 的隐喻更接近于现实的软件开发 ( 真正恰当的隐喻 )
- 软件开发更像是园艺而非建筑, 它更像一个有机体而非砖石堆砌
- 根据最初的计划和条件, 在花园里种植很多花木, 有些茁壮成长, 另一些注定要成为堆肥
- 你会改变植物相对的位置, 利用光和影、风和雨的相互作用
- 过度生长的植物会被分栽或修剪, 不协调的颜色会转移到更美观的地方
- 你拔除杂草, 给需要额外帮助的植物施肥
- 不断地检测花园的健康状况, 并根据需要 ( 对土壤、植物、布局 ) 做出调整
- 商务人士对建筑的隐喻感到很舒服 : 它比园艺更科学, 是可重复的, 管理上有严格汇报层次结构, 等等
- 但是编程并不是在建造摩天大楼 -- 也没有受到物理和现实世界的限制
- 也许某个例程日渐庞大, 或许是它想要完成的事情太多, 所以需要一分为二
- 无法得到计划中结果的东西需要被删除或修剪
- 重构 :重组现有代码实体、改变内部结构而不改变其外部行为的规范式技术
- "Discilined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior."
- 重构不是一种特殊的、隆重的、偶尔进行的活动
- 为了重新种植而在整个花园中翻耕, 重构不是这样的活动
- 重构是一项日复一日的工作, 需要采取低风险的小步骤进行, 更像是耙松和除草这类活动
- 这是有针对性的、精确的方法, 有助于保持代码易于更改, 而不是对代码库进行自由的、大规模的重写
- 时间压力 常常被用作不重构的借口, 但是这个借口根本站不住脚
- 如果现在不进行重构, 那么以后就需要投入更多的时间来解决问题 -- 因为需要处理更多的依赖关系
- 到时会有更多的时间吗? 不可能有
- 软件开发最常见的隐喻是 "建筑的构建"
- 像除草和翻整花园那样, 只要有需要就对代码进行重新编写、修订和架构, 以便找到问题的根源并加以修复
41. 为编码测试 ( Test to Code )
- Tip 66 : 测试与找 Bug 无关 ( Testing Is Not About Finding Bugs )
- 测试是观察代码的一个视角, 可以从中得到针对设计、接口和耦合度的反馈
- 测试不是关于找 bug 的工作, 而是一个从代码中获取反馈的过程, 涉及设计的方方面面, 以及 API、耦合度等
- 这意味着, 测试的主要收益来自于你的思考和编写测试期间, 而不是运行测试那一刻
- 为方法写一个测试的考虑过程, 使我们得以从外部看待这个方法, 这让我们看起来是代码的客户, 而不是代码的作者
- 测试不是关于找 bug 的工作, 而是一个从代码中获取反馈的过程, 涉及设计的方方面面, 以及 API、耦合度等
- 测试是观察代码的一个视角, 可以从中得到针对设计、接口和耦合度的反馈
- Tip 67 : 测试是代码的第一个用户 ( Test Is the First User of Your Code )
- 用测试的反馈来引导工作
- 与其他代码紧密耦合的函数或方法很难进行测试
- 因为你必须在运行方法之前设置好所有环境, 所以让你的东西可测试也减少了它的耦合
- 在你能对一个东西做测试之前, 必须先理解它
- 虽然听起来很傻, 但现实中我们开始编写代码, 都只能基于对必须要做的事情的模糊理解
- 我们打算边干边解决, 过程中不断解决各种新的问题, 代码变得比应有的长度长数倍
- 如果用测试先探照一下代码, 事情就会变得清楚 -- 在开始编码之前, 就考虑过测试边界条件及其工作方式, 就更可能发现简化函数的逻辑模式
- TDD - Test Driven Development : "既然预先考虑测试有那么多好处, 为什么不先把它们先写出来呢?"
- TDD 的设计倾向于 "自底向上" ( Bottom Up ). 务必实践一下 TDD, 但过程中不要忘记时不时停下来看看大局
- 人们很容易被 "测试通过" 的绿色消息所诱惑, 从而编写大量的代码, 但实际上这些代码并不能让你离解决方案更近
- 自上而下 V.S.自下而上
- 自上而下 : 应该从试图解决的整个问题开始, 把它分解成几块 ( 分治 )
- 然后逐步拆分成更小的快, 以此类推, 知道最后得到小到可以用代码表示的块为止
- 自下而上 : 主张构建代码就像 构造房子
- 从底层开始, 生成一层代码, 为这些代码提供一些更接近目标问题的抽象, 然后添加一层具有更高层次的抽象
- 整个过程会持续下去, 直到所要解决的问题的抽象出现
- 实际上这两派都没有成功, 因为它们都忽略了软件开发中最重要的一个方面 : 我们不知道开始时在做什么
- 自上而下学派认为 : 可以提前表达整个需求, 然而实际做不到
- 自下而上学派假设 : 能构建出一系列抽象, 这串抽象最终会将他们带到一个单一的顶层觉接方案, 但是当不知道方向时, 如何决定每一层的功能呢?
- 自上而下 : 应该从试图解决的整个问题开始, 把它分解成几块 ( 分治 )
- ( 讨论过程详见原书, 这里就不摘录更多了 )
- 与其他代码紧密耦合的函数或方法很难进行测试
- 用测试的反馈来引导工作
- Tip 68 : 既非自上而下, 也不自下而上, 基于端对端构建 ( Build End-to-End, Not Top-Down or Bottom Up )
- 创建一小块端到端的功能, 从中获悉问题之所在
- 我们坚信 : 构建软件的唯一方法是增量式的
- 构建端到端功能的小块, 一边工作一边了解问题
- 应用学到的知识持续充实代码, 让客户参与每一个步骤并让他们知道这个过程
- 测试对开发的驱动绝对能有帮助, 但是就像每次驱动骑汽车一样, 除非心里有一个目的地, 否则就可能会兜圈子
- 我们坚信 : 构建软件的唯一方法是增量式的
- 创建一小块端到端的功能, 从中获悉问题之所在
- Tip 69 : 为测试做设计 ( Design to Test )
- 写下代码之前先从测试角度思考
- 需要从一开始就在软件中构建可测试性, 并在尝试将每个部分连接在一起之前, 对它们进行彻底的测试
- 回归测试 : 与相同测试的前一次运行结果比较
- 在调试完之后, 需要把这个临时测试正式化
- 如果代码出过一次问题, 就有再出问题的可能性
- 不要讲创建出来的测试扔掉, 把它添加到现有的单元测试库中
- 一般来说, 可以留一个特性开关, 为特定用户或用户组启用额外的诊断信息 ( 详见原书例 )
- 写下代码之前先从测试角度思考
- Tip 70 : 要对软件做测试, 否则只能留给用户去做 ( Test Your Software, or Your Users Will )
- 无情地测试, 不要等用户来帮你找 Bug
- 毫无疑问, 测试是编程的一部分, 不该留给其他部门的人去做
- 测试, 设计, 编码 -- 都是在编程
- 毫无疑问, 测试是编程的一部分, 不该留给其他部门的人去做
- 无情地测试, 不要等用户来帮你找 Bug
42. 基于特性测试 ( Property-Based Testing )
- Tip 71 : 使用基于特性的测试来校验假设 ( Use Property-Based Tests to Validate Your Assumptions )
- 基于特性的测试将会进行你从未想过的尝试, 并会以你不曾打算采用的方式操练你的代码.
- 让计算机来做一些测试 -- 自动生成一些测试值
- 基于契约式设计 : 当你的输入满足条件时, 它对输出做出一定的保证
- 基于代码不变式 : 一个函数执行后, 某部分的状态保持为真
- ( 详见原书例 )
- 让计算机来做一些测试 -- 自动生成一些测试值
- 基于特性的测试将会进行你从未想过的尝试, 并会以你不曾打算采用的方式操练你的代码.
43. 出门在外注意安全 ( Stay Safe Out There )
- Tip 72 : 保持代码简洁, 让攻击面最小 ( Keep It Simple and Minimize Attack Surfaces )
- 复杂的代码给 Bug 以滋生之沃土, 给攻击者以可趁之机
- 应该始终牢记的一些基本原则
- 1. 将攻击面的面积最小化 ( Minimize Attack Surface Area )
- 2. 最小特权原则 ( Principle of Least Privilege )
- 3. 安全的默认值 ( Secure Defaults )
- 4. 敏感数据要加密 ( Encrypt Sensitive Data )
- 5. 维护安全更新 ( Maintain Security Updates )
- ( 详见原文讨论 )
- 应该始终牢记的一些基本原则
- 复杂的代码给 Bug 以滋生之沃土, 给攻击者以可趁之机
- Tip 73 : 尽早打上安全补丁 ( Apply Security Patches Quickly )
- 攻击者会尽可能快地部署攻击, 你必须快上加快
- 密码的反模式 : 好的安全性常常与尝试或管理背道而驰
- 严格的密码策略实际上会降低安全性. 美国 NIST 的建议
- 不要讲密码长度限制在 64 个字符以内 -- NIST 推荐 256 为最佳长度
- 不要截断用户选择的密码
- 不要限制特殊字符 -- NIST 表示接受所有可打印的 ASCII 字符、空格和 Unicode
- 不要向未经身份认证的用户提供密码提示, 或提示输入特定类型的信息
- 例如, 你的第一只宠物叫什么名字
- 不要禁用浏览器中的粘贴功能
- 破坏浏览器和密码管理器的功能, 并不能使系统更安全
- 实际上, 它会促使用户创建更简单、更短、更容易破解的代码
- 因此美国 NIST 和英国的国家网络安全中心都特别要求校验方允许粘贴功能
- 不要强加其它组合规则
- 例如, 不要强制要求任何特定的大小写混合、数字或特殊字符, 或禁止重复字符
- 不要蛮横地要求用户在一段时间后更改密码
- 只有在有正当理由的情况下才这样做. 例如, 系统遭到了破坏
- 我们应该鼓励长的随机的密码, 因为它有更高程度的熵
- 人为的限制局限了信息熵, 助长了使用糟糕密码的习惯, 让用户的账户更容易被接管
- 攻击者会尽可能快地部署攻击, 你必须快上加快
44. 事物命名 ( Naming Things )
- Tip 74 : 好好取名; 需要时更名 ( Name Well; Rename When Needed )
- 用名字向读者表达你的意图, 并且在意图改变时及时更名
- What’s in a name? When we're programming, the answer is "everything!"
- We create names for applications, subsystems, modules, functions, variables -- we're constantly creating new things and bestowing names on them.
- And those names are very, very important, because they reveal a lot about your intent and belief.
- We believe that things should be named according to the role they play in your code.
- This means that, whenever you create something, you need to pause and think "what is my motivation to create this?"
- This is a powerful question, because it takes you out of the immediate problem-solving mindset and makes you look at the bigger picture.
- When you consider the role of a variable or function, you’re thinking about what is special about it, about what it can do, and what it interacts with.
- Often, we find ourselves realizing that what we were about to do made no sense, all because we couldn't come up with an appropriate name.
- ( 一旦我们怎么都想不出一个适合它的名字, 旺旺就会幡然醒悟, 意识到这件事情其实毫无意义 )
- 文字和颜色不匹配 : 读出文字 ( 例如, 红蓝黑白) / 读出字的颜色
- ( 详见原书例, 是巧妙而恰当的举例 )
- 大脑很尊重书面问题 -- 我们需要确保使用的名字不辜负这一点
- 尊重文化 ( Honor the Culture )
- 命名取决于特定编程语言或环境所处的文化氛围, 以及社区习惯
- 一致性 : 保持对团队有特殊意义的术语的一贯性
- 重要的是, 团队中的没一个人都知道这些词的意思, 并始终如一地使用它们
- 使用项目术语表, 列出对团队有特殊意义的术语
- 命名是软件开发中最困难的事情之一
- 不得不给很多东西起名字, 而我们的选择的名字在很多方面决定了所创造的最终是什么
- 在编写代码时, 需要注意任何潜在的语义偏移
- What’s in a name? When we're programming, the answer is "everything!"
- 用名字向读者表达你的意图, 并且在意图改变时及时更名
章 8 : 项目启动之前 ( Before the Project )
45. 需求之坑 ( The Requirements Pit )
Perfection is achieved, not when there is nothing left to add but when there is nothing left to take away... ( 所谓完美境界, 亦非加无可加, 而是减无可减. )
-- Antoine de St. Exupery, Wind, Sand, and Stars, 1939
- Tip 75 : 无人确切知道自己想要什么 ( No One Knows Exactly What The Want )
- 他们或许知道大概的方向, 但不会了解过程的曲折
- 尽量要在开始之前就理解整个问题
- 需求很少停留在表面. 通常情况下, 他们被埋在层层的假设、误解和政治之下. 更糟糕的事, 需求通常根本不存在
- 现实世界是混乱的、矛盾的、未知的. 在现实世界中, 得到任何事物的精确规范, 即使不是完全不可能, 也是非常罕见的
- 他们或许知道大概的方向, 但不会了解过程的曲折
- Tip 76 : 程序员帮助人们理解他们想要什么 ( Programers Help People Understand What They Want )
- 软件开发更像是一种由用户和程序员协同创造的行为
- 典型的客户会带着需求来找我们. 这种需求可能是战略性, 但更可能是战术性的 : 对当下面临的问题做一个回应
- 新手开发人员经常犯的错误是 : 把这种对需求的声明照单全收, 然后实现对应方案
- 根据经验, 最初对需求的声明, 往往并非绝对化的要求. 客户可能没有意识到这一点, 但一定希望你能一起去探索
- ( 详见原书例, 很有代表性 )
- 软件开发更像是一种由用户和程序员协同创造的行为
- Tip 77 : 需求是从反馈循环中学到的 ( Requirements Are Learned in a Feedback Loop )
- 理解需求需要探索和反馈, 因此决策的结果可以用来完善最初的想法
- Tip 78 : 和用户一起工作以便从用户角度思考 ( Work with a User to Think Like a User )
- 这是看透系统将如何被真正使用的最佳方法
- Tip 79 : 策略即元数据 ( Policy Is Metadata )
- 不要将策略硬编码进系统, 而应该将其表达为系统的一组元数据
- "只有员工的主管和人事部门可以查看该员工的记录" -- 在一份绝对性陈述中嵌入了一条业务策略
- 如果需求被声明为 "只有主管和人事可以查看员工记录"
- 更可能会为每次访问该数据编写一个显式的测试
- 如果声明为 "只有授权用户才能访问员工记录"
- 更可能会设计并实现某种访问控制系统 : 当策略改变时, 只需要更新该系统的元数据
- 不要将策略硬编码进系统, 而应该将其表达为系统的一组元数据
- Tip 80 : 使用项目术语表 ( Use a Project Glossary )
- 为项目的所有特定词汇创建一张术语表, 并且在单一源头维护
- 我们相信, 最好的需求文档, 或许也是唯一的需求文档, 就是可以工作的代码 ( 不够理解? )
- 简短的描述通常被称为 用户故事 ( user stories )
- 通过保持需求的简短陈述, 鼓励开发人员去澄清问题
- 生成需求文档的另一大危险是过于具体. 好的需求是抽象的
- 就需求而言, 最简单最能准确反映业务需求的语句是最好的
- 需求不是架构; 需求无关设计, 也非用户界面; 需求就是需要的东西
- 项目所有的参与者, 包括最终用户和支持员工, 都应该使用同一张术语表来确保一致性
- 如果大家用不同的名称来称呼相同的事物, 更糟糕的是, 使用相同的名称来指代不同的事物, 那么项目就很难取的成功
- 为项目的所有特定词汇创建一张术语表, 并且在单一源头维护
46. 处理无法解决的难题 ( Solving Impossible Puzzles )
- Tip 81 : 不要跳出框框思考 -- 找到框框 ( Don't Think Outside the Box -- Find the Box )
- 在面对无法解决的难题时, 识别出真正的约束. 可以问自己 : "必须 这样做才能搞定吗? 必须搞定它吗?"
- 如果 "框框" 是约束和条件的便捷, 那么诀窍就是找到框框, 他可能比你想象的要大得多
- 解决谜题的关键是, 认识到你所收到的约束和你所拥有的的自由度, 因为认识到这些就会找到答案
- 这就是为什么谜题如此有效 (而巧妙) -- 我们太容易忽视潜在的解决方案
- ( 详见原书例, 非常巧妙恰当 )
- 你必须挑战任何先入之见, 并评估它们是否是真实的、硬性的约束
- 在面对无法解决的难题时, 识别出真正的约束. 可以问自己 : "必须 这样做才能搞定吗? 必须搞定它吗?"
47. 携手共建 ( Working Together )
-
Tip 82 : 不要一个人埋头钻进代码中 ( Don't Go into the Code Alone )
- 编程往往困难又费力, 找个朋友和你一起干
- 结对编程
- 不同的人有不同的背景和经验, 有不同的解决问题的技巧和方法, 对任何特定的问题有不同的关注点
- 充当打字员的开发者必须专注于语法和编码风格的底层细节, 而另一个人可以自由地在更高层次的范畴考虑问题
- 我们人类的大脑带宽有限, 天马行空地输入编译器勉强能接受的深奥单词和符号, 就已占用了我们相当大的处理能力
- 第二个人带来的同伴压力, 有助于克服脆弱的瞬间, 以及避免把变量起名为 foo 这样的坏习惯
- 当有人盯着的时候, 也不太可能去走那些让你事后尴尬的捷径, 有利于提高软件质量
- 结对编程
- 编程往往困难又费力, 找个朋友和你一起干
-
Tip 83 : 敏捷不是一个名词; 敏捷有关你如何做事 ( Agile is Not A Noun; Agile Is How You Do Things )
- 敏捷是一个形容词, 有关如何做事情
- 敏捷宣言中的价值观:
- 个体与互动 高于流程和工具 ( Individuals and interactions over processes and tools )
- 工作的软件 高于详尽的文档 ( Working software over comprehensive documentation )
- 客户合作 高于合同谈判 ( Customer collaboration over contract negotiation )
- 响应变化 高于遵循计划 ( Responding to change over following a plan )
- 敏捷宣言中的价值观:
- 敏捷是一个形容词, 有关如何做事情
48. 敏捷的本质 ( The Essence of Agility )
章 9 : 务实的项目 ( Pragmatic Projects )
49. 务实的团队 ( Pragmatic Teams )
At Group L, Stoffel oversees six first-rate programmers, a managerial challenge roughly comparable to herding cats. ( 在 L 组里, 斯托弗管理着六个一流的程序员, 这在管理上的挑战与羊毛差不多. )
-- The Washington Post Magazine, June 9, 1985
- Tip 84 : 维持小而稳定的团队 ( Maintain Small, Stable Teams )
- 团队应保持稳定、小巧, 团队中的每个人都应相互信任、互相依赖
- 程序员有点像猫 : 聪明、意志坚强、固执己见、独立, 并且经常引起网络崇拜
- 务实的团队
- 如果团队的成员经常被分配到其它地方, 相互之间缺乏了解, 那么这也不是一个团队, 他们只是暂时同在一个公交车站躲雨的陌生人
- 务实的团队很小, 充其量也就 10-12 人左右, 成员很少进出. 每个人都很了解彼此, 相互信任, 互相依赖
- 禁止破窗
- 质量是一个团队问题. 即使是最勤奋的开发者, 只要身处一个什么都不在乎的团队中, 也会发现自己很难保持修复琐碎问题的热情
- 如果团队不鼓励开发者在这些修复工作上花费时间, 那么问题就会进一步恶化
- 煮熟的青蛙 : 整个团队甚至更容易被一锅炖熟
- 团队应保持稳定、小巧, 团队中的每个人都应相互信任、互相依赖
- Tip 85 : 排上日程以待其成 ( Schedule It to Make It Happen )
- 如果你不把事情纳入日程表, 它们就不会发生. 反思、实验、学习、提高技能, 这些事都应放入日程表
- "只要有空闲时间" 就去做, 意味着这件事永远不会发生
- 在外人看来, 最糟糕的团队就是那些看起来闷闷不乐、沉默寡言的团队
- 他们的会议组织混乱, 没有人愿意发言; 电子邮件和项目文档一团糟: 每一个都使用这不同的术语, 没有那两样东西看起来是相同的
- 优秀的项目团队有独特的个性
- 人们期待与他们会面, 因为知道他们准备得很充分, 会让看到他们表现的每个人都心情愉悦
- 他们生成的文档时清晰、准确和一致的. 团队用同一个声音说话, 甚至可能还不乏幽默感
- 如果你不把事情纳入日程表, 它们就不会发生. 反思、实验、学习、提高技能, 这些事都应放入日程表
- Tip 86 : 组织全功能的团队 ( Orgaize Fully Functional Teams )
- 围绕功能而不是工作职能组织团队. 不要将 UI/UX 设计者从程序员中分离出去, 也不要分开前端和后端; 不要区分数据建模者和测试人员, 以及开发和设计. 构建一个团队, 这样你就可以渐进地不断迭代端到端的代码
50. 椰子派不上用场 ( Coconuts Don't Cut It )
- Tip 87 : 做能起作用的事, 别赶时髦 ( Do What Works, Not What's Fashionable )
- 不要仅仅因为别的公司正在那么干就采纳一项技术或采用一个开发方法, 而是要采用自己所处环境中对团队有效的东西
- 例如, 宣称在使用 Scrum 的团队, 实际上他们只是套用了那些概念和术语, 并没有领会其中的要义 ( 详见原书例 )
- How do you know “what works”? You rely on that most fundamental of Pragmatic techniques: Try it.
- ( 怎样才能知道 "什么能起作用"? 有一个最基本的使用技巧可以依靠, 那就是 : 试一试 )
- 不要仅仅因为别的公司正在那么干就采纳一项技术或采用一个开发方法, 而是要采用自己所处环境中对团队有效的东西
- Tip 88 : 在用户需要时交付 ( Deliver When Users Need It )
- 不要卡着流程要求, 刻意等到几周甚至几个月后才交付
- 需要坚实的基础设施 ( 例如, CI 持续集成 )
- 不要卡着流程要求, 刻意等到几周甚至几个月后才交付
51. 务实的入门套件 ( Pragmatic Starter Kit )
Civilization advances by extending the number of important operations we can perform without thinking. ( 文明的进步是以增加那些不需要思考就能完成的重要操作来实现的 )
-- Alfred North Whitehead
- Tip 89 : 使用版本控制来驱动构建、测试和发布 ( User Version Control to Drive Builds, Tests and Releases )
- 利用提交或推送来触发构建、测试、发布, 利用版本控制的标签来进行生产部署
- 应该考虑每个团队需要的最基本、最重要的元素是什么, 而不是去考虑方法、语言或技术栈
- 务实的入门套件 -- 版本控制、回归测试、完全自动化
- 利用提交或推送来触发构建、测试、发布, 利用版本控制的标签来进行生产部署
- Tip 90 : 尽早测试, 经常测试, 自动测试 ( Test Early, Test Often, Test Automatically )
- 每次构建都跑一下的测试, 要比束之高阁的测试计划有效得多
- Tip 91 : 直到所有的测试都已运行, 编码才算完成 ( Coding Ain't Done 'till All the Tests Run )
- 无须多言
- Tip 92 : 使用破坏者检测你的测试 ( Use Saboteurs to Test Your Testing )
- 在一个单独的源码副本中特意引入 Bug, 验证测试能否将其捕获
- Tip 93 : 测试状态覆盖率, 而非代码覆盖率 ( Test Your State Coverage, Not Code Converage )
- 要识别并测试重要的程序状态, 只测试一行行的代码是不够的
- Tip 94 : 每个 Bug 只找一次 ( Find Bugs Once )
- 只要人类测试者找到一个 Bug, 就应该是该 Bug 最后一次被人类发现. 从此之后, 自动化测试完全可以发现它
- 单元测试 / 回归测试
- 只要人类测试者找到一个 Bug, 就应该是该 Bug 最后一次被人类发现. 从此之后, 自动化测试完全可以发现它
- Tip 95 : 不要使用手动程序 ( Don't Use Manual Procedures )
- 计算机能一次又一次, 按照同样的次序, 执行相同的指令
- 完全自动化
- 计算机能一次又一次, 按照同样的次序, 执行相同的指令
52. 取悦用户 ( Delight Your Users )
- Tip 96 : 取悦用户, 而不要只是交付代码 ( Delight Users, Don't Just Deliver Code )
- 为用户开发能够带来商业价值的解决方案, 并让他们每天都感到愉快
- Tip 97 : 在作品上签名 ( Sign Your Work )
- 过去的工匠在为他们的作品签名时非常自豪, 你也应该这样
- 保持匿名会滋生粗心、错误、懒惰和糟糕的代码, 特别实在大型项目中
- 很容易把自己看成只是大齿轮上的一个小齿, 在无休止的工作汇报中制造蹩脚的接口, 而不是写出好的代码
- 保持匿名会滋生粗心、错误、懒惰和糟糕的代码, 特别实在大型项目中
- 过去的工匠在为他们的作品签名时非常自豪, 你也应该这样
53. 傲慢与偏见 ( Pride and Prejudice )
跋
- Tip 98 : 先勿伤害 ( First, Do No Harm )
- 犯错在所难免, 确保犯错后没人会因此受难.
- Tip 99 : 不要助纣为虐 ( Don't Enable Scumbags )
- 因为这样做你也有变成纣王的风险
读者跋
- 适合刚毕业从事编程工作一两年的人来阅读
- 不少内容算是常识, 工作久一点也能了解到
- 但对于领悟力一般的人来说, 早点知道或许能少走许多弯路.
- 工作久了, 经历多了, 对其中一些内容感同身受
- 但从另一个角度来说, 从中得到的收获也少了
- 毕竟不少内容已经从他处学到了, 或者已经通过自己的血泪领悟到了…