软件开发工作的第一现场

我的同事王健最近写了一篇文章——《从汽车贴膜看专业团队》。看了之后感触良多,特别是文中提到的现场管理和全功能团队两点。

我有一个观点,说到专业性,传统行业比我们IT行业要强得多。从这篇文章可以看出,不管是管理人员的现场管理能力,还是全功能团队中一线人员的全栈能力,传统行业都比我们要强一些。相信有人会有些不服,不管什么现场管理,全功能团队我们也在做呀。

说的没有错,但是,在IT行业里还真不容易找到这么专业的一个团队。而在传统行业里,这种水平的一个团队已经越来越常见了。这是为什么呢?按说大家都是人,通常来讲,IT行业的人能力素质不是还高一点吗?在我们这个行业中,专业团队不是应该更常见吗?虽然我们不一定要比你强,但是也不会比你弱得这么明显啊。

当然,一方面是由于我们这个行业的工作比较复杂,不容易做到全栈,但我觉得更重要的是,IT行业属于知识工作,知识工作者的现场非常不明显,极难做到现场管理。

我们IT行业的管理者,不管是项目经理,产品经理,还是技术领导者,大家也基本都和团队坐在一起。但是坐在一起,并不意味着就能够真的在现场。

我们看到在那个贴膜团队里,团队领导只需要看一眼,发现有气泡,就知道质量有问题。也就是说,在传统行业进行现场管理的时候,问题都是非常直观的,非常容易发现。在软件行业想做到一点就难的多,记得几年前我的同事熊节也曾经写过一篇文章,文章的核心洞见就是软件开发的现场在代码里。

这个想法在ThoughtWorks有很多拥护者,公司里有很多人提出过类似的观点,于是我们的很多方法就是构建于这些类似的观点之上。

然而,如果我们想要追求IT工作者开发效率的极限,这个洞见还不够极致。经过几年的工作,我发现,代码只是软件开发工作的第二现场,软件开发工作的第一现场,在语言里。

这里说的语言,不是编程语言,也不是广义的人类语言,比如汉语、英语。指的是我们在从事软件开发工作中所使用的一系列术语和相关的一系列呈现方式和沟通工具。借用一个技术术语,我们所说的语言是一套仅供软件开发所有相关人员使用的、组合的DSL,DSL全称:Domain specific language,中文名叫做:领域特定语言。

DSL就DSL,还组合的DSL,为什么要说的这么拗口呢?什么叫组合的DSL?我们知道在软件开发的过程当中,需要各种不同的角色参与。每个角色有自己特定的领域,泛泛的讲可以分为三类:我们把产品经理和设计人员所使用的领域特定语言叫做设计语言;把开发和测试使用的语言叫做技术语言;把业务人员、组织管理者使用的语言,叫做业务语言。

所以我们使用的这套领域特定语言是把这三类语言组合在一起而形成的一套语言。这就意味着我们这套语言非常容易充满歧义,造成每个角色自说自话却难以被发现。

软件工程里的核心观点是:一个问题被发现的越晚,修正的成本就越高。比代码更早的是沟通,比沟通更早的就是语言。我们用语言去描述沟通的错误,去描述代码中的错误,我们用什么来描述语言的错误呢?还是语言,这就使得整个工作困难重重,难以达成共识。所以我们更需要非常严肃的对待软件开发工作的第一现场。

之前一些方法试图建立纯粹的统一语言,所有人都说一套语言,事实上这个方法已经被行业放弃了,我们要承认,各自不同的语言有些部分可以简单统一成一种表达以消除歧义,有些部分只能结合。也就是说相关人员要懂多门语言。这个现实是我们必须接受的,软件正在吞噬世界,语言只会越来越复杂。就像我们再努力消除污染,也不能幻想世界回到工业文明以前了。无论我们多么努力的去建立统一语言,也不可能形成一门简单的语言,只能是多门DSL的一个杂合体。

不过由于历史的原因,在行业放弃的过程中,由于“反对预先设计”走的过了头,“不谈建模,不谈标准化”成了一种奇怪的政治正确,导致很多优秀的工具和方法被边缘化了。其实我们憎恨的只是预先设计造成的反馈速度变慢,只是在这个过程中连带上了憎恨预先设计时代的一些工具,这就有点上纲上线了。

幸而最近几年,各种领域建模的设计方法又重新回归。最近大行其道的领域驱动设计,就是在很严肃的对待业务语言的设计和使用。而在前端领域,Design System试图解决前端开发和设计师之间的语言分歧问题。我个人在从事软件开发工程师的培养方面发现,很多传统的可视化工具,比如说UML。如果不以繁重的预先设计为目的,来使用这些工具,仅把它们用作提高沟通效率的工具,他们的威力是十分惊人的。

