date: 2017-10-13 17:59:11 title: 「修改软件的艺术」 读书笔记

百度脑图 - 修改软件的艺术: http://naotu.baidu.com/file/3300eebf1014c10fd4d1a96ad6cf65ac?token=ff8bec2d896c0c61

图灵社区 - 修改软件的艺术: http://www.ituring.com.cn/book/1749

修改软件的艺术:构建易维护代码的9条最佳实践

将理解具象化

优秀软件是具象化的理解

敏捷 vs 瀑布

遗产 vs 遗留

奏效: 受雇的开发者必须意识到,仅仅言听计从是不够的,他们有义务让交付的软件持续产生价值

开发者有三种状态: 已完成 / 未开始 / 快完事了

评估未知

一个充满外行人的产业

导致低成功率的核心因素: (1) 代码变更 (2) bug修复 (3) 复杂度控制

顺流直下: 瀑布模型是从制造业和建筑行业借鉴而来

需求分析 设计 实现 集成 测试 安装 维护

为什么瀑布模型不管用

虚拟世界并不奏效

让我们建造出来的东西难以改变

开发和测试分离

出错误并非我的工作;创造错误才是

当“流程”变成“体力劳动”

流程无法支配创造力

敏捷: 希望软件能拥有即刻适应需求变化的能力

敏捷 Scrum 极限编程(Extreme Programming,XP)

敏捷流程核心: 通过持续不断地及早交付有价值的软件使客户满意

小即是好

并不是急于求成而是循序渐进

艺术与技能的平衡: 开发软件需要许多的技能和能力,也就是技艺,但是无论学到多少技能都没办法解决所有问题

追求技术卓越

遵循实践(消毒某一特殊器械的行为)和遵循原则(对所有器械进行消毒的理由)的差别

如果想降低软件持有者的开销,我们必须关注软件的构建过程

守、破、离

单一职责原则

开闭原则:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭

原则帮助我们把某件事情通用化,有助于梳理知识体系

实践必须: 在多数情况下产生价值; 容易学习且容易传授; 简单易行——简单到无需思考

原则指导实践

压力对于构建更好的产品毫无帮助

要有一个产品负责人

用户故事: 做什么、为什么做、给谁做

为验收测试设立明确标准 / 自动化验收标准

产品负责人的7个策略:

  • 成为特定领域专家
  • 在开发过程中探索
  • 帮助开发者理解为什么和为了谁
  • 描述你想要什么,而不是怎么做
  • 及时回答问题
  • 消除依赖
  • 支持重构

编写出更好用户故事的7个策略:

  • 它当作一个占位符
  • 关注“什么”
  • 把“谁”人格化
  • 知道为什么会有一个功能需求
  • 开始时简单,日后再加强
  • 心系边界情况
  • 使用验收标准

小批次构建

工作单元应该可以展现出可度量的结果

大小适中,而不仅仅是“小”而已

做出调整

人类无法度量

关键路径的重要性:“怀孕生子需要九个月,无论有多少妇女参与其中。”

范围、时间、资源(scope,time,resource,STR)

控制发布节奏

越小越好有四个基本原因:更容易理解;更容易预估;更容易实现;更容易测试

分而治之

之所以会很繁重,是因为两个原因:要么是复杂型的,要么是复合型的

探索未知事物的时候需要做两件事: 未知变为已知,未知进行封装

更短的反馈回路

提高构建速度

对反馈做出响应

建立待办列表

讨论待办列表的顺序而非优先级

把用户故事拆分为任务

跳出时间盒子思考

Scrum并不是一个“全有或全无”的提议

要么有风险,要么没有风险。当你有风险的时候,则充满未知。未知即是风险

范围控制

降低风险的唯一方式是把用户故事进行到底,这意味着我们需要对什么是“完成”有明确的定义

迭代”真正的目的是让团队消除以发布为单位的构建习惯

看板

度量软件开发的7个策略:

  • 度量产生价值的时间
  • 度量编码时间
  • 度量缺陷密度
  • 度量发现缺陷的时间
  • 度量功能的客户价值
  • 度量未交付功能的损失
  • 度量反馈回路的效率

分割用户故事的7个策略:

  • 把复合的故事拆分为组件
  • 复杂的故事分割为已知的和未知的
  • 对未知持续迭代直至完全理解
  • 根据验收标准分割故事
  • 最小化依赖
  • 保持目的单一
  • 保持故事可测试性

持续集成

处理痛苦的方式有两种:避免痛苦,或者学着承受

建立项目的心跳

实践持续部署

自动化构建

尽早集成,频繁集成

告诉你的开发者每天至少集成一次

测试驱动开发(Test Driven Development,TDD)

软件应该从第一天起就具备发布条件

构建敏捷设施的7个策略:

  • 用版本库管理一切
  • 一次点击全部构建
  • 持续集成
  • 为任务定义验收标准
  • 编写可测试的代码
  • 保证必要的测试覆盖率
  • 即时修复失败的构建

消除风险的7个策略:

  • 持续集成
  • 避免分支
  • 自动化测试上下功夫
  • 识别风险区域
  • 征服未知
  • 构建可以体现价值的最小部分
  • 频繁验证

协作

极限编程

开放性的思想

沟通与协作

结对编程

伙伴编程

穿刺,群战,围攻

在时间盒子中对未知进行调研

定期代码审查和回顾会议

加强学习和知识分享

