第二个问题本身是一个母问题,每当我们对这个问题进行回答,都要回答一个衍生出来的子问题:我们要做点什么才能在需要Check的时候能够Check。 常用的套路有两个: Plan的时候估计一个时间,然后开始做,做的时候计时,做完就要Check这个时间是否达标,无论快了还是慢了(通常是比较明显的差距才能引起反思,比如20%以上的差距),Check都要反思并产生Action,纳入到未来的Plan中去。 估计的任务列表和实际做的任务列表是否是一样多的?往往是会多出来,这时就要反思,自己在哪里有不足导致了这个差别。 这些反思往往是发现自己的问题,比如自己不熟悉的知识点、方法,甚至业务知识,最后的Action也往往都是通过刻意练习来提升生产效率,比如反复练类似题目。有时也会借助一些工具来提升效率,比如抽取live template,使用快捷键,只是效率工具的使用往往也需要刻意练习。有时也可以通过复用技术(其实live template已经是复用技术了)来提升生产效率,然而可复用模式的识别与抽取本身也是需要练习的,否则在那里纠结浪费的时间更长。 有些同学会感觉到,记录了时间却不知道哪里有问题,这个时候可以跟TDD相结合,把时间划分为写测试的时间,写实现的时间和测试通过的时间。其实除去这几种时间,还有其他时间消耗,比如调研的时间。不管怎么划分,将时间消耗结构化掉,一部分一部分的追求最高效率是一种可行的办法。 举例 我们做一个简单的修改用户信息功能的API。那么我们在某一个Java技术栈上可能的任务列表是长这样的: 写UserController (10分钟) 写UserDAO (15分钟) 当你真正开始做的时候,会碰到两种主要的意外: 任务列表扩张 时间估计不准 下面就这个例子,就讲一讲当我们遇到这两种意外,该怎么反思和处理。 任务列表扩张 任务列表扩张,顾名思义,就是指我们所估计的任务数量会随着我们开始工作变的比预想的多,可能有两种主要原因: 技术原因 业务原因 技术原因: 比如在这个案例里面,第二项任务是“写UserDAO”,就是一个没想清楚的事情。我们还需要建数据库表,我们在一个有migration脚本支持的技术栈设计上工作,我们还需要写初始化脚本和回滚脚本。也许这是我的第一个表,所以我还得配置数据库,搞不好还要把ORM的基础代码都写完,所以这些导致了我可能任务估少了。 再比如,项目规范要求我们Controller不能直接调DAO,要在中间加一个Service,尽管我个人觉得这是一件很二的规范,然而规范就是规范,我对项目技术规范不熟悉,导致我的计划缺少了一些必要的任务。再比如,我们的项目采用了Jersey,根本没有Controller这么一个东西,那么不了解技术框架导致我的任务表从根本上就列错了。 这种情况属于我对技术了解不足,通过对任务列表扩张的原因进行Check,我会得出一些Action:去了解技术规范、项目的技术架构、现有的代码,以防止以后的任务画错。 业务原因: 也比如在这个例子里,在更新用户的API里不能更新密码,所以我们还需要一个专门修改密码的API。再比如,这是一个遗留系统,用户信息的修改会触发数据库里的一系列触发器,进而修改系统的其他数据,然而有些修改是有前提的,那么我就需要更多的任务去处理这些前提条件;或者当数据变化时,要求我去修改系统里的其他数据,那么我就需要更多的任务去完成这些工作。 这种情况属于我对整个系统的业务了解不足,通过对任务列表扩张原因的Check,我会得出一些Action:通读数据库表、通读代码、更全面的阅读需求,或者跟需求方更多的沟通,以了解业务。 时间估计不准 时间估计不准就简单很多,在这个例子里,可能的主要原因也有三个: 任务列表扩张了,但是我没意识到。比如UserDAO写起来没有我想的那么简单,所以多花了时间; 单纯的技术不熟练; 花了太多时间在纠结上; 对于隐藏的任务列表扩张,不准确的时间估计给了我们一个很好的线索去发现。一旦发现了,可以如前文所述去处理,也就不再赘述。
对于单纯的技术不熟练,正如前文所述,要设计刻意练习。比如我就曾设计过针对数据库的增删改查训练以提升自己的速度,使我即便使用TDD依然保持一个极高的速度。我们或许不曾意识到,基础能力的薄弱对于我们的高级能力的限制有多严重,这种体验也只有基础能力已经熟练的人去教基础能力不熟练的人一些高级技能的时候才会发现。这种视而不见的收益,使得大多数人都会轻视基本功的练习。哪怕已经获得收益的人,也容易鼓吹要更多的启发而忽略了基本功的价值。 (责任编辑:本港台直播) |