00 开篇词 程序员解决的问题,大多不是程序问题

你好!我是郑晔,一个程序员。

很多人都说,程序员很辛苦,与这个角色联系在一起的词儿,通常是忙碌、加班、熬夜等。

作为程序员,我们将其看作一个值得全情投入的职业,希望能够把精力放在设计算法、改进设计、优化系统这些具有创造性与成就感的本职工作上。

但现实情况却是,许多人因为一些“意外”,陷入了无休止的忙碌,比如:

你辛辛苦苦写的代码还没上线,产品经理就告诉你需求变了; 你拼命加班只因错估了工作量,自己造的“孽”,含着泪也要搞定; 你累死累活做出来的东西和要求不符,只能从头再来; 你大面积地修改代码只是因为设计糟糕,无法适应新的需求变化; …… 诸如此类,不胜枚举。我们很辛苦,但耗费我们大量时间和精力去应付的工作,并不是技术工作,反而是这些看似很“不值当”的事儿。

为什么会这样?

软件行业里有一本名著叫《人月神话》,其中提到两个非常重要的概念:本质复杂度(Essential Complexity)和偶然复杂度(Accident Complexity)

简单来说,本质复杂度就是解决一个问题时,无论怎么做都必须要做的事,而偶然复杂度是因为选用的做事方法不当,而导致要多做的事。

比如你要做一个网站,网站的内容是你无论如何都要写的,这就是“本质复杂度”。而如果今天你还在用汇编写一个网站,效率是不可能高起来的,因为你选错了工具。这类选错方法或工具而引发的问题就是“偶然复杂度”。

作为一个在软件行业奋斗了近二十年的程序员,我深刻意识到一个遗憾的事实:大部分程序员忙碌解决的问题,都不是程序问题,而是由偶然复杂度导致的问题。

换句话说,只要选择了正确的做事方法,减少偶然复杂度带来的工作量,软件开发是可以有条不紊进行的。

如何减少偶然复杂度引发的问题,让软件开发工作有序、高效地进行,这正是我希望通过这个专栏帮你解决的问题。

许多人工作做事主要依靠直觉,在这个科学越发昌明的时代,我们清楚地看到,人类的直觉常常是错的,就像古人凭直觉认为大地是平的一样。

软件开发也不例外,如果你不曾在做软件这件事上有过学习和思考,形成一套高效的工作方法,只是凭直觉行事,在真实世界中往往会举步维艰。

幸运的是,总会有不同的人在不同的方向上探索不同的做法,一旦通过真实世界的验证,就会沉淀出可供行业直接应用的最佳实践(Best Practice)。

在软件行业中,这样能够提升工作效率的最佳实践已经有很多,但是,学习掌握这些最佳实践是有难度的,其根源就在于,很难找到这些实践彼此间的内在联系。

直觉大多是错误的,最佳实践又多而琐碎,所以在这个专栏中,我会尝试给你提供一个思考框架,帮你在遇到问题时梳理自己真正要做的事情。围绕着这个框架,我还会给你一些原则。

这些原则,是我从软件行业的诸多软件开发最佳实践中总结出来的,也是我如今在工作中所坚持的。

这些原则就是一条主线,将各种最佳实践贯穿起来。

这些原则不多,总结起来就四个:

  • 以终为始;

  • 任务分解;

  • 沟通反馈;

  • 自动化。

也许看到这四个原则的名字,你会不以为然,这些说法你在很多地方都看到过,但我想与你分享的内容可能与你想的并不完全一致。

比如:

你以为的“终”可能不是终,因为你只是站在自己的角度;

你以为自己做了任务分解,在我看来,可能还不够,因为我希望你能够做到微操作;

你以为的沟通反馈就是说话聊天,我想告诉你很多技术实践的存在也是为了沟通反馈;

你以为自动化就是写代码,我会告诉你,有时候不写代码而解决问题,可能才是一个好方案。

在我看来,想要将精力聚焦在本质复杂度上,提高工作效率,摆脱直觉的束缚,只要掌握上面的四个原则就可以了。

或许你此时会问,这些原则很难吧?其实并不难,在探讨这个专栏的内容时,我的编辑作为软件开发的局外人,经常发出感叹:“这事真的就这么简单吗?这不就是正常做事应该有的逻辑吗?”

是的,就是这样简单,但大多数人没有这样做,因为这些原则在实际工作中很可能是反直觉的。只要打破思维误区,你的整个人都会变得不一样。

