今天做项目,和同事讨论一个细节的实现问题。纠结了很久,才发现在不改变原来设计的前提下,根本不可能实现这个细节需求。于是乎,改设计,改实现,这直接导致一批 UT 的修改甚至荒废,几天前 TDD 的很多努力都白做了。
事后思考原因,当初设计的缺陷是因为我们做了一个假设,在今天实现这个细节的时候,才发现这个假设根本不成立,而根本原因是设计的时候对需求的了解不够全面。这是谁的错?是设计人员的错吗?
我觉得没有谁能保证在实现前能把需求想得面面俱到,或者说,能保证自己设想的需求能 100% 准确。实际上,需求绝对不是在项目开始就定好的,需求也是随着项目的进展而不断演化的。所以,上面的设计人员实际上没错,这是常态。敏捷开发提倡拥抱变化,对于变化,我们不应该认为是谁错了,而应该感到高兴,说明我们的分析和设计越来越趋近于真实情况。我们应该时刻重构,时刻保证系统的松耦合和扩展性,这样就能积极响应变化。不过针对于今天的情况,我觉得仍然有可以改进的地方,就是 Test Case 的撰写。
对于 Test Case ,究竟应该什么时候写? TDD 都提倡在做设计的时候写,在代码实现之前写。这样做的好处是
1. 强迫我们在写代码前就仔细思考,并做出设计。水平高的程序员在写代码前都会先想好大概的设计,但是对于水平低的程序员往往没这个习惯。所以这是一个比较好的习惯,或者是管理手段。
2. 用 Test Case 来代替设计文档。
3. 在实现前写好 case ,会让程序员目标明确且充满动力地实现目标
4. 写好实现后,重构代码, Test Case 能够保证重构的正确性
但是通过这么长时间的实践,我也体会到设计的时候写 TC 也有弊端。那就是当需求调整,设计调整的时候,会导致之前写的 TC 白写,又有大量的修改和重写工作,而且这种事情经常发生。这直接导致了劳动力的浪费,也推迟了项目原型的快速实现。但是如果在需求都稳定后再写,我们又丧失了 TDD 带给我们的好处。应该怎么做呢?
我觉得可以尝试这样来做,简单的说,就是先写主要的TC,之后再补齐。在设计的时候,只针对每一个接口写一个 TC ,这个 TC 应该是考虑系统正常运行时的情况,而不用考虑边界条件(我觉得 TC 的繁多,其实大部分都是同一个接口的不同 case ,考虑了很多边界条件)。这样就照顾到了好处 1 、好处 2 、好处 3 。在功能都稳定后,再补齐所有的 TC ,这个时候的 TC 就应该全,考虑尽量多的情况。
不过这里有个问题,就是何时才是功能稳定?我的一个想法是,当实际运行效果出来,然后让 stake holders 看过满意后,才叫功能稳定。但是对于这个想法,我仍然有两点质疑:
1. 如果都要等到 stake holders 看过之后,我们才能写 TC ,这个回馈周期就会比较长。那在这个期间内我们做的重构,就没有 TC 的保障。难道要等到功能稳定, TC 都补齐之后才进行重构?这个显然不太合理。
2. Stake holders 满意之后就能肯定功能稳定了吗? stake holders 看到的是操作流程界面上的效果,而往往一些阶段性成果都反映在后台数据里(比如今天的问题)。这个 stake holders 是很难察觉到的。
还需要在实践中继续摸索。