读程序员的README笔记03_变更代码

发布时间 2023-12-07 06:24:44作者: 躺柒

1. 行为准则

2. 变更代码

2.1. 变更代码和在新代码库中写代码完全不一样,你必须在不破坏现有行为的情况下进行这些修改

2.1.1. 必须理解其他开发者的想法,坚持原有的代码风格和设计模式

2.1.2. 必须在工作中温和地改进代码库

2.2. 善于利用现有代码

2.2.1. 安全地在现有代码库中修改代码的步骤

2.2.1.1. 定义变更点

2.2.1.2. 寻找测试点

2.2.1.3. 打破依赖关系

2.2.1.4. 编写测试

2.2.1.5. 进行修改和重构

2.2.2. 找到你需要修改的代码,并想出如何测试它

2.2.2.1. 如果需要的话,为了让测试成为可能,可以对代码进行重构

2.2.2.2. 针对现有的软件行为也要添加测试用例

2.2.2.3. 一旦竖起栅栏,你的修改点周围的区域就得到了很好的保护,然后就可以在里面修改代码了

2.2.3. 一旦你定位了代码,就要找到它的测试点

2.2.3.1. 测试点是你想要修改的代码的入口,也就是测试用例需要调用和注入的区域

2.2.3.2. 测试点揭示了代码在被你变更之前的行为,你需要使用这些测试点来测试你自己的变更

2.2.4. 依赖关系不是指类库或服务的依赖关系,而是指测试你的代码时所需要的对象或方法

2.2.4.1. 打破依赖关系意味着改变代码结构,使其更容易测试

2.2.4.1.1. 将一个大的、复杂的方法拆分成多个小的方法,这样就可以分别去测试独立的特性片段
2.2.4.1.2. 引入一个接口(或其他中介),为测试提供一个复杂对象的简单实现——不完整,但要满足测试需要
2.2.4.1.3. 注入明确的控制点,允许你模拟难以控制的执行的切片,如时间的推移

2.2.4.2. 你只有改变代码,才能将你的测试挂起来,并提供合成的输入

2.2.4.2.1. 这些代码变更一定不要改变原有的代码行为

2.2.5. 不要为了方便测试去改变访问声明

2.2.5.1. 将私有(private)方法和变量公开以让测试用例访问代码,但同时也破坏了封装,这是一种糟糕的方式

2.2.5.2. 破坏封装会增加你在项目的生命周期内必须保证的原有行为一致性的覆盖面积

2.2.6. 当你重构和打破依赖关系时,应该添加新的测试来验证旧的行为

2.2.6.1. 在迭代过程中要频繁地运行测试套件,包括新的和旧的测试用例

2.2.6.2. 考虑使用自动测试工具来生成捕获现有行为的测试用例

2.3. 过手的代码要比之前更干净

2.3.1. 帮助你的代码随着时间的推移而变得更好

2.3.2. 在不影响整个项目持续运转的情况下要持续地重构工程,这样重构的成本就会平摊在多次的版本更迭中

2.3.3. 当你修复错误或增加新的特性时,只清理有关联性的代码

2.3.4. 不要不顾一切地去找“脏”代码,要“随缘”一些

2.3.5. 尽量将清理代码的提交和改变行为的提交各自分开

2.3.5.1. 分开提交可以让你在不会丢失针对代码清理的提交的基础上,更容易地去恢复代码变更

2.3.5.2. 较小的提交也更容易针对变更的部分进行评审

2.3.6. 代码异味(code smell)

2.3.6.1. 要随时定位有异味的代码

2.4. 做渐变式的修改

2.4.1. 使用较小规模的提交

2.5. 对重构要务实

2.5.1. 重构是指在不改变软件行为的情况下改进内部代码结构

2.5.1.1. 经常发生在添加新特性的时候,因为它使新特性可以更容易地被添加

2.5.1.2. 在修复bug的过程中,则经常删除代码

2.5.1.3. 使用重构以打破依赖关系是工作中风险最大的部分

2.5.1.3.1. 采取小步前进的方式,在这个阶段不要引入任何新特性

2.5.2. 重构并不总是明智的选择

2.5.2.1. 因为团队的工作有截止日期和排他的优先事项,重构需要花费时间

2.5.2.2. 你的团队可能会决定忽略重构,而去开发新特性

2.5.3. 重构的成本也可能超过其价值

2.5.3.1. 正在被替换的旧的、废弃的代码不需要被重构

2.5.3.2. 低风险或很少被触及的代码也不需要

2.6. 善用IDE

2.6.1. IDE在重构时特别有帮助

2.6.2. IDE使重构变得如此容易

2.7. 代码变更都应该被提交到版本控制系统(VCS)

2.7.1. 在开发过程中,尽早并频繁提交你的修改

2.7.2. 频繁地提交可以显示出代码随着时间的推移而发生的变化,方便你撤销修改,并将之作为一份远程备份

3. 避“坑”指南

3.1. 现有的代码都多多少少地背负历史包袱

3.2. 继承原有的代码标准可以保持代码的可读性,但前后的不统一将使开发人员难以理解代码

3.3. 如果你想重构代码或重定义标准,你的改进就必须是一个数量级层面的改进

3.3.1. 小的收益是远远不够的,因为成本太高了

3.3.2. 大多数工程师低估了惯例的价值,而高估了忽视惯例的收益

3.4. 保守一些的技术选型

3.4.1. 成功的公司留用旧的代码,比如旧的类库和旧的模式的原因是成功需要时间,而在技术上大动干戈会让人分心

3.4.2. 新技术的问题是它不太成熟

3.4.2.1. 所有的技术都会发生故障,但旧的东西以可预测的方式发生故障,新东西往往会以令人惊讶的方式发生故障

3.4.2.2. 缺乏成熟度意味着更小的社区、更低的稳定性、更少的文档,以及更差的兼容性

3.4.2.3. 新技术的收益必须超过其成本

3.4.3. 一种语言可能具有很大的优势:一种特定的编程范式,更易于实验,或消除某些类型的代码错误

3.4.3.1. 一种语言的优势必须与它的劣势保持平衡

3.4.3.2. 价值数十亿美元的公司都是建立在成熟但有些无聊的编程语言之上的,伟大的软件基本都是用C、Java、PHP、Ruby和.NET编写的

3.4.3.3. 除非某种语言正在消亡,否则它的年龄和缺乏吸引力都很难成为反对使用它的理由

3.5. 不要特立独行

3.5.1. 要因为你不喜欢你公司(或行业)的标准就忽视它们,编写非标准的代码意味着它将无法适应公司的环境

3.5.2. 改变被广泛采用的东西肯定进展很缓慢,但并不意味着不值得这样做

3.6. 不要只分叉而不向上游提交修改

3.6.1. 分叉(fork)是对一个代码库进行完整的、独立的复制,分叉之后的代码库有自己的主干、分支和标签

3.6.2. 分叉操作可以让那些对主代码库没有写入权限的人仍然可以对项目做出贡献,这是一种正常而健康的做法

3.6.3. 不太健康的做法是只分叉代码库而不打算回馈修改

3.6.3.1. 这种情况发生在对项目的方向有分歧的时候,原来的项目被废弃了,或者是很难把修改的代码合并到主代码库里

3.6.4. 分叉公司内部的代码库并进行维护特别有害

3.6.5. 没有及时贡献到上游代码库的小调整会随着时间的推移而变得复杂

3.7. 克制重构的冲动

3.7.1. 重构工作常常升级为全方位的重写

3.7.2. 不要以为重构工作会很轻松,这将是一个艰难的过程