下面是整个专栏的目录,我希望能帮助你回答,或者厘清一些开发过程中,曾经遇到,又未曾深入的问题。

10x

当我们详谈这些原则时,我会给你讲述一些最佳实践,让你看到这些原则是如何应用于不同的实践中的。希望我对这些实践的理解成为你的知识地图,让你拥有继续探索的方向。

我做这个专栏的原则是“授人以鱼,不如授人以渔”。我希望你很好地理解这些原则,掌握高效工作的方法。至于最佳实践,你可以自行决定,是直接采纳还是曲线救国更为合适。

介绍一下我自己,我是郑晔,目前在火币网担任首席架构师,写过代码、带过团队、做过咨询,创过业,还维护着一个拿过 Oracle Duke 选择奖的开源项目 Moco,至今仍然在编程一线写着代码。

很长时间里,我一直对如何做好软件充满了好奇,了解过各种技术以及开发方法。做咨询的经历让我有机会见识到不同公司面临的问题;带团队的时候,我也看到很多小兄弟因为不会工作,虽然很努力却收效甚微;而我自己菜鸟时期的笨拙依然是历历在目。

在我看来,所有做软件的人能力都很强,这些问题都只是因为不会工作造成的,但更可怕的是,许多人深陷泥潭而不自知

在这些年的工作里,我一遍又一遍给别人讲如何工作,逐渐总结出一套自己的工作原则,如今呈现在你面前的就是我这些年思考的总结。

我不指望所有人都能从这个专栏受益,我只想把这个专栏写给那些愿意成长的人。我只是来做一次信息分享,分享一些思考,分享一些做法,希望可以将你从常见的思维误区中带出来。

也许在这个专栏的最后,你发现自己并不认同我的原则,却能够用自己的原则来与我探讨,那么,恭喜你,因为那是最美妙的事情!

01 10x程序员是如何思考的?

你好,我是郑晔。

在开篇词中我们提到,程序员在工作中遇到的很多问题,大多不是程序问题,辛苦而低效的工作,多数是由偶然复杂度导致的。

那这个由于偶然复杂度造成的差距会有多大呢?

1975年,弗雷德里克·布鲁克斯(Frederick Brooks)出版了软件行业的名著《人月神话》,他给出了一个统计结果,优秀程序员的开发效率是普通程序员的10倍。

40多年过去了,这个数字得到了行业的普遍认同。

成为10x程序员是很多程序员的追求。但工作产出并不只是由写代码的效率决定的,一些不恰当工作方法很大程度上影响着你的产出。

在接下来的这段时间里,我希望通过这个专栏和你一起探讨,作为一个程序员,该如何更高效地工作,怎样才能把时间和精力尽可能地放在处理本质复杂度的事情上,减少在偶然复杂度上的消耗。

作为整个课程第一讲,我就从我常用的一个思考框架开始。

一个思考框架

我曾经组织过针对应届毕业生的培训,第一堂课我往往亲自操刀,其中有一个头脑风暴的环节“畅想未来”,我会让大家思考三个问题:

  • 我现在是个什么水平?

  • 我想达到一个什么水平?

  • 我将怎样到达那个目标?

大家会围绕着这三个问题,从各种角度展开讨论。

这是一个有趣的练习,你会发现大家“最擅长”的是回答第一个问题:我现在处于什么水平?和有经验的人相比,他们大多自认为比较“菜”。但对于后两个问题的讨论,却可以切实看出人和人之间处理问题的能力差异。

有人通过之前的资料搜集,已经对自己的未来有了一个打算。比如想成为一个研发大牛,或者想做一个开源软件等,也就是说,对于第二个问题,他有明确的答案。

而有的人则是一脸茫然,他很可能根本没有考虑过这个问题。而从题目本身来看,目标相对清晰的同学,才会进入到第三个问题,而茫然的同学,则完全无从下手。

那么我为什么会问这几个问题呢?我是想让大家跳出现有的思考模式,摆脱仅凭直觉“闷头做事”的习惯方式,把低着的头抬起来,看一眼未来,给自己找一个方向。

否则,如果你对未来没有定位,是茫然的,尽管你也知道要努力,但劲儿该往哪里使呢?如果使劲的方向不对,那么你越使劲儿,可能会在错误的路上跑得越远。南辕北辙的道理大家都懂,但具体到自己的工作和发展上,真正能体会并实践的却是少数。

