做一个有职业操守的软件匠人

TDD系列

Posted by Bruce Wong on July 23, 2022

月初参加了申键、武可、邓志国三位老师的CSD(Certified Scrum Developer)认证课程后,深受三位老师的感染,对TDD、重构、CICD等一系列XP的工程实践有了切实感受的同时,进一步激发了我内心的一个信念——“做一个有职业操守的软件人。更希望自己能成为一个匠人”。课后这一个月的时间内,我也尝试在实际工作中落地了若干的实践。今天把课上印象深刻,以及最近的一些实践感受分享一下。希望对小伙伴有所帮助。

迭代的最小单元可以到一个函数

平时我们一提到迭代,可能第一个想到的就是1~4周的Timebox。是的,这是对业务交付价值的一个周期。不过其实我们在开发一个功能,甚至小到一个函数都可以看成是一个迭代。迭代就是要在一定的时间范围内可交付,并能获得反馈,从而推动下一次迭代的调整,如此反复直到获得最优的结果。这不正是写代码和调试代码的目的吗?结合TDD的工作方式:红灯->绿灯->重构,自然就是一个迭代的闭环。

红绿切换的目的是解决当前需求/问题,而重构是为了降低技术债,应对以后的变化。

TDD的经典三步法:红灯,绿灯,重构。而他们三步还有各自的意义:红、绿步骤是为了解决需求/问题而存在的。而重构是为了未来而存在的。但是只有前两步构建起了安全网,才有能力对未来进行调整。这也从一个角度阐释了:好的代码/架构是写出来的,而不是预先设计出来的。重构本身就是一种设计,更务实的设计,避免过度的设计。

当你不知道如何重构的时候,那就是步子还是不够小

最近正好和一个小伙伴一起结对重构了一个100多行的函数。当时我问他有什么思路的时候,他一脸懵地看着我。我就想起了老师在课上的建议:不知道如何重构的时候,那就是步子还不够小。我当时就说,咱们来找相同吧!对,不是找不同,是找相同。DRY(Don’t Repeat Youself)原则。结果发现这100多行的代码中有三个if语句块。于是使用提取函数法,将3个if语句提取成三个方法后。我的小伙伴又提出来后续的重构思路:改成switch->改成多态。似乎思路源源不断地被打开了。最后这个100多行的函数体只有25行代码。而这个重构结果是一开始无法想到的。

重构小Tip:

  1. 从一小步开始,不要求一次重构解决所有问题。
  2. 将代码改成一样的形式后,看是否能够合并同类项。
  3. 每一个改动后必须运行测试。保证全部通过。
  4. 重复2~3步,直到重构满意为止。

TDD三定律

  1. 没有测试之前不要写任何功能代码
  2. 只编写恰好能够体现一个失败情况的测试代码
  3. 只编写恰好能通过的测试代码。

#1 是最难转变的,一旦能够接受并坚持下来就会形成习惯。#2,#3主要是避免过度设计,减少浪费,只需要编写够用的功能code就好。更简单的code更容易修改,也更能避免bug。同时更容易适应将来的变化。

对遗留系统进行重构的步骤

  1. 编写测试保留当前功能的行为,而不对其进行修改。
  2. 新作功能导致当前功能测试无法通过后,再来修改测试逻辑。
  3. 只针对bug添加测试开始。不对运行良好的部分特意编写测试,直到有必要的时候。

对遗留系统增加测试是一个成本非常高的活动,但是如果不做,系统就无法跳出由于裸奔导致的不断产生bug的恶性循环。而如何能够迈出这一步并让飞轮正向旋转起来,结果会是怎么样呢?老师给的三个步骤确实能够对推动单元测试的工程实践落地。既避免了一味补写测试的高成本,又能因地制宜地逐步构建安全网。我成功的应用这个方式将单元测试落地到一个遗留系统团队,并在一个月的时间内增加了100多条测试。而研发小伙伴也开始主动应用TDD来开发和修改功能。

写在最后

有职业操守其实就是要对自己写的代码负责的态度,身边很多研发写code都是凭感觉,当你真的问他如何保证他写的代码是正确的时候,他们给不出具体的证明。只能说我就是知道没有问题。最直接的一个案例是最近发生的,当我用TDD对另一个团队的SDK做行为测试的时候。仍然可以发现他们的bug。而这些bug居然是他们一直认为的某一段代码在正常工作,但事实却是“并没有”。所以再一次让我感受到:用测试把自己写的代码踏踏实实的保护起来。同时通过重构让代码更加精益求精。把自己看成一个软件匠人,在创造艺术品,而不是普普通通的一个码农,只是苦逼搬砖。

践行敏捷实践,让工作变得更美好。欢迎关注我的公众号,交流落地经验。