《高效程序员的45个习惯 - 敏捷开发修炼之道》之读书笔记

《高效程序员的45个习惯 - 敏捷开发修炼之道》之读书笔记
Photo by Welkin Chen / Unsplash

多年前阅读此书时,如醍醐灌顶,从那之后,这些书里面的核心思想,慢慢渗透到我的开发生涯之中,受益良多,把内容分享出来,时时阅读。

  • 金句子
    • 所有学习上的成功,都只靠两件事: 策略和坚持,而坚持本身就应该是最重要的策略之一
    • 只有明白”为什么做“,才能解决”如何做“的问题
    • 敏捷就是管理变化的
    • 程序员在拒绝设计的同时,也就放弃了思考
    • 敏捷并不止是针对代码开发,它存在于软件工程的所有环节
    • 先解决困难的问题,简单的问题留到最后
    • 敏捷依赖人,而不是依赖于项目的甘特图和里程表
    • 如果你没有犯任何错误,说明你可能没有努力去工作
    • 很多开发者由于脏乱代码和看不懂的逻辑而秃顶
    • 每个敏捷开发的"时间盒"必须是短期的
    • 好的设计应该是正确的,而不应该是精确的
    • 敏捷的一大特点就是持续开发,持续保证可开发,持续保证可发布
    • 敏捷使用短迭代
      • 给我一份详细的长期计划,我就会给你一个注定完蛋的项目
      • 在小且重复的周期中,增量完成各种开发任务: 分析、设计、实现、测试和获得反馈
    • 敏捷开发给我的感觉
      • 专注且效率
      • 能看到一个实际并且确切的目标
      • 不会遗留长期悬而未决的问题
    • 硬件成本比开发人员的时间便宜
    • 开发人员应该为能够创造出来一个简单并且可用的设计而骄傲,而不是以程序的复杂性为荣
    • 什么是敏捷
      • 对敏捷的错误理解
        • 越快越敏捷
        • 只写代码不写文档
        • 需要重构而无需设计
        • 敏捷迭代就是尽量做到最小
        • 敏捷只有天才程序员才能用
      • 本意: lightweight processed(轻量型软件开发过程)
      • 概括 -> 敏捷开发是在一个高度协作的环境中,不断地使用反馈进行自我调整和完善
    • 敏捷不是目的,而是手段
    • 敏捷开发宣言
      • 个体和交互胜过过程和工具
      • 可工作的软件胜过面面俱到的文档
      • 客户协作胜过合同谈判
      • 响应变化胜过遵循几乎
    • 敏捷精神
      • 要求团队中的每个人都有专业的工作态度
        • 并非是所有人都是有经验的专业人员
        • 每个人都希望尽最大可能做好自己的工作
      • 持续推进系统前进和完善
        • 需求采集
        • 开发
        • 技术技能的学习
        • 产品部署
        • 用户培训
    • 如何操作
      • 防微杜渐
        • 把问题解决在萌芽状态
      • 可行性先行
        • 探索未知领域,在大量成本投入之前先确定其可行性
      • 持续优化团队协作
        • 往往是小型团队
        • 共享代码和任务
        • 大部分时间能在一起工作
      • 使用自动化工具构建、测试和集成
      • 持续重构
        • 编码永远没有真正意义的结束
      • 以小步迭代的方式工作
        • 尽可能的频繁给客户演示迭代的最新的系统
  • 态度决定一切
    • 做事
      • 出了问题,最高优先级是解决问题,而不是找到解决掉制造问题的人
      • 一再伤害团队的成员需要让他离开
      • 如果大部分成员(尤其是领导者)不职业,那么你就应该主动从团队离开,没必要跟随"死亡之旅"
    • 欲速则不达
      • 出现bug,不要快速修复,而是要真正从根源修复
      • 提升代码的清晰度,让代码变得易读
      • 要理解团队开发方法,不要孤立地编码
      • 使用单元测试
    • 对事不对人
      • 公正的去讨论
        • 不要把对方案设计和实现的讨论,失控成为情绪化的指责
          • 每个人都能有一些极好的创新想法,同样也会萌生很蠢的想法
          • 负面的评论会扼杀创新
      • 尽最大可能放大方案的优缺点
      • 设立仲裁人
        • 防止开会时明星员工操纵会议,并及时打断务虚发言
      • 支持已经做出的决定
        • 一旦方案被确定,就必须通力合作
  • 学无止境
    • 只有变化是不变的,要拥抱变化
    • 迭代和增量式学习
      • 每段时间给制定一个新技术的学习目标
      • 实操
        • 要明白为什么需要这项新技术
          • 它可以解决什么问题,用在什么地方
        • 避免为了学习新技术而直接将应用切换到新技术。必须全面评估造成的影响
    • 对团队投资
      • 找出团队中高手擅长的领域,帮助其他成员迎头赶上
      • 坚持有计划地举行分享
    • 懂得丢弃
      • 丢弃旧的不好的习惯和思维定式
    • 不停地问"为什么"
      • 深入挖掘背后的原因,单点突破,由点方可及面
  • 交付用户想要的软件
    • 真正的敌人,是变化
      • 变化的需求
      • 变化的市场
      • 成功的软件开发方法,取决于你识别和适应变化的能力
      • 保持项目随时可以发布
        • 提早实现自动化部署
          • 在开发之前就要进行全面自动化部署,而不是等到上线之前
          • 能运行在开发环境,也能运行在生产环境
          • 最大程度避免手工操作,且应该简单、可靠及可重复
      • 提早集成,频繁集成
        • 集成的周期越长,成功的风险就越高,代价就越大
        • 避免大范围的修改代码
        • 将持续集成作为编码的日常部分
    • 明确客户/开发人员职责
      • 方案设计时,必须有开发参与
      • 业务方面的决定,要让客户来做
      • 实操
        • 记录客户做出的决定,并注明原因,PS: 使用方便的工具,便于同步
        • 从开发角度,给客户提出好的业务建议
    • 设计和编码都在变化
      • 一旦开始,最初的设计和实际编码都处于不断变化中
        • 需合理性的更新维护设计文档
      • 好的设计应该是正确的,而不应该是精确的
        • 战略级别的设计不应说明程序方法、参数、字段等,详细战术设计文档需包含这些。
      • 不要在前期做大量的设计
    • 合理地使用新技术
      • 避免"新技术-简历驱动设计"
      • 避免出现不可控的"新技术大杂烩"
      • 新技术真的能解决问题吗
      • 新技术维护成本多大
      • 新技术的引入,必读多问挑剔性的问题
      • 如果还没有足够的经验,不要急于决定用什么技术
      • 不要开发哪些你容易下载到的东西
    • 用户侧
      • 统一好用户软件安装所需要的各个版本、数据库、操作系统等
      • 在没有征得用户同意前,绝不能删除用户的数据
      • 软件需保证可以完整的正确安装,卸载,并升级(如果有的话)
        • 尤其是在质量保证人员的电脑上
        • 保证考虑了用户侧的设备,网络和权限
      • 尽量降低维护安装脚本,把这一切做的最简化
      • 频繁给客户演示
        • 使用演示获得频繁的反馈
          • 减少与客户的理解的需求偏差,最大程度接近真实需求
          • 提早发现环境或者功能的bug
        • 跟踪问题
          • 反馈类型
            • 修正
            • 建议
            • 变更需求
            • 功能增强
            • bug修复
            • ...
          • 需保持对反馈的记录
        • 演示周期根据需求和用户的反馈,动态调整演示周期
      • 需维护一份项目术语表,统一跟用户的术语认知
      • 如何报价
        • 使用迭代来报价
          • 每个迭代周期,提供部分功能,从而判断出一个相对合理的报价
        • 避免为整体项目直接报价
          • 当然也需要具体针对项目整体报价的能力,毕竟市场如此...
  • 敏捷反馈
    • 测试
      • 清楚要测试的内容
        • 确保测试是可重复的
        • 测试你的边界条件
        • 不要放过任何一个失败的测试
      • 一定要有本地单元测试,一旦单元测试到位,方便回归测试和重构代码
      • 要使用自动化的单元测试
        • 搭建机器,不断获取最新版本代码,然后编译代码,并运行单元测试
    • 单元测试
      • 及时提供反馈
        • 针对修改或者重写的逻辑,测试用例会检查你是否破坏了已有的功能,可以得到快速反馈
      • 让代码更加健壮
        • 帮你练习正面、反面以及异常情况
      • 是有用的设计工具
        • 有助于实现简单的、注重实效的设计
      • 单元测试会天然强制代码分层
      • 是你自信的根本
        • 减少盲区,提供稳定性
      • 是可信的文档
      • 是学习工具
        • 通过测试新技术的API,加深理解
      • 单元测试需达到一定的测试覆盖率的时候,才能真正发挥作用
        • 可使用一些测试覆盖率工具来了解
    • 不同环境,就有不同问题
      • 使用持续集成工具,在每一种支持的平台和环境中运行单元测试,持续保证每个环境下软件的可用性
    • 错误的反馈最好统一到一个地方,而不是因为一个错误而收到5次通知轰炸
    • 度量真实的进度
      • 不要用不恰当的度量来欺骗自己和团队
        • 要诚实
        • 要找到更加合适的方式
      • 使用TO-DO来评估还需要多少小时
    • 倾听用户的声音
      • 每一个抱怨的背后都隐藏了真正的问题,即时这个抱怨很low,很愚蠢
    • 举办项目复盘会
      • 总结本次项目的所有问题,在以后的项目中逐个避免
  • 敏捷编码
    • 代码要清晰的表达意图
      • 保持代码的简单
        • 注重可读性,而不是只图自己方便和炫技
        • 代码清晰程度的优先级甚至应该排在执行效率之前
        • 无需对设计模式执着
          • 除非有不可辩驳的原因,否则不要使用模式、原则和高难度技术之类的东西
        • 为简单的设计而骄傲
          • 相比一个过分复杂,拙劣的解决方案,简单的方案通常更加难以获得
      • 代码阅读的次数要远远超过编写的次数
    • 用代码沟通
      • 好的编码规范可以让代码变得更加容易理解
        • 逻辑分离清晰
        • 变量名准确
        • 表达式简介
        • ...
      • 清楚的注释
        • 大量节省阅读代码的时间
        • 可以沟通代码以外的问题
        • 可能要说明以下信息
          • 为什么需要这个方法
          • 需要什么样的输入
          • 会更改哪些状态,有哪些返回值
          • 可能会发生什么异常
        • 在代码可以传递意图的地方,不要使用注释
    • 动态评估取舍
      • 没有最佳方案,只有最合适的方案
      • 影响因素,要做到平衡
        • 性能
        • 生产力
        • 优雅
        • 成本
        • 上市时间
    • 增量式编程
      • 在很短的构建/测试循环中编写代码
      • 确保增量过程稳定且易于维护
      • 时常保持对代码的精炼
    • 编写高内聚的代码
      • 单一职责原则
      • 内聚性指一个组件(包、模块等)成员的功能相关性,注意好隔离和分类
      • 具有高内聚的代码的变化,不会导致别的太多的变化,因此更加稳定
      • 低内聚的代码,一旦发生变化,容易引起"涟漪效应",导致无可预估的维护的成本
      • 让类的功能尽量集中,让组件尽量小
        • 避免创建很大的类和组件,也不要创造"大杂烩"类
      • 编码的时候考虑变化及扩展性以及他们造成的影响范围
  • 敏捷调试
    • 所有项目,都有bug,缺陷
    • 记录解决问题的日志
      • 问题简述
      • 解决方案详细描述
      • 引用文章及网址,跟多细节信息
      • 将解决方案的日志保存为可供搜索的方式 - Wiki
    • 警告就是错误!
      • 将其配置到持续集成中,并解决掉这些警告
    • 应该报告所有的异常!
      • 处理掉当前异常,或是向上传播所有的异常,不要将其忽略
    • 提供有用的错误信息
      • 提供出优雅的错误信息
      • 记录错误日志
        • 最起码应该是以文本文件的形式维护
        • 日志要清晰
        • 要易于理解
        • 做好日志类型划分的细节,方便日后阅读和索引
        • 禁止记录敏感信息
    • 对问题各个击破
      • 隔离出发生问题的模块,更容易修复。而不必过多关注整个负责的大型系统
    • 使用断言来提供与异常报告同样的信息,而不是等待系统出错来发现问题
  • 敏捷协作
    • 靠单打独斗在车库里面开发出一个完整产品的日子早已不在
    • 定期安排会面时间
      • 合理动态安排会议周期和时长
      • 每天/每N天(视情况而定)的例会
        • 昨天做了什么
        • 今天计划做什么
        • 有什么障碍
      • 公开问题,并积极寻求帮助
      • 动态分配任务和人手
      • 让大家知道项目的进展情况
      • 防止多人开发重复的解决方案
      • 促进代码和思路的共享,提升效率和质量
      • 看到别人的前进,从而形成正向的激励
      • 保证会议短小且不跑题
        • 例会解决不了的其他问题,可通过专题会议拉上相关人员解决
      • 一般10-15分钟较为合适
      • 注意报告的具体程度
        • 不可太粗略
        • 不可太详细
    • 架构师必须写代码
      • 架构师不应该只是画一些很漂亮的设计图,说一些"黑话",使用一大堆设计模式
        • 有太多"PPT"架构师了
        • 高度概括的。粗略的设计图无法很好地理解系统
      • 通过持续编码来实际了解系统,丰富和改进设计
      • 不存在所谓的最终决策,要根据实际和反馈做出改变
      • 要鼓励主力程序员参与设计,担任架构师的角色
        • 程序员在拒绝设计的同时,也就放弃了思考
        • 优秀的设计,从积极的程序员那里开始演化
          • 积极的编程可以带来更加深入的理解
          • 不要使用不愿意编程的架构师 - 不知道系统的真实情况,是无法展开设计的
      • 指导开发团队,提升水平,以解决更为复杂的问题
    • 实行代码集体所有制
      • 任何成员缺席都不会对项目造成影响
      • 保留技术专家对于其他业务代码的权限
      • 开发人员要向团队分享设计和代码,以便做人员热备
    • 成为指导者
      • 相互分享会得到更多的知识
      • 不必局限于自己的团队所用的技术
      • 开设博客进行技术分享
      • 通过指导而加深自己的结构化知识
      • 提升团队的整体实力,提升效率和质量
    • 允许大家自己想办法
      • 合理给出提示即可
      • 激励大家,给大家解决问题的机会
      • 不要用填鸭式的方式给予别人帮助
    • 合理利用版本控制系统
      • 控制每次提交的意义
      • 控制提交节奏
        • 在保证提交意义的同时,尽可能频繁的提交
      • 不要向代码库提交正在开发的代码
      • 要保证在提交代码之前,所有的单元测试都是通过的
        • 使用持续集成是保证源代码控制系统没有问题的良好方式
    • 坚持做CodeReview
      • 是任何已知形式测试的效果的两倍
      • 能移除80%的代码缺陷
      • 产出质量极高且稳定的代码
      • 每个人都需要进行代码复查
      • 基本的检查列表
        • 代码能否被读懂和理解
        • 是否有明显的错误
        • 是否对应用的其他部分产生不良的影响
        • 是否存在重复的代码或者重复的开发逻辑
        • 是否存在需要重构的部分
        • 所有的异常处理程序不允许为空
        • 所有的数据库调用都要在包的事务中进行
        • 需要积极评估代码的设计和清晰程度
      • 不要随意批评别人的代码,每个人的code风格毕竟不一致,要多鼓励,用开放的心态
      • 及时跟进代码复查的执行结果
    • 及时通报进展及问题
      • 出现不同于计划的情况,要及时向上,向大家通报
      • 根据不同的汇报对象,给出不同的汇报细节
  • 走向敏捷
    • 改变不好的习惯,并持续优化这些习惯
    • 向大家说明,敏捷开发是让大家的工作变得更加轻松
    • 入门习惯
      • 版本控制
      • 单元测试
      • 自动构建