其实,这三个问题来自一个思考框架。在给其他公司团队做咨询时,我也经常会运用到它,原来的问题是:

  • Where are we?(我们现在在哪?)

  • Where are we going?(我们要到哪儿去?)

  • How can we get there?(我们如何到达那里?)

这三个问题实际上是帮我们确定:

  • 现状;

  • 目标;

  • 实现路径。

如果一个人能够清晰地回答出这三个问题,通常意味着他对要做的事有着清晰的认识。这个框架虽然看似简单,但却非常有效,它已经成为我工具箱里一件非常称手的思考工具。

在我的职业生涯里,与很多人讨论不同的事时,我都会用到这个思考框架的不同变体,而在这个专栏里,我也会用它来帮助回答“怎样高效工作、怎样做好软件”这件事。

四个思考原则

在实际的工作中,这个思考框架会帮助我更好地了解自己的工作。

比如,当一个产品经理给我交代一个要开发的功能特性时,我通常会问他这样一些问题:

  • 为什么要做这个特性,它会给用户带来怎样的价值?

  • 什么样的用户会用到这个特性,他们在什么场景下使用,他们又会怎样使用它?

  • 达成这个目的是否有其它手段?是不是一定要开发一个系统?

  • 这个特性上线之后,怎么衡量它的有效性?

如果产品经理能够回答好这些问题,说明他基本上已经把这个工作想得比较清楚了,这个时候,我才会放心地去了解后续的细节。

我们用思考框架对照一下,为什么我会问这些问题。一般来说,一个新特性要开发时,现状我是知道的。

所以,我更关心目标,这里“为什么要做这个特性?”就是在问目标,“给用户带来怎样的价值”是在确定这个目标的有效性。

接下来,我会关注实现路径,用户会怎么用,是否有其他的替代手段,我需要了解产品经理的设计是经过思考的,还是“拍着脑袋”给出的。衡量有效性,则是要保证我的工作不会被浪费。

通过这个例子,我给你展示了怎么用这个思考框架提出问题。但我估计你更想了解的是,我怎么会想到问这些问题。

给出思考框架是为了让你明白为什么要提出问题,而具体问题要怎么问,就可以遵循下面这四项原则:

  • 以终为始;

  • 任务分解;

  • 沟通反馈;

  • 自动化。

这是我从思考框架延伸出来的。

在这个专栏里,我会围绕这四项原则和你详细讨论。

解释一下,以终为始就是在工作的一开始就确定好自己的目标。我们需要看到的是真正的目标,而不是把别人交代给我们的工作当作目标。你可以看出这个原则是在帮助我们回答思考框架中,Where are we going?(我们要到哪儿去?)这个问题。

任务分解是将大目标拆分成一个一个可行的执行任务,工作分解得越细致,我们便越能更好地掌控工作,它是帮助我们回答思维框架中,How can we get there?(我们如何到达那里?)的问题。

如果说前两个原则是要在动手之前做的分析,那后面两个原则就是在通往目标的道路上,为我们保驾护航,因为在实际工作中,我们少不了与人和机器打交道。

沟通反馈是为了疏通与其他人交互的渠道。一方面,我们保证信息能够传达出去,减少因为理解偏差造成的工作疏漏;另一方面,也要保证我们能够准确接收外部信息,以免因为自我感觉良好,阻碍了进步。

自动化就是将繁琐的工作通过自动化的方式交给机器执行,这是我们程序员本职工作的一部分,我们擅长的是为其他人打造自动化的服务,但自己的工作却应用得不够,这也是我们工作中最值得优化的部分。

这四个原则互相配合,形成了一个对事情的衡量标准。

总体上可以保证我的工作是有效的,在明确目标和完成目标的过程中,都可以尽量减少偶然复杂度。

怎么把这四个原则用在工作中呢?

我们回过头来看一下前面的场景,产品经理把要做的功能特性摆在我面前。站在以终为始的角度,我需要了解真正的目标是什么,所以,我会关心为什么要做这个特性。

为了保证目标是有效的,我会关心它给用户带来的价值。

有了任务分解的视角,我需要将一个大的目标进行拆解,如果我要达成这个目标,整体解决方案是远远不够的,我需要把任务分解成一个一个小的部分。所以,我会关心一个一个具体的使用场景。

一方面,我会了解到更多的细节,另一方面,当时间紧迫的时候,我会和产品经理来谈谈究竟优先实现哪个场景。