以我本人的团队为例,我们使用ant design为基础,设计了design system。使得我们可以在三天之内得到一个可以点击的软件原型,并在此基础上,进行各利益相关方之间的需求交流和反馈。在交流的过程当中,我们也刻意的统一了语言,使得我们尽管是一个远程团队,但是在交流的时候,能够很清楚的知道我们在对信息架构在哪一层进行反馈。这不但使得业务方可以反馈技术方,其实技术方也在引导业务方。语言的影响是双向的。

在技术领域里,我们也选择了隔离性更好的技术架构,使得MVP的代码不会变成我们演进道路上必须长期背负的负累。而之所以在一篇聊“语言”的文章里提技术架构,是因为我们认为真正的架构不是纸上的,也不是代码里的,而是每个团队成员心里的架构。实施一个架构必然也是要进行大量沟通,也需要统一语言。

而在交流业务的时候,我们刻意的划分了各种不同的子领域,又在每个领域当中统一了名词。统一名词还是比较简单的,最难的是划分领域,我们为此投入了大量的工作,也犯了一些错误,但这些付出是值得的,这之后,我们的沟通变得非常流畅。

具体的实践有机会再跟大家分享。在这里仅仅聊一下沟通顺畅带来的价值,沟通顺畅的威力并不仅仅表现在沟通的时候可以很顺畅的传递信息,最重要的是当有团队成员对信息理解出现错误的时候,可以很容易的发现和给予反馈。

这个优势在IT行业至关重要。IT行业的人员流动率接近25%,这意味着每年我们团队中至少有1/4的新人加入。即便我们想尽方法让我们的团队保持稳定,随着敏捷和精益创业的相关思想慢慢成为我们的工作常识,每个项目存在的时间都不会太长,这使得IT团队经常性的重组,有时是团队被打散,有时是同一个系统从一个团队交给了另一个团队。如果缺乏一种有效的反馈机制,那么无论是人员流动还是组织重组,所造成的切换成本都是一个很大的。尽管这个切换成本无法消除,但是尽量减少切换成本是我们每个专业人员应该追求的,尤其是团队中的技术领导者。

技术领导者重音在“领导”,而不在“技术”。尤其在Tech@Core的今天,技术就是业务。优秀的技术领导者更不能把自己变成一个救火队员,只是被动的响应,尽管救火队员常常因为很容易被人看到而获得一些关注和赞扬,但在中国的文化里,我们都知道还有更高一层的境界,这个境界存在于很多典故中,比如上医治未病,善战者无赫赫之功。同理,软件开发领域的技术领导者们也应该努力使大多数问题发生的基础消灭于无形,这就需要我们走出舒适区,深入到软件开发的第一现场,进行现场管理。


更多精彩文章,请关注微信公众号:软件乌托邦

Share

团队的精进之道

之前写过一篇文章《编程的精进之法》,这篇文章主要侧重于个人精进之法。然而现在已经不是个人英雄的年代了,我们需要再深想一步,一个团队应该怎么办?

当我们带领一个团队的时候,我们想的总是,如何做好任务分配、平衡团队战斗能力、交付最好的结果。于是做的时候就会下意识的去简单、被动的因材分工,那么随着项目的进展、人员的流动、各种意外的发生,我们在项目后期会感到处处掣肘,于是只能加班以示诚意。

我刚入行的时候,经历的各个项目都是如此,一直觉得这种事情就是天经地义的,直到认识了一个项目经理。该项目经理是个高人,他在项目开始的时候,问清楚每个人擅长的部分,然后让每个人去做自己不擅长的部分,不会?去找擅长的人帮忙。

(图片来自:cargocollective.com/)

比如,张三以前做过用户权限管理,李四以前做过单据管理,王五以前做过工作流。(交代一下例子的上下文,当时那家公司主要就做一个大的领域,不像现在前后端分这么清楚,项目经理有时候还要身兼Tech Lead)他就会说,好,张三去做工作流,王五去做单据管理,李四去做用户权限管理,遇到不会的,谁擅长什么你们都知道了啊,去问。

虽然看起来有点乱来,但是他负责的项目从来没出过问题。后来我加入了ThoughtWorks才知道,这是“把项目成功交付看作能力建设的副产品”口号的一种朴素实现。

很多团队能力不强,团队的领导者就总是在向外寻找方法和帮助。这个行为本身没错,但是做这件事的人往往无法摆正心态,很多人的潜意识是假设团队成员能力不变的,期待在此前提下通过一种魔法般的方法改变团队的绩效,这种思路在真实世界里是走不远的。