诲人不倦且不耻下问

结对编程的7个策略:

  • 尝试一下,你会喜欢的
  • 驾驶员和领航员都要参与其中
  • 频繁交换角色
  • 充实工作一天
  • 尝试各种配置
  • 让团队决定细节
  • 跟踪进度

高效回顾会议的7个策略:

  • 找寻小的改进
  • 责怪流程而不是人
  • 5个为什么
  • 解决根源问题
  • 倾听每个人的声音
  • 给予支持
  • 度量进度

编写整洁的代码

CLEAN: Cohesive 内聚 / Loosely Coupled 松散耦合 / Encapsulated 封装 / Assertive 自主 / Nonredundant 没有冗余

高质量的代码是内聚的

单一的职责

高质量的代码是封装良好的

由外而内编程 vs 由内而外编程

只暴露解决问题所必需的

高质量的代码是自主的

高质量的代码是没有冗余的

DRY Don't Repeat Yourself

提高代码质量的7个策略:

  • 明确代码质量的定义
  • 对基本的实践达成一致
  • 放弃完美主义
  • 理解取舍
  • 用“什么”来隐藏“怎么”
  • 良好的命名
  • 保持代码的可测试性

编写可维护代码的7个策略:

  • 确立代码的集体所有权
  • 积极重构
  • 坚持结对编程
  • 频繁的代码审查
  • 学习其他开发者的风格
  • 不断学习软件开发
  • 读代码,写代码,练习编码

测试先行

测试是标准,测试定义行为。

  • 验收测试 = 客户测试
  • 单元测试 = 开发者测试
  • 其他测试 = 质量保证测试

质量保证(Quality Assurance,QA):

  • “组件测试”体现各个组成单元之间的配合情况。
  • “功能测试”体现所有组成单元在一起完成整个端到端的行为。
  • “场景测试”体现用户和系统的交互行为。
  • “性能测试”验证这些情形:“这个系统能承受很大的负载吗?我们进行过独立测试,但是如果百万级用户进行并发请求会怎么样?”
  • “安全测试”验证代码的脆弱程度。

以行为作为单元

TDD可以提供迅速的反馈

TDD可以为重构提供支持

编写可测试的代码

进行优质验收测试的7个策略:

  • 明确构建目标所产出的价值
  • 理解为谁而做以及他们为什么需要
  • 将验收测试自动化
  • 定义边界用例、异常、次要路径
  • 用实例来充实细节和展示不一致
  • 用验收标准来拆分行为
  • 保持每个测试的唯一性

进行优秀单元测试的7个策略:

  • 从调用者的角度出发
  • 用测试定义行为
  • 仅仅编写能体现区别的测试
  • 仅仅编写可以让测试通过的代码
  • 用测试来构建行为
  • 对代码进行重构
  • 对测试进行重构

用测试描述行为

测试就是标准

测试需要完整

bug是缺失的测试

工作流测试用所谓的模拟对象(mock)进行测试

使用测试作为标准的7个策略:

  • 将测试仪表化
  • 使用见名知意helper方法
  • 突出重点
  • 测试行为,而不是实现
  • 用模拟对象测试工作流
  • 避免过度描述
  • 利用真实的例子

修复bug的7个策略:

  • 一开始就避免写出bug
  • 尽早发现bug
  • 通过设计让bug更容易找到
  • 问对问题
  • 把bug当作失败的测试
  • 利用发现的缺陷修正流程
  • 从错误中学习

最后实现设计

可变性的阻碍

缺乏封装

滥用继承

僵化的实现

内联代码

依赖

可持续性开发

删除死代码

保持名称更新

集中决策

抽象

对类进行组织

编码与清理

软件被阅读的次数比编写次数多

意图导向编程

降低圈复杂度

演化式设计

进行演化式设计的7个策略:

  • 理解面向对象设计
  • 理解设计模式
  • 理解测试驱动开发
  • 理解重构
  • 关注代码质量
  • 要冷酷无情
  • 培养优秀的开发习惯

清理代码的7个策略:

  • 让代码自我表达
  • 为添加测试创造间隙
  • 让方法更内聚
  • 让类更内聚
  • 集中决策
  • 引入多态
  • 封装构建过程

重构遗留代码

重构能降低以下四个方面的成本:

  • 日后对代码的理解
  • 添加单元测试
  • 容纳新功能
  • 日后的重构

技术债和财务债一样:利息会把你拖垮

通过重构糟糕代码来培养良好习惯

推迟那些不可避免的

  • 图钉测试
  • 依赖注入
  • 系统扼杀
  • 抽象分支

  • 以支持修改为目的重构

  • 以开闭原则为目的重构
  • 以提高可修改性为目的重构

第二次做好

助你正确重构代码的7个策略:

  • 从已有系统中学习
  • 循序渐进
  • 遗留代码中添加测试
  • 始终进行重构
  • 有更好的理解后对一个实现进行重新设计
  • 继续其他工作前进行清理
  • 重构以避免误入歧途

决定何时进行重构的7个策略:

  • 关键代码维护不善的时候
  • 当唯一理解代码的人没空的时候
  • 有信息可以揭示更好的设计的时候
  • 修复bug的时候
  • 需要添加新功能的时候
  • 当需要为遗留代码写文档的时候
  • 当重构比重写容易的时候

在不需要的事情上花钱

我们是软件开发者,利用现有工具尽我们所能开发最好的软件