为什么要学会沟通反馈?

因为我需要明确,自己是否真正理解了产品经理提交的需求。所以,我要不断地问问题,确保自己的理解和产品经理交代的内容一致。

另外,我也需要保证我的产品做出来确实能够达到目标。所以,我会关心它上线后的衡量手段。因为我知道,这个行业里有太多代码上线后,从来没有运行过。

自动化的角度很有意思,我们做的方案通常是一个自动化方案,但我们需要了解这个方案没有自动化之前是怎么做的。

如果不自动化,用户会怎么用。所以,我会关心是不是还有其它替换方案,比如,买一个现成的服务。因为很多需求的提出,只是因为我们有了一个开发团队而已。

好,现在你已经对这四个原则在工作中的应用有了一个直观的认识。但你也会发现,我问的这些问题似乎已经“超纲”了,超过了一个普通程序员应该关注的范围。

但这就是真实世界,它不像考试一样,有一个标准答案

我们不是一个人孤独地在工作,而是与其他人在协作,想要做到高效工作,我们就要“抬起头”来,跳出写代码这件事本身。

所以,我在开篇词里说,程序员解决的问题,大多不是程序问题。

可能你对这些原则的了解还没过瘾,没关系,这篇文章只是让大家清晰地了解思考框架和原则的背后逻辑。接下来,我会结合行业里的最佳实践,给你进一步讲解这些原则和具体应用。

总结时刻

大多数人工作低效是由于工作中偶然复杂度太多造成的,只要能够更多地将注意力放到本质复杂度上,减少偶然复杂度造成的消耗,我们“真实”的工作效率自然会得到大幅度提升。

而想要减少偶然复杂度的消耗,就要了解一些高效的工作方式和行业的最佳实践,而这一切是可以用统一的框架进行思考的。

运用这个思考框架,我们需要问自己一些问题:

Where are we?(我们现在在哪?) Where are we going?(我们要到哪儿去?) How can we get there?(我们如何到达那里?)

为了把这个框架应用在我们程序员的工作中,我给了你四个思考原则:

以终为始,确定好真实目标; 任务分解,找到实施路径; 沟通反馈,解决与人打交道出现的问题; 自动化,解决与机器打交道出现的问题。

如果今天的内容你只能记住一件事,那请记住:面对问题时,用思考框架问问自己,现状、目标和路径。

最后,我想请你思考一下,如果把这个思考框架运用在你的职业发展规划上,你会如何回答这三个问题呢?

02 以终为始:如何让你的努力不白费?

今天内容的开始,我希望你可以先来思考一个问题:

如果让你设计一个登录功能,你会怎么做?

我曾在公司内部做过这样一个练习,我扮演客户,让大家帮我设计一个登录功能。同事们一听就高兴了,登录不就是用户名加密码嘛,我熟啊,我还可以设计出验证码、找回密码、第三方登录等等功能。

更有个别动作快的同事,甚至已经开始设计数据库表,考虑用Redis做缓存了。

整个过程下来,大家彼此讨论得热火朝天,唯一没人理会的就是我这个“客户”。

讨论结束,扮演客户的我告诉大家,作为一个“土豪”,我打算做一个打车软件,用户可以通过手机号接收验证码的方式进行登录。你可以想见,同事们一副“被套路了”的表情。是的,他们设计那套用户名密码登录完全是文不对题。

虽然这是一个简单的练习,但反映的却是我们日常面对的真实工作场景:许多人都是刚刚听到别人要求做的一个功能,就开始脑补接下来的一切。导致的结果,就是付出的努力毫无意义。

那么问题出在哪呢?因为我们欠缺了“以终为始”的思维习惯。

一种反直觉的思维方式

以终为始,就是在做事之前,先想想结果是什么样子的。

说起来很简单,但做到并不容易。因为我们习以为常的思维模式是线性而顺序的,第一步做完,做第二步;第二步做完,做第三步。

这也情有可原。我们人类都是从远古时代演化而来,在那个食不果腹的时代里,倒着思考的用途并不大,人们甚至不确定自己能否见到明天的太阳。几十万年的进化留给我们很多短视的行为和思考习惯,因为这样的做法最为节省能量,把目光放长远是需要额外消耗能量的。

“以终为始”是一种反直觉的思维方式,是大多数人不具备的。所以,日常生活中,我们看到很多有趣的现象。