在ThoughtWorks,我们认为,软件开发中的一切问题,根本上都是人的能力问题。如何发展每个成员才是问题的关键。如果成员没有进步,始终是治标不治本的。所以我们采用的一切实践,不管是以前曾采用的还是以后会采用的,核心目的都只有一个:发展人的能力。因此才有了那个听起来很耸动的口号:“把项目成功交付当成能力建设副产品”。

如何发展人的能力?讲东西吗?不太靠谱,信息仅靠分享是没用的,我经常把刚讲过一遍的知识,让人复述;把结对时刚写完的代码全删掉让同伴重写一遍,能做到的人不多。记也记不住,做也做不到。

就像我之前在《然而培训并没有什么用》里说的,做练习?没时间,项目太忙了。而且,就算你有时间,我们拿出时间来做练习,你能保证到了跟练习不一样的场景下,团队成员们都能用好吗?把学会的知识在新场景下用好这件事,还是挺看天赋的。

讲东西不靠谱,做练习没时间,那难怪大家不考虑能力建设了。不过,如果我们反过来想,这个问题就变得没那么难办了,既然没有时间做能力建设,那么也许一切活动都可以看作是能力建设。所以那个项目经理的招数虽然看起来比较乱来,却恰恰是这个思路,我在项目开始的时候,不是着急去以最快的速度交付结果,而是通过任务分配,发展团队成员的能力。在一个较长的时期里平均来看,我们就是在以最快的速度交付结果。

(图片来自:cargocollective.com/)

所以,回到我们的主题,团队的精进之道就是把交付过程中的一切活动都看作能力建设,把整个团队构造成促进每个成员成长的生态系统。

说起来好像挺简单,我只要换个角度看就好了,然而想要做到却没有那么简单。这里面差异微妙而关键。

比如以上一篇文章《编程的精进之法》中讲到的方法为例。一个人要划任务、估时间、在做的时候计时、根据实际结果进行反思。我们可以把这个方法做成非常邪恶的、仿佛流水线上工人的强制要求。我不关心你为什么超时,就通过这种方法来控制程序员,要求每个人都严格按照一个死板而僵化的步骤做一些简单重复的机械动作。也可以用这个方法来锻炼一个人的自我认知和发现知识漏洞等能力,促使他以最快的速度成长,等他成长起来马上给他更重要的任务,比如评估技术、评估项目、带新人、做架构等等。这两种结果的差异,背后就是领导者认识的差异、团队成员认识的差异。这其中的不同早在很多年前,就被一些大牛们观察到,作为敏捷宣言里的一句话表达了出来:“个体与交互 胜过 流程和工具”。

(图片来自:http://t.cn/RXYXkek)

团队里的流程和工具,是为了成就个体,促进交互?还是为了抹杀个体,消除交互?这个微小而关键的差异,是一切的本质。有多少团队学了ThoughtWorks的一些实践,搞了看板、开放工作空间、TDD、CI,团队氛围依然压抑,成员之间交流不畅,个体成长不受尊重,领导与员工玩“猫和老鼠”。在上一篇文章《编程的精进之法》的开篇曾经简单的提到,新时代的管理者比起老板,更像老师。师者,传道,授业,解惑。各位老师,团队的未来就靠你们了。


更多精彩洞见,请关注微信公众号:思特沃克

Share

编程的精进之法

编程,众所周知被定义为知识工作。所有的知识工作,从业者和门外汉都喜欢把它神秘化,将整个过程以不可知论的风格来解释。理由往往非常简单粗暴,无非是“照搬体力工作时代的工作方式会在知识工作中失败。”

这其实没什么靠谱的理论。偶有几个人能写写理论与理由,也跟癔症者的呓语无甚差别。我个人是反对将知识工作神秘化的,我是科学管理原理的忠实粉丝,尽管科学管理原理的具体案例都过时了,但泰勒的研究方法依然是有效的,只是研究者和被研究者发生了一些微妙的变化。

彼得德鲁克在《21世纪的管理挑战》中提到,“知识工作者需要自我管理”,那么很明显不是体力工作时代形成的方法不能用在知识工作中,而是不能指望个体之外的人对个体进行简单的粗暴的分析和命令,不要妄想以此产生很高的生产效率。这个分析和下命令的人必须是知识工作者自己,我们需要自己纪录自己的行为,然后分析、优化,才能得到生产力的提升,任何向外诉求都会很快的撞上一个“天花板”而无法提升。如果非要寻求外部干预,那么我们只能说,对于新时代的管理者定位,老板更像老师,以引导和帮助训练为主,真正的效率提升主要还是靠自己。

1-floor

那么书归正传,追求知识工作的一种——编程的效率,是本文关注的重点。但我们首先要声明,本文不会给一个可以直接产生高效率的方法,而会给一个可以将生产效率可视化,并从中发现瓶颈的方式。至少在不改变质量的前提下,可以极大的提升你的效率,如果使用得当,可能会得到质量和效率的双提升。

本文引入的方法也并不新鲜,简单说来,就是任务列表法+PDCA的一套组合使用而已。大道至简,坚持者寡,而坚持下来的人往往都可以获得数倍的效率提升。

任务列表法

我们做任何事情都应该划出任务列表,按照列表一项项去完成,这不是什么特别稀奇的工作方式。然而,很多人的问题在于,列出的问题列表不能达到完全穷尽,各自独立。

2-todolist

完全穷尽是什么意思呢?

当我开始做事情的时候,如果不能把所有的事情穷尽,我列出的列表跟我做的事情是不完全等价的,这说明我们的工作行为非常混沌且不可视,哪怕是对自己。

有时,事情看起来在大面上穷尽了,但是做的时候,又会发现新的任务。那说明每一项任务的输入和输出没有想清楚,因此每当发现有所欠缺,就需要输入新的任务作为补充。于是任务列表就增加了,这也是一种没有穷尽。

各自独立是什么意思呢?

意味着,每一项任务都可以单独做完,而不需要先做完其中一项任务,才能做另一项。 假如我有三项任务:任务1、任务2、任务3。

我做的时候,必须把任务2做完,任务1才能做完;任务3做完,任务2才能做完。结果我就从任务1开始一路做到任务3,最后再逐步回到任务1,整个过程非常混乱,那就不是各自独立的。

在现实生活中想做到各项任务都独立,挑战还是比较大的,但是在编程的世界里就轻松多了。优秀的设计都是要求解耦的,如果做不到,基本等于活儿比较烂。

3-fly

当我们做到“任务的完全穷尽与各自独立”之后,我们的任务列表法才算达标,这之后才能高效的工作,然而达到这一点并不是一蹴而就的,没有谁可以一上来就做到任务划分的完全穷尽、各自独立,需要不停的刻意练习。所以我们称之为编程的精进之法。

PDCA

PDCA是Plan-Do-Check-Action四个词的组合。这是著名的戴明环。讲究从计划开始、经过实践、反思、产生的改进行动再纳入下一轮计划的持续改进过程。

当我们把这一套从工业领域搬过来的时候,我们对计划的理解还是工业领域那一套。如果用在个人提升方面,我们应该把PDCA微观化,这之后就有两个问题需要被解答,一个是Plan是什么?一个是Check什么?

第一个问题的答案很显然,我们前面讲的任务列表法就是在形成这个Plan。

第二个问题本身是一个母问题,每当我们对这个问题进行回答,都要回答一个衍生出来的子问题:我们要做点什么才能在需要Check的时候能够Check。

常用的套路有两个:

  • Plan的时候估计一个时间,然后开始做,做的时候计时,做完就要Check这个时间是否达标,无论快了还是慢了(通常是比较明显的差距才能引起反思,比如20%以上的差距),Check都要反思并产生Action,纳入到未来的Plan中去。
  • 估计的任务列表和实际做的任务列表是否是一样多的?往往是会多出来,这时就要反思,自己在哪里有不足导致了这个差别。

这些反思往往是发现自己的问题,比如自己不熟悉的知识点、方法,甚至业务知识,最后的Action也往往都是通过刻意练习来提升生产效率,比如反复练类似题目。有时也会借助一些工具来提升效率,比如抽取live template,使用快捷键,只是效率工具的使用往往也需要刻意练习。有时也可以通过复用技术(其实live template已经是复用技术了)来提升生产效率,然而可复用模式的识别与抽取本身也是需要练习的,否则在那里纠结浪费的时间更长。

有些同学会感觉到,记录了时间却不知道哪里有问题,这个时候可以跟TDD相结合,把时间划分为写测试的时间,写实现的时间和测试通过的时间。其实除去这几种时间,还有其他时间消耗,比如调研的时间。不管怎么划分,将时间消耗结构化掉,一部分一部分的追求最高效率是一种可行的办法。

举例

我们做一个简单的修改用户信息功能的API。那么我们在某一个Java技术栈上可能的任务列表是长这样的:

  1. 写UserController (10分钟)
  2. 写UserDAO (15分钟)

当你真正开始做的时候,会碰到两种主要的意外:

  1. 任务列表扩张
  2. 时间估计不准

下面就这个例子,就讲一讲当我们遇到这两种意外,该怎么反思和处理。

任务列表扩张

任务列表扩张,顾名思义,就是指我们所估计的任务数量会随着我们开始工作变的比预想的多,可能有两种主要原因:

  1. 技术原因
  2. 业务原因

技术原因:

比如在这个案例里面,第二项任务是“写UserDAO”,就是一个没想清楚的事情。我们还需要建数据库表,我们在一个有migration脚本支持的技术栈设计上工作,我们还需要写初始化脚本和回滚脚本。也许这是我的第一个表,所以我还得配置数据库,搞不好还要把ORM的基础代码都写完,所以这些导致了我可能任务估少了。

再比如,项目规范要求我们Controller不能直接调DAO,要在中间加一个Service,尽管我个人觉得这是一件很二的规范,然而规范就是规范,我对项目技术规范不熟悉,导致我的计划缺少了一些必要的任务。再比如,我们的项目采用了Jersey,根本没有Controller这么一个东西,那么不了解技术框架导致我的任务表从根本上就列错了。

这种情况属于我对技术了解不足,通过对任务列表扩张的原因进行Check,我会得出一些Action:去了解技术规范、项目的技术架构、现有的代码,以防止以后的任务画错。

业务原因:

也比如在这个例子里,在更新用户的API里不能更新密码,所以我们还需要一个专门修改密码的API。再比如,这是一个遗留系统,用户信息的修改会触发数据库里的一系列触发器,进而修改系统的其他数据,然而有些修改是有前提的,那么我就需要更多的任务去处理这些前提条件;或者当数据变化时,要求我去修改系统里的其他数据,那么我就需要更多的任务去完成这些工作。

这种情况属于我对整个系统的业务了解不足,通过对任务列表扩张原因的Check,我会得出一些Action:通读数据库表、通读代码、更全面的阅读需求,或者跟需求方更多的沟通,以了解业务。

时间估计不准

时间估计不准就简单很多,在这个例子里,可能的主要原因也有三个:

  1. 任务列表扩张了,但是我没意识到。比如UserDAO写起来没有我想的那么简单,所以多花了时间;
  2. 单纯的技术不熟练;
  3. 花了太多时间在纠结上;

对于隐藏的任务列表扩张,不准确的时间估计给了我们一个很好的线索去发现。一旦发现了,可以如前文所述去处理,也就不再赘述。

4-time

对于单纯的技术不熟练,正如前文所述,要设计刻意练习。比如我就曾设计过针对数据库的增删改查训练以提升自己的速度,使我即便使用TDD依然保持一个极高的速度。我们或许不曾意识到,基础能力的薄弱对于我们的高级能力的限制有多严重,这种体验也只有基础能力已经熟练的人去教基础能力不熟练的人一些高级技能的时候才会发现。这种视而不见的收益,使得大多数人都会轻视基本功的练习。哪怕已经获得收益的人,也容易鼓吹要更多的启发而忽略了基本功的价值。

对于花了太多时间在纠结上,这其实也是一种不熟练,是对设计知识和能力的不熟练。之前看的设计知识只能有一个大概的感觉,对于每个知识的边界、使用之后的发展、如何从一种设计过渡为另一种设计了解不清,从而害怕在那一刻犯错。实际上真正值得纠结的部分没有那么多,大多是自己吓自己,或者引入了过度设计。

当然也有一种情况是暴露出了架构级的问题,比如我们对于应该提出的原则性规范没有提出,导致我们每个地方都要现想,大家可以想象在没有RESTful之前设计Web API,我们可能真的是每一个API都现想的,有了它之后,我们的纠结时间就变少了。这种情况下,通过本方法,架构师也算是有了相应的数据支持,那么架构师也就有了发现问题的一种工具。

结论

总的来说,任务列表法+PDCA式工作法形成的组合方法,是一个通过逐渐提升个人能力以实现高效工作的方法。这两种方法单独拿出来用,都会由于各自的局限而触碰到各自的天花板,只有有机结合才能真正突破这个天花板。

刚开始使用时,很多人会感觉到一些痛苦,这一点上我只能说,提升就是痛苦的,而新的习惯一旦养成,痛苦也就不翼而飞,所以美国心理学之父威廉詹姆士说,“我们需要在尽可能早的时候,让尽可能多的有用动作变成自动的和习惯的……一段痛苦的艰难时期之后就是自由的时光”。当我们的基础能力达到一个极高的水平之后,会发现争取自由的筹码会变得更多。

5-freedom

更多精彩内容,请关注微信公众号:ThoughtWorks

Share

然而培训并没有什么用

人们对于培训这件事情总有一些不切实际的想法。

我们期望,那些不具备某些能力的人,通过培训就具备了。就像洗车一样,通过一个黑屋子,出来就是一辆崭新的车。

1-car

而实际上,在专业领域,一个更悲观的论调是被广泛认可的,那就是:培训没有用。

1、既然没有用,为什么大家都在搞培训呢?

其实我们应该反过来想这个问题,既然行业里普遍认为培训没有用,而到处又都在搞培训。那大家的印象里一定有“培训有用”的印象,所以才会不断地去做培训。问题就变成了,“我们什么时候觉得培训有用了?”

从我个人出发,来ThoughtWorks之后,我接受的最有用的几个培训分别是TWI、OO BootCamp、DDD。

上完培训颇有顿悟之感。受启发之后,我拿学到的东西去跟其他人交流,发觉有的人顿悟,有的则是不明觉厉;有的在接受培训之后会发生较大改变,有的则像什么都没发生过一样。

我对比了一下这些不同的人,可能的原因是这样的:这些培训之所以对我们有用,是因为我们在参加培训之前就已经在软件开发的各个方面都积累了足够多的经验,就差有人给我们梳理一下了。

所以我常常用最后一块馍来比喻培训。用吃饱的感觉来比喻顿悟的感觉。

2-insight

后来我跟其他人的交流,则是拿着最后一块馍去喂别人吃。同样差这一块馍的人,吃下去马上顿悟了。但是还没吃到足够馍的人,也就只能听个热闹。

2、培训到底有没有用?

当充分准备好的人,遇到充分准备好的内容时,是有用的。而这里面最难满足的条件是充分准备好的人。这是可遇而不可求的。

不知道大家看到这个结论的时候什么感觉,反正对于我,一个专职做培训的人来说,简直是死刑判决一样的感觉。

当我开始去给毕业生做培训的时候,这个“没准备好”的情况就越发的严重。我们完全可以在培训前就预言:培训完几乎感觉不到变化。当想到这个,我的内心几乎是崩溃的。

好吧,虽然现实很坑爹,但生活还要继续。

既然培训有用的关键在于充分准备好的人。那么有没有这么一种情况,人并没有充分准备好,但是培训依然有很好的效果的呢?为什么出现了效果呢?

我个人的经历里,只碰到过一次:韵涛(ThoughtWorks交付总监)的战略课。

当时这个课程,其实我也没有完全听懂,很多内容停留在“我以为我懂了,其实完全没懂”的状态。

但是为什么效果好呢?因为这个课程在我心里种下了一个种子。就像是盗梦空间里说的那样,“在意识的底层被注入了一个想法”,我看到一个企业的行为,就忍不住去分析一下,他们的战略是什么?为什么要这么做?可不可以做别的?有什么在影响他们的决策?

我觉得后来这无穷无尽的自我训练,才是我成长的关键。

3、也许培训的重点不是培(讲课),而是训呢?

编程是一门手艺活,这个世界上的手艺都是练会的。基本功不熟练,说别的都是骗人。也许生产“准备好的人”的最有效方法,就是我们早就非常熟悉而又深恶痛绝的那个——题海战术?

于是我们出了下面的一些题目(大概70道),要求学生做简单的集合运算。

 

然后,魔鬼般的训练开始了。当时的场景让我想起了:

4-devil-training

训练开始之后,我跟徐昊交流了一下经验,当时的对话场景是这样的:

我: blah blah……

徐昊: 这类似于体育里的体能训练。

我: 是的。

徐昊: 体能训练不是应该天天练吗?

我: ……

于是,本来只打算做一遍的基本训练,变成了天天都在练的体能训练。当然考虑到不能一天都练这个,于是我们简化了一下,每天做的练习类似于:“选出一个集合中下标为偶数的元素,在选出的元素中,选出元素为偶数的元素。将其按几位数分组,对每一组求中位数,放到一个数组里为最终结果”。

掐表,不能超过一个小时,当然难度也是一直在加的,除了条件会改以外,还会让他们抽取一个filter的高阶函数来用等要求。

4、这些简单粗暴的训练会有用吗?

结果是出乎意料的,进行过这些训练的同学,他们不但写代码的速度提升了(这简直是一定的)。

而且相比没有进行这些训练的同学,在面对一些比较抽象和复杂的编程技巧讲解时,更能跟上我的节奏,有的甚至还能指出我的错误。

是的,准备好的人诞生了。虽然还没有达到我们期望的那种“有sense”的人,但是相对于他们自己,有了明显的突破。而诞生的方法,是我们最痛恨的题海战术。我们总会变成我们最讨厌的那种人是吧。一个被题海折磨的人,若干年后成为了出题折磨别人的人。

5-training

当然,这是开玩笑的。

曾几何时,我们开始相信一个都市传说:中国人没有创造力是因为填鸭式教育和题海战术扼杀了我们的创造力。于是我们对于这种简单粗暴的方法总是下意识的排斥。

那我在这里不得不给它辩护一下:这个方法,粗暴是粗暴了一点,但并不简单。

5、实际上,题海战术与创造力并不矛盾

任何领域只有对基本能力拥有直觉般熟悉才有更高级的综合应用,包括创新。

而建立直觉般熟悉的最快方式,要么在短时间内进行高密度的训练,要么只能用更长的时间获得,反正训练量是守恒的。

但一个方法再有效,人们恨它,也不会有太多人用。为了兼顾效率和人性,我们的思考不能停止。既然题海战术没有错 ,为什么我们这么恨它?

我想来,大约是因为没有针对性。

假设有10个知识点,其中9个我已经不需要训练了,有一个我还很弱。但是我眼前这10道题,只有一道是针对那个弱项的,我训练一次弱项,必须训练9次强项,这显然是充满浪费的。

而每个人又都是不同的,哪些是弱项,哪些是强项,何时加难度,何时引入更复杂的练习,到什么阶段限定什么时间长度选什么题,都要根据人处于的阶段不同去做调整。要有压力而又不能压垮。这个环节上我们需要个性化的关注。那就不得不考虑教练的角色。人的问题需要人解决,想想看也是个让人很头疼的事。(要么就是强人工智能,强人工智能还没有诞生,诞生了……还要培训干什么……机器就该取代人类了)

6-robot

所以,一个真正有用的培训,我们需要培(讲课),需要训(练习),还得是大量的。不仅如此,为了让人能坚持下来大量的训练,还需要教练的个性化关注。这些条件往往无法同时具备,难怪培训是没有用的。我们进行了长时间的探索,得出了行业通行的结论。是不是一个很悲剧的故事?

我个人觉得不是,我们起码不再像一个原始人一样去看待这个结论,“培训没有用”从一个神谕般的断言,变成了一个由具体的,可以看得见摸得着的原因支撑的结论。问题被细化了,那么就给了我们质疑的着眼点,给了我们改进的着手点。坚持分析、实验、改进,也许未来我们就能看到一篇文章——《培训真的有用》。

 


你想看到的洞见,都在这里

wechat

Share

像机器一样思考

本文的起源是因为思考一个问题:什么样的人适合做程序员?

我曾经苦苦思索这个问题,直到我在SICP上看到了答案。说的白话一点就是,能像机器一样思考的人就适合做程序员。 那么“计算机”这台机器是怎么思考的呢?这里是我的答案:

1

我们所有的计算机,都可以用下面这个模型来表示,江湖人称“冯・诺伊曼体系”。

从这个模型上我们看到了什么吗?嗯,可能太多干扰了,看的不够清楚,我给你们再抽象一层:

1.pic

现在清楚了吧?计算机在中间,两边是输入输出。所有的问题都从输入和输出的角度去思考,这就是“计算机”这台机器的思考方式。也就是说你能做到这样思考,你就会像机器一样思考了。 很简单吧?但是新的问题又产生了,处理自然要处理输入、产生输出了,输入、输出是些什么呢?这就要在微观层面理解机器是怎么思考的,这一部分叫:机器在加工什么?

SICP中又说了,“非形式的讲,我们只在处理两种东西,数据和过程,他们之间并没有明确的区分。”先不管是否有明确区分,我们回看模型,中间处理的部分其实就是过程,输入和输出则是数据。(在冯诺伊曼体系里,数据和过程被称为数据和指令)那说到数据,我们有一门学科叫做数据结构,它很好的解释了什么是数据。我们还有面向对象、类型系统之类的知识,他们都能帮助我们更好的定义数据。

各位看官估计心里犯嘀咕了,扯了这么多,还是无法想象怎么就算像机器一样思考了。不急,下面我们拿几个例子来学习一下。

我们来写一个加法函数,接受两个参数作为加数和被加数,返回一个和,这个太简单了,几乎任何一个程序员都可以在几秒钟内写完。拆成机器的思维是什么样呢?

加法函数

加法函数 
输入:
  a   
  b 
输出:   
  result            

大概就长这样,输入是a和b,输出是一个结果,我们起名叫result。它到底表达了个啥样的代码呢?大概长这样(本文所有的代码都会采用javascript描述,但是不代表本文内容,只适合描述前端开发):

function add(a, b) {
    return a + b; 
}

咦?result哪去了?在你调用的地方可能会有一行代码:var result = add(1,2); 这个表达方式不仅仅可以用来描述函数定义,还可以用来描述表达式。比如,如果我们把前面的输入输出思维描述改为加法表达式。你会发现这段描述“编译”成代码大概长这样:

var result = a + b;

所以不仅仅可以用来描述函数定义,还可以描述代码块。

但是到这里就结束了吗?感觉好像对数据的表述不够细致啊。确实,我们忘了加类型了。不加类型这描述简直万灵丹啊,反正俩参数一个返回值的都能用,这不行,我们还得把类型加上看着才清楚点。加上类型就变成了这样:

加法函数 
输入:   
   a: Number  
   b: Number 
输出: 
   result: Number

这看着就好多了,是不是比刚才更加理解上文所讲的“像机器一样思考”了呢?好吧,你可能会说,“这玩意有啥用啊,我有分析的这个空,我代码都写完了啊。”不急,我们接着往后看。

刚才那个题目有点太简单了,我们做一个稍微复杂的。比如下面这个:写一个函数,可以选出一个由数字组成的集合当中所有的偶数的最大值。

这回一步做出来可能就有点难了,没关系,我们可以分成两步:

  1. 选出集合中的偶数
  2. 选出偶数中的最大值 这两步呢,按照我们之前的格式写一下,大概是下面这个样子:
    #1 选出集合中的偶数 
    输入: 
      inputArray
    输出: 
      evenArray

    #2 选出偶数中的最大值 
    输入: 
      evenArray 
    输出: 
      max:Number

哎呀,突然发觉不知道该怎么描述集合呢。Javascript里就用数组就好了,但是还是不知道怎么描述数组啊。这个其实很简单,这不是一个由数字组成的数组吗?我们只要写成[Number]就可以了。因为我们的一个好习惯是一个集合里不要放两种类型的元素,所以就这么写就好了。那么加上去的话,大概就长这样:

#1 选出集合中的偶数
输入: 
  inputArray:[Number] 
输出: 
  evenArray:[Number]

#2 选出偶数中的最大值
输入: 
  evenArray 
输出: 
  max:Number

咦,第二步的evenArray没有写类型。嗯,因为evenArray是第一步的输出,我就把它省了,相信大家也能看明白。 耐着性子看到这里,你估计已经发现了,我还是没有回答你“思维方式有什么用”这个问题。我很想忽悠着你再做一道题,不过估计你坚持不完就会转身离开了。那我们就这两道题试着讲一讲。

第一道题,我们只是展示了这个思维,第二道题,我们才开始发挥出它的威力。尽管这道题也不复杂,但是思考过程还是展示了:

  1. 分解问题;
  2. 找到子问题之间的关联(通过输入、输出关联起来);
  3. 找到问题的边界,明确假设与结果。

上述三点看着简单,却是思维清楚与否的关键。我们管这个能力叫Analytical Thinking。

思维清楚带来的收益是什么?这些步骤可以直接转化为工作的任务列表,而且可测试。这样分解出来的任务列表,完成效率是极高的。我们曾经做过实验,按这个思路分解过问题的人,比没有分解过的人效率要高3倍以上,而且前者只学了一周的编程。 一个完全不会写程序的人,只要学会了这个思维,就可以开始编程之旅了,而且威力非常巨大。

听起来好简单啊,有那么神吗?不是编程的人都应该会吗?然而并不是的,很多人思考编程这件事情是靠感觉的。 我前几天面了40多个外包公司外派来的人,只有5个人,可以按照输入输出来对问题进行分解。所以我觉得我还是有必要写点东西来讲讲这个。

除了对初学者有益之外,对Team Lead也是有益的。当你觉得你遇到的人没sense的时候,你可以试着让他们这么表达一下程序。一般就会发现一些问题。

题外话-1:

我们像机器一样思考,不就都变成机器了吗?嗯,其实不是的。所谓我们像机器一样思考,那机器这种思考方式又是从哪里来的呢?机器的思考模型是一个叫“图灵机”的计算模型,而图灵机则是图灵祖师爷模拟人思考而发明出来的。所以,其实不存在什么像机器一样思考,只不过是学会一种人类的思考方式而已。 考虑到图灵只能以自己和自己周围的天才科学家的作为人类的具体实例来抽象图灵机,所以我们学习的其实不是什么机器的思考方式,而是天才的思考方式,这篇文章其实应该叫《像天才一样思考》。

题外话-2:

这个不就是面向过程编程吗?如果你的思考仅仅停在这里,那就是面向过程编程了。如果我们接着想下去,当数据复杂到一定程度的时候,我们会自然的引入封装,于是面向对象诞生了。回到数据与过程不严格区分那半句,当我们试图模糊数据和过程的界限,将过程像数据一样纳入输入输出的范畴,我们就走上了函数式编程之路。

题外话-3:

有人觉得练习不够吗?请留言,如果感兴趣的人多,我就加紧写更多练习的解析。

Share