比如,大学毕业时,有很多人想考研,如果你问他为什么要考研,得到的理由通常是为了找个好工作。但考研真的能帮他找个好工作吗?不一定,因为找工作和考研根本就不是同一棵技能树。

如果真的是想找个好工作,那你就应该了解工作的要求是什么,怎样才能掌握工作要求的技能。

从后面这个角度出发,你会发现考研只是通往工作诸多道路中的一条,其他的路径也是可以到达的。

比如,你应该找个实习的地方锻炼一下职业技能。这就是“以终为始”思考问题的方式。

回到前面“设计登录功能”的例子,对比“以终为始”的思维,你也许会替我的同事抱不平,他们或许也有“以终为始”的思路,只不过,他们的“终”和我这个客户的“终”不一样罢了。

这就要说到做软件,本质上是在构建一个“集体想象”

想象的共同体

如果你读过尤瓦尔·赫拉利的《人类简史》或《未来简史》,有一个说法你一定不陌生:想象的共同体。

作者认为,人类历史发展的一个重要因素是“集体想象”,无论是国家、宗教,还是法律、习俗,都是人们达成的“集体想象”。人类就是认同了这些“集体想象”的一个共同体。

我们这些做软件的人其实就是一个想象的共同体,这个“集体想象”就是我们要做的软件,任何想象都需要一个载体将其展现出来,我们编写软件的过程就是将这个“集体想象”落实的过程。

既然是“集体想象”,那么在载体将想象呈现出来之前,我们的想象很难统一起来,都或多或少存在差异。

所以,任何事物都要经过两次创造:

一次是在头脑中的创造,也就是智力上的或者第一次创造(Mental/First Creation),然后才是付诸实践,也就是实际的构建或第二次创造(Physical/Second Creation)。

我们在工作中遇到的很多问题,其实就是在于第一次创造没有做好,就进入到第二次创造。

所以,我们在工作中会遇到很多“惊喜”,准确地说,是惊吓。

相比于第一次创造,第二次创造是一件成本很高的事。我们知道,软件开发最费时费力,一旦投入大量精力做出来,却发现与理解偏差甚大,所有人都会欲哭无泪。

所以,在动手做事之前,我们要在第一次创造上多下一些功夫,将相关各方的“集体想象”统一起来。以建筑为例,就是先在图纸上构思各种细节。

对应到做软件,我们也可以做很多事,比如:

  • 要给用户看产品的样子,可以用原型工具把它做出来,而不是非得把完整功能开发出来;

  • 要呈现服务接口的样子,可以用模拟服务器搭出一个服务,而不用等后端全部开发完毕;

  • 要让程序员知道要开发产品的细节,可以在任务上描述出软件各种场景给出的各种行为。

再回到前面“设计一个登录功能”的例子上,我的同事们在构建的其实是他们自己的想象,而不是我们共同的想象。

这其中最大的一个区别就在于,没有人会为他们自己的想象买单的。

所以说,他们看到的“终”不是真正的终,只是一个自我的“终”,至于看到什么样的“终”,这取决于每个人的见识。

对做软件的人来说,我们应该把“终”定位成做一个对用户有价值的软件,能够为别人带来价值,自己的价值才能体现出来

至此,你对“以终为始”已经有了一个初步的认识,有了这种思维方式,我们可以在工作中怎样运用它呢?

规划和发现

软件行业有很多英雄传说,一个人或者一个团队连续奋战一段时间,写好了一个软件,在上线前夜发现了一个问题,然后冒着“不成功便成仁”的风险,通宵达旦解决了问题,一战成名。

这种故事听起来让人热血沸腾,但仔细想想,为什么总在最后一刻发现问题?除了时间压力确实大的情况以外,大多数情况,他们还是一开始没有想好就动手了。

在团队内部,我一直坚持“以终为始”,让大家在执行任务之前,先倒着想想再动手规划,这样规划出来的工作更能瞄准真正的目标。

举一个之前做产品的例子,当年在创业的时候,我们打算做一个物联网开发平台,但具体应该做成什么样子呢?

有了“以终为始”的思维,我们考虑的是别人会怎么用我们的平台。

我们设计的方式是,用户到我们的网站,阅读相关文档,然后参考文档一步一步照着做。

这其中的一个关键点是:文档,特别是《起步走》的文档,这是用户接触我们这个平台的第一步,决定了他对我们产品的第一印象

所以,我们决定从写《起步走》这个文档开始,这个文档描绘了用户怎样一步一步使用我们的开发平台,完成第一个“Hello World”级别的应用。

请注意,这个时候,我们一行代码都没有写。

写好了这个《起步走》文档,团队的所有人对于我们的平台要做成什么样子,已经有了一个比较初步的认识。

更重要的是,我们可以拿着这个文档,去和外部的人讨论这个尚未出世的平台。

人类是一个擅长脑补的群体,一旦有人看到了这个文档,他就已经可以构想出这个平台已经存在的样子,进而给出各种各样的反馈:“我认为这个地方可以这样做”“我觉得那个地方可以改改”。

所有这些反馈都是真实的,因为他们已经“看到了”一个真实的东西。正是这些真实的反馈,让我们逐渐地锁定了目标。之后,我们才开始动手写代码

“以终为始”的方式,不仅仅可以帮我们规划工作,还可以帮我们发现工作中的问题。

有一次,我的团队在开发一个大功能,要将现有的系统改造成支持多租户的系统。也就是说,别的商家可以到我们的平台上发起申请,拥有和我们现有平台一样的能力。

功能来了,各个团队将任务分解,然后就各忙各的去了。但我有着习惯性的不安,总担心丢点什么,于是催着项目经理梳理一下上线流程。

是的,上线流程,虽然我们的代码还没开发完,但是本着“以终为始”的态度,我们就假设各个部分已经开发好了,来想一想上线应该怎么做。

果不其然,一梳理上线流程,我们便发现了问题:怎么识别不同的租户呢?有人给出的方案是设置一个HTTP头。但谁来设置这个HTTP头呢?

没人仔细想过。于是,一个潜在的问题就这样被发现了,至少不用在未来为它加班了。

至于解决方案,作为程序员,我们有的是办法。

事实上,在今天的软件开发实践中,已经有很多采用了“以终为始”原则的实践。

比如测试驱动开发。测试是什么?

就是你这段代码的“终”,只有通过测试了,我们才有资格说代码完成了。当然,测试驱动开发想做好,并不是先写测试这么简单的。

比如持续集成,我们是要交付一个可运行的软件,倒着来想,最好的做法就是让软件一直处于可运行的状态,那就是持续地做集成。

概括地说,践行“以终为始”就是在做事之前,先考虑结果,根据结果来确定要做的事情

这是“以终为始”这个内容版块的开篇,后面我会给你介绍这个原则在不同场景下的应用,也会引入一些现在行业内的最佳实践进行解析。相信会对你的实际工作有帮助。

总结时刻

有一段时间,网上流传着一个帖子,亚马逊 CTO 介绍亚马逊是如何开发一项产品的,简单来说,他们采用向后工作的方法,开发一项产品的顺序为:

  • 写新闻稿;

  • 写FAQ(常见问题解答);

  • 写用户文档;

  • 写代码。

今天我带你了解了“以终为始”的做事思路,回过头再来看这个帖子,相信你不难理解为什么亚马逊要这么做事情了。

人们习惯采用顺序思考的思维方式,几十万年的进化将这种思考模式刻在了我们的基因里。

要成为更好的自己,我们要克服自身的不足,而这个做法很简单,那就是“以终为始”,做事倒着想,先考虑结果。

人类是一个想象的共同体,做软件的团队更是如此,而我们写出来的软件是我们将“集体想象”落地的载体。

任何事物都要经过两次创造:一次是在头脑中的创造,也就是智力上的或者第一次创造(Mental/First Creation),然后才是付诸实践,也就是实际的或第二次创造(Physical/Second Creation)。

我们应该在第一次创造上多下功夫,统一集体想象,让目标更明确。

“以终为始”的思维可以帮助我们更好地规划我们手头任务,也可以帮助我们发现过程中的问题。

如果今天的内容你只能记住一件事,那请记住:遇到事情,倒着想

最后,我想请你思考一下,在实际的工作或生活中,你有运用“以终为始”的思维方式吗?帮助你解决过哪些问题?欢迎在留言区写下你的想法。

03 DoD的价值:你完成了工作,为什么他们还不满意?

在开始今天的讨论之前,我们先来看一个小故事。

小李是一个程序员,有一天,项目经理老张来到他身边,和他商量一个功能特性的进度:

老张:这有一个任务需要完成,你看一下。- 小李:这个不难,两天就能做完,两天以后就能上线。

两天以后,老张又来到小李的身边验收工作:

老张:怎么样,做完了吗?今天能上线吗?- 小李:我的代码写完了。- 老张:测试人员测过了吗?- 小李:还没有。- 老张:那今天能测完吗?- 小李:那我就不知道了。- 老张:什么?我可是答应了业务的人,今天一定要上线的!

很明显,老张有些愤怒,而小李也有些委屈。于是,老张、小李和测试人员一起度过了一个不眠之夜。

听完这个故事,你有什么感想呢?

先不急,我们继续看后面的故事。

又过了几天,老张又来找小李,给小李安排一个很简单的功能。在小李看来,一天就能搞定,而按照老张给出的时间表,小李有两天时间处理这个功能。小李心中暗喜:看来我可以“偷得浮生一日闲”了。

两天以后,老张又来检查工作。

老张:这个功能开发完了吗?- 小李:写完了,你看我给你演示一下。

小李熟练地演示了这个新写好的功能,这次老张很满意:

老张:做得不错。单元测试都写了吧?- 小李:啊?还要写单元测试吗?- 老张:要不为啥给你两天的时间?

怎么会这样?小李心里很委屈,自己明明已经很好地完成了工作,老张是不是故意在找自己的麻烦呢?

好,故事讲完了。是不是有些似曾相识的感觉呢?

为什么小李辛辛苦苦地工作,老张却总能挑出毛病来呢?

老张是不是来挑刺的呢?其实,老张才没那么闲,小李的委屈主要是因为他和老张对于“完成”有着不一样的理解。换句话说,他们之间存在一个理解的鸿沟。

理解的鸿沟

在这个模块里,我们讨论的主题是“以终为始”。

那我们第一个问题就是,“终”到底是什么?在前面这个例子里,“终”就是“完成”,可是,小李认为他的活已经做完了,老张却认为他没做完。

怎么会这样?

二人之所以有分歧,归根结底,就在于二人对“完成”的定义理解的不同。

在第一个故事里,作为项目经理,老张认为“完成”应该是“上线运行”,而程序员小李则认为“完成”是“功能代码编写完毕”。这中间存在的理解偏差,包括了测试人员的测试工作,可能还包括了运维人员的上线工作。

在第二个故事里,老张给了小李两天时间。小李认为这两天都是编写功能代码的,而老张想的是,小李应该自己写好功能代码和单元测试,可能还包括了功能测试,这中间的差异是测试代码的工作量。

因为双方的理解不一致,所以无论怎样努力,小李都不可能达成项目经理老张的要求,正所谓“南辕北辙”。

那该怎么办呢?小李会说,我又不是老张肚子里的蛔虫,怎么才能和他达成一致呢?

答案很简单,既然双方的理解有差异,那就把这个差异弥合上,后面的问题便也不是问题了

弥合差异的方式有很多,有一个最佳实践,它的名字叫 DoD(Definition of Done,完成的定义),从这个概念的名字便不难看出,它就是为了解决软件开发中常见的“完成”问题而生的。

完成的定义

DoD 这个概念本身并不复杂,它就是告诉我们怎样算是完成了,尽量减少因为理解偏差造成的各种浪费。

具体怎么做呢?就是团队在开始工作前,先制定 DoD。

以前面的场景为例,团队可以规定:

特性开发完成,表示开发人员经过了需求澄清、功能设计、编写代码、单元测试,通过了测试人员的验收,确保代码处于一个可部署的状态,相关文档已经编写完毕。

开发完成,表示开发人员编写好功能代码,编写好单元测试代码,编写好集成测试代码,测试可以通过,代码通过了代码风格检查、测试覆盖率检查。

大家都是聪明人,一旦 DoD 确定好了,谁该做什么事就一目了然了。这个时候,如果小李说“我已经开发完了”,却只是写好了功能代码,那就别怪老张手下无情了。

好了,你已经知道 DoD 是什么了,它简单到让人一目了然,相信你很快就能知道该怎样把它用到你的工作里。

不过,我们不仅要知道怎么用,还要知道怎样让 DoD 更好地发挥作用

DoD 是一个清单,清单是由一个个的检查项组成的,用来检查我们的工作完成情况。DoD 的检查项,就是我们开发产品所需的一系列有价值的活动。

比如:编写代码、编写测试代码、通过测试人员验收等。什么样的活动是有价值的,也许每个团队的认识是不同的。但如果你的团队认为除了功能代码,其他都没价值,也许这是个信号,说明你的团队整体上是缺乏职业素养的,在这样的团队工作,前景并不乐观。

DoD 的检查项应该是实际可检查的。你说代码写好了,代码在哪里;你说测试覆盖率达标了,怎么看到;你说你功能做好了,演示一下。

DoD 是团队成员间彼此汇报的一种机制。别把“汇报”想复杂了,最简单的汇报就是说一句“这个功能做完了”。

当我们有了 DoD,做事只有两种状态,即“做完”和“没做完”。在团队协作中,我们经常会听到有人说“这个事做完了80%”,对不起,那叫没做完,根本没有80%做完的说法。

在前面的讨论中,我们所说的 DoD 只是从个人层面入手。

在团队层面,我们也可以定义 DoD。

  • 某个功能的 DoD,比如:这个功能特性已经开发完成,经过产品负责人的验收,处于一个可部署的状态。

  • 一个迭代的 DoD,比如:这个迭代规划的所有功能已经完成。

  • 一次发布的 DoD,比如:整个软件处于可发布的状态,上线计划已经明确。

站在 DoD 的肩膀上

至此,我们只是从软件开发团队内部协作的角度来谈 DoD。

但实际上,它不仅局限在团队内部协作上,如果你可以放开思路,会发现 DoD 的思维在工作中用途非常广泛。

比如,当我们需要和其他团队合作开发一个接口时,我们都知道第一步就是要把接口定义下来。

那么,怎样才算定义完成?很多团队认为落在字面上就够了。

但是有了 DoD 的思维,我们定义接口,就会去明确定义可检查的检查项。那么在定义接口这件事上,什么才是“可检查”的呢?我们可以参照一个可运行的接口来进行评估。

只要检查:

  • 服务方提供的接口是不是和这个可运行的接口返回值是一样的;

  • 调用方是否可以和这个可运行的接口配合使用。

谁错了,谁改去。你可能会问,应该参照哪些可运行的接口呢?

这不难解决,现在模拟服务器的框架到处都是。

如果你不介意的话,我的 Moco 就是这样一个开源项目,你可以看一下。

在协作中一旦确立好 DoD,我们甚至可以通过流程把它固化下来,从而更高效高质地完成工作。

当然,我们在工作生活中难免会有一些临时的工作,它们没有复杂到需要一个流程,但是也可以用 DoD 思维来高效地解决。

比如:

经常会有人过来,让我帮忙做些事。

运用 DoD 的思维,我首先会问他我具体要做哪些事,确认好细节(相当于定义好“检查项”),然后我就知道,这个忙我能帮到什么程度

我请别人帮忙的时候,也会很清楚告诉他,哪些事是需要他做的,尽量减少不必要的误解。

DoD 是一个思维模式,是一种尽可能消除不确定性,达成共识的方式。

我们本着“以终为始”的方式做事情,DoD 让我们能够在一开始就把“终”清晰地定义出来。

人与人协作中,经常会出现各种问题,根本原因就是,有太多因为理解差异造成的误解,进而浪费了大量的时间,而DoD 就是一种将容易产生歧义的理念落到实处的方法。

总结时刻

好,我们来总结一下今天学到的内容。

首先,你应该知道,人与人协作,总会有这样或那样的理解差异。开始协作之前,我们最好先同步一下彼此的理解,确保之后不会因为理解不一致,而让协作方措手不及。

怎样解决大家的理解偏差呢,我介绍了 DoD(完成的定义),它是行业中的一种最佳实践,能够在团队内部很好地同步大家对“完成”的理解。

好的 DoD 是一个可以检查的清单,可以确保你不遗漏任何事情。

如果深入领会 DoD,你会发现 DoD 可以灵活应用在不同的协作场景中。

比如应用于个人工作、团队工作,甚至跨团队工作。当然,你也可以将它灵活地运用于各种生活场景,弥合人与人理解之间的差异,更好地协作与沟通

如果今天的内容你只能记住一件事,那请记住:在做任何事之前,先定义完成的标准。

最后,我想请你回想一下,你在工作或生活中,是否发生过因为双方理解差异导致的问题或不快呢?

有了 DoD 的概念以后,你是不是有了一些新的想法呢?欢迎在留言区留言。

参考资料

http://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/10x%E7%A8%8B%E5%BA%8F%E5%91%98%E5%B7%A5%E4%BD%9C%E6%B3%95