那些年,我见过的那些"废柴"

那些年,我见过的那些“废柴” 序 在我们的日常工作中,和其他人一起合作在所难免。与三观未知的他人朝夕相处往往不啻于一场冒险。那些传说中行云流水般流畅的合作似乎仅存在于书本中,现实世界中的合作则常常难以契合,大多互相掣肘,不得通畅。毫无疑问,在众多的项目中,与我合作过的很多同事都令我大开眼界并深受启发,并在或长或短的合作中受益良多。作为对比,同样有很多同事(我把一起工作的人们都成为同事,而并不特别区分内部外部,比如团队上的其他合作方也可以视为同事)则从相反的方向令我印象深刻。 我仔细回想,如果整理出一个“我最不希望与之合作的人之特质”的清单的话,一来或许可以警示自己不要成为这样的人,二来潜在地也可以让具备这些特质的人从中受益。古语有云:有则改之,无则加勉,此之谓也。 无法根据事情的重要/紧急程度划分优先级而导致自己疲于奔命的 只知道抱怨而不着手解决问题 能说不能做的 固步自封,不思进取 急于撇清责任,将自己置身事外 缺乏责任感,将问题归类到自己无法的力量的 不会化分优先级 在一个团队中,效率最低的当然是那些什么都不干的人。不过可能与大部分人的直觉相反的是,什么都做的那个人效率是倒数第二低的(如果不是最低的话)。这样的人被巨大的backlog支配,深陷其中而无法自拔,各个未完成的任务可能还会互相影响,直至崩溃。很多时候,这些人手头的工作甚至都不是被分配的,而是他们自己揽上身的。 如果你认为所有任务都很重要,也就意味着所有任务都不重要。具体来说,这样的做法有很多潜在的问题,对于个人和团队都有很大坏处: 个人成为信息孤岛 由于太多并行任务,无法真正完成某一项任务 WIP(work in progress)队列变的无限大 成为团队的瓶颈 正如《凤凰项目》中描述的场景,那个Superstar成员成为了团队中最大的瓶颈。当那个Superstar的带宽被完全只用之后,所有的在队列中的工作都需要进入等待状态,而这个曲线是呈指数态势增长的。 一个优秀的团队成员,需要在某种程度上遵循UNIX哲学中的一条原则:每次仅完成一件事,并将其做到最好。当然,这并不妨碍一个团队成员在很多方便都有优秀的技术专长,重点是:在每次任务重都专注的做好某一件事。 最有意思的是,在这种情况下,即使这位同事的主观意愿是好的,个人能力是出色的,但是从外部来度量整个团队的产出的话,其效率会低于平均水平中等,但是任务分配合理的团队。也就是说,在策略上有缺陷的忙事实上是在帮倒忙。 祥林嫂式的抱怨 在我早年的一个项目上,在一个产品的研发进行到某一阶段后,团队进行了一次类似于Bug Bash的活动:收集产品中潜在的设计缺陷(包括可用性以及功能性的设计)。有一位同事从各个方面提出了近30条发现的“问题”,但是几乎没有给出一条有用的建议。基本上就是一副:这个产品是个彻头彻尾的垃圾,而我也不知道怎么能把它变得更好的心态。后来团队只好草草结束了这个有始无终的设计缺陷收集,而重新开始做一些Up Front Design。 在工作中这种人是非常容易招人厌恶的。一方面他们往往散发着浓郁的负能量,看着什么都充满怨念,另一方面,他不愿意或者不能够提出任何有效的改进方法。 应该注意到的是,提出问题 – 特别是不需要自己去费力解决的问题 – 是很容易的,而能想到可能的解决方案则相对困难。更进一步,想到一个直观的方案很容易,而给出多个选项,并对比各自的优劣则很困难。最后,如果你想要真正解决问题,或者让工作实际有所推进,那就需要尝试多做一些困难的事情(比如罗列并对比可能的方案,并给出可行的计划),并尝试简化他人的工作。 眼高手低 事实上,业界有很多坐而论道的大神。言必称高内聚、低耦合,动辄S.O.L.I.D,对各种编程范式更是如数家珍。但是具体到动手的时候,要么顾左右而言他,要么勉强写出几行未必能通过编译的代码,而内容则不忍卒读。 遇到这种情况,我会在他用鼠标哆哆嗦嗦的在文件列表里逐个找文件的时候把键盘抢过来,然后确保不让他碰键盘。如果心情好的话,我会规劝他多动手写一些代码。 要解决这个问题,方式其实并不复杂。胡适说:多研究些问题,少谈些“主义”。要我说,应该多写写业务代码,少谈些理论。我们都知道,在实践中,用TDD写个FizzBuzz是一回事儿;用相同的方法和原则把复杂的业务逻辑抽象并归类,则完全是另一件事儿。 这些人往往会混淆了知道和能做到间的界限,知与行并不统一。正如那个著名的画马的漫画所要表达的那样: 大部分情况下,我们缺少的不是用简笔画画出几个圆圈,而正是那些被轻视的细节。无论如何,我们的各种理论最终还是要体现在可以真正执行的软件代码上。 Talk is cheap, show me the code. Linus Torvalds 一些可供参考的常见的技巧是: Code kata 用脚本来自动化一些常见任务 重构复杂的业务代码 简而言之,坐而论道,不如作而行之。 致命的舒适区 这种人痛恨变化,这种情感可能来自于过往项目中引入新技术之后的负面的体验,或者纯粹的对新鲜事物兴趣的缺失,又或者二者兼而有之。总之你从他们口中经常听到的是诸如:“为什么不用redux”(因为redux我比较熟),或者为什么要用“Material-UI”(因为我不会)。事实上,这种情况甚至会发生在那些曾经热切而激进的技术人员身上。 大多数情况下,是他们没有勇气走出“舒适区”。他们误以为目前已经熟练掌握的技术永不过期,且是解决手头问题的最佳方案。然后现实是,没有什么是不变的。技术很快会过期,通常比我们想象的要快很多。 在我入职ThoughtWorks的时候,RoR是西安办公室的主流,前端的Backbone+Jasmine算是新的技术,而两年后一些团队开始零星的试点Angular1.2,bower,PhantomJS等,而如今这些技术都去哪儿了呢?React+Redux,GraphQL在很多场景下确实可以简化前端的工作,不过谁又知道下一个breaking change正在何处默默酝酿呢? 与固步自封相对的另一个极端是追逐一切新奇的事物,这样的做法亦不足取 – 它会浪费你大量的时间和精力在那些可能永远不会涉及的技术上。...

August 29, 2019 · 1 min · 邱俊涛 | Juntao Qiu

无意识偏见即其他

It doesn’t make sense 我偏故我在 我们的大脑在很多方面都可以算是进化上的奇迹了。在大脑及神经系统的帮助下,我们发明了各式各样的工具和技术。更进一步,利用这些工具和技术我们可以建造摩天大楼,可以将数吨重的飞行器发送到千万公里之外太空,可以将数千年的文明历史压缩并记录在手掌大的设备中,可以构建复杂的社会关系和经济活动,当然还可以编写用途广泛而且无处不在的软件系统。 这个神奇的仅重1.5公斤的器官似乎蕴含着无穷的可能性,然而它并不完美。甚至可以说有着诸多缺陷。它的处理速度有限,经常把存储于其中的信息搞混,它非常容易被各种外部因素吸引从而将手头的任务搁置,当信息片段之间出现间隙时,它还会自作主张的向这些间隙中加入很多实际上并没有发生的事情 – 仅仅为了让信息看起来make sense一些。 在进化中,我们需要依靠大脑有限的处理能力快速从环境中推断出对我们有害/有利的因素,并付诸行动(Fight or Flight)。相对于没有这些能力的其他个体,我们的祖先通过这种能力存活下来,并将基因延续至今。 在应对复杂多变的外部环境这一点上,偏见可以在很多情况下帮我我们:比如根据经验,比我们高大的动物往往都很危险,当这个经验植入基因之后,即使我们遇到了一只从未见过的但性情温顺的大型动物,依然会自然的感到畏惧 – 这种偏见在进化中使得我们更具优势。 由于大脑每秒钟要处理大量的数据,它需要一种机制将多余的信息过滤掉,将信息按照特征来分门别类同样是必须而且高效的。大脑非常擅长给不同的人和事物打上不同的标签,这样都新的输入来到时,它就可以非常快速的做出判断。从进化的角度来说,大脑的这种走捷径的方式无疑是经济且高效的,但是当我们进入到文明社会,这些在进化中的优势有时候则会成为前进的障碍。 比如我们常说的刻板印象,即粗略而无意识的将团体和从属于团体的个体混淆起来。从四川人都爱吃辣广东人吃一切到各种地域黑,从女司机都不会开车到亚洲人的学习客户且数学都很好,再到中国式过马路是国人之陋习等等,这些刻板印象(stereotypes)都可以归为无意识偏见的范畴。 无意识 在英国Leicester大学心理学学院的一项实验中,研究人员在超市的红酒架旁边播放不同的音乐作为BGM(Background Music),并观察音乐对消费行为的影响。结果非常有趣,当音乐是法国音乐时,有76.9%的人会选择法国产红酒,而当音乐是德国音乐时,有73.3%的人会选择德国红酒。而在试验后的访谈中,只有14%的人表示注意到了背景音乐。也就是说,大部分时候在我们做出某些判断的时候,并不是意识(也就是我们通常说的“我”)做出来的。 现实中的典型场景 虽然乍看之下似乎令人难以接受,但是偏见是始终而且会永远伴随着我们存在的,它植根于我们的DNA中。尽管如此,我们仍然相信,意识到这一点与对其没有感知之间的差异,对于我们的学习和生活的会是非常巨大的。只有认识它、理解它并接纳它,我们才可能比以前更加客观的做出各项决定。 这篇文章里,我打算从三个典型场景来讨论偏见对我们的影响,以及你应该如何应对它。 企业招聘 在企业招聘界有个经典的笑话:HR会从一堆简历里面随机抽出一半丢进垃圾桶,理由是不要运气不好的人。不过自诩理性的我们,事实上在简历筛选和面试中并不比这个方式好多少。来自多伦多大学的两个医生Donald Redelmeier和Simon D. Baxter通过对医学院入学面试的历史数据分析后发现,下雨天的候选人的分数比晴天的候选人得到的分数往往更低,而这些分数与这些候选人的实际水平无关。所以下次找工作的时候,务必把面试约在一个晴天。 候选人脸上略显焦虑的神色,他身上的某个牌子的牛仔裤,甚至候选人凑巧和你大学时很讨厌的同学有着相同的口头禅这一特点等,都可能会成为TA在面试中的减分项,而且这一切都发生在你真正意识到之前。这种无意识偏见的可怕之处在于,它无处不在,难以被我们意识到,并且这些对TA不利的假设都和TA是否可以胜任工作没有任何关系。 甚至多个面试官并不能真正解决这个问题,有时候反而可能会使情况更糟。比如面试官A的有5个不同类型的偏见,而面试官B有另外3种,那么对于被面试者来说被不公正判断的可能性反而更高。最后,假设面试标准是客观公正的,依然有一个更为阴险的确认偏差(Confirmation Bias)的偏见在作用。 确认偏差 What the human being is best at doing is interpreting all new information so that their prior conclusions remain intact. — Warren Buffett 确认偏差(Confirmation Bias)是指人们会选择性地回忆、搜集有利与自己观点/认识的细节,忽略不利或矛盾的资讯,来支持自己已有的想法或假设的趋势。简而言之,就是人们倾向于相信自己已经相信的。 也就是说,如果你在面试中,甚至第一个照面时在潜意识中对候选人产生的印象就一定程度上决定了你对他的认识(后边的流程在很多时候就是在走过场),而后续的各种确认中,你会不自觉的选择性收集利于之一认识的证据。比如你喜欢他,你就会发现他对于之前某个项目的描述很清晰,应该有过hands-on的经验;但同时对于他写代码时频繁出错的事实却视而不见。最后在所有面试官一起投票表决时,你对这位候选人的印象就只剩下优点了。 下面这个韦恩图很形象的表述了这一点:你看到的既不是完全客观事实,也不完全是可以支持你结论的,而是两者的交集(即你希望看到的): 当你在面试别人时,请时刻在心中保持警惕。你对TA的某个判断确实是来自于对事实的观察吗?还是来自其他细节?如果这个判断不完全来自于事实,那么你可能已经不自觉的使用了某个无意识偏见了。(是不是有点像胡八一在精绝古城里被尸香魔芋迷惑的感觉?) 开发活动 在号称客观,理性的开发活动中,我们依然被情绪和各式各样的偏见所左右。很少有人愿意承认自己的技术选型是被一些难以觉察的偏见影响过的。这些偏见对于我们来说,好像水和鱼关系 – 我们生活其中,却难以觉察。除非机缘巧合下,我们跳出了水面,才能看到浩瀚无垠的大海。 我们会为哪个语言是最好语言的而吵得面红耳赤。人们喜爱自己熟悉的东西,看看网络上的各种技术拥趸对自己钟爱的技术的论战,比如PHP作为最好的语言,Emacs是正统的编辑器,纯粹的函数式编程语言等等,就知道人们对于自己相信的事物有多么的幼稚和可爱。 首先,确认偏差确保了我们只能看到自己想要看到的,那些在实际场景中不是很契合的部分很多都被选择性过滤。 其次,我们倾向于喜欢自己熟悉并投入更多心血的技术。而且投入越多,就越难放弃,心理学上称之为心血辩护(Effort justification)。这种现象有一个更有趣的名字:宜家效应,我们内心会对自己动手完成组装的家具赋予更多的价值 – 除了家具本身的价值外,我们还会体会到某种成就感。同样,我们更难以割舍自己动手组装的家具,即使有一个更好的选择。...

June 28, 2019 · 1 min · 邱俊涛 | Juntao Qiu

修复缺陷的正确姿势

如何修复一个缺陷 如果给我一个小时来修复一个缺陷,我会花50分钟来写测试,用剩下的10分钟来改代码 – 本来是一句模仿爱因斯塔的名言,结果发现爱因斯坦并没有说过…… 你确定这是个缺陷吗? 下午2点,你喝下了一杯拿铁,它可以保证你在接下来的几个小时内保持清醒。突然,一位QA同事急匆匆的走了过来,从他的表情你就看出来事情不妙。果然,他告诉你SIT环境有个重大缺陷,如果不及时修复,好几个测试流程都不能进行。没错,你在还没有完全搞清楚发生了什么值钱,就莫名其妙的突然变成为了系统中一个“blocker”。 这位QA的神情和他对事情的描述让你不自觉的受到了一些感染,你也开始有点焦虑。你想要快速切换到IDE下,赶紧复现并修复他描述的问题。毕竟,你可不想成为一个阻塞别人工作的人。 我对你急切的心情表示理解,不过在你实际动手改代码之前,先冷静一下,稍微抑制一下赶紧修复问题的那种冲动。我们来看看在面对如此场景,如何表现的更为专业,以及更加卓有成效。 开始之前 事实上,在开始修复任何一个缺陷之前,你需要确认它确实是一个缺陷。这一点经常为很多新手忽略,从而导致修复缺陷从艺术变成了救火工作。 作为一名靠谱的开发,在真正动手修复之前,你可以做这样一些预先的check: 缺陷是不是发生在不受支持的浏览器上? 部署之后,有没有清理浏览器缓存? 下游系统是不是有计划内更新? 确定部署了最新版本吗?(部署之后,有没有机制可以确保SIT是最新版本) 或者你可以当QA宣称他找到了一个缺陷时,你可以反问:“你有没有试着重启浏览器/系统?”。很多情况下,在做了这些检查之后,你发现问题自己就解决了 – 此谓不战而屈人之兵也。 如果事情仅仅到这一步就结束的话,你就可以接着看看medium上的文章或者刷刷知乎什么的。不过有时候,事情就不会这么顺利了。 遗漏掉的需求 不论前期分析的多么完善,在实际项目的行进中,还是会遇到一些遗漏掉的需求点。比如招聘系统中的对留学生通道的考虑,银行系统中的海外信用卡的货币汇率转换等等。有时候,当听完QA的描述后,如果你和团队里的其他人都表示这个功能点是第一次听说,那么不用慌张,这很可能是一个被遗漏掉的需求。 这时候只需要冷静下来,将其记录下来,然后作为正常的Story流程进行即可(排列优先级,kick-off,被拖入in-doing等等) 还真是个缺陷 如果它竟然还不是一个漏掉的需求,承认自己写的代码有缺陷也不是什么丢人的事儿。而且即使是缺陷,也并不意味着需要立即修复。和所有的其他需求那样,缺陷也应该被分级,并当成一个正常的Story卡流入Backlog。 在实践中,我发现这一点非常关键。很多团队在开发过程进入修复缺陷阶段之后变得各种混乱,其源头也正是来源于此。一个非常糟糕的实践是:某个人负责将测试团队中发现的缺陷分发给指定的人,并一天两次的常规Check是否有所进展。这个貌似高频率反馈的过程可以毁掉团队的敏捷氛围:工作从拉动的方式变成了指派。 正确的做法是:为缺陷建立卡片,并和其他需求卡一起排列优先级,并通过拉动的方式流入开发流程,并像任何一张卡片那样进行kick-off,in-dev,sign-off等。换言之,不要特殊对待缺陷,把它当成普通的需求变更即可。 如何重现 一旦你确定了一个缺陷,并且需要修复它,那么第一件要做的事情自然是重现它。很多时候,重现并不那么容易。在实际项目中,应用可能有各种各样的外部依赖,比如: 依赖网络请求来获取数据 依赖特定的浏览器(可能是旧版本,也可能是特定浏览器) 依赖后端服务正常工作 所有的后端服务的异常都可能导致前端页面上的非预期行为:一个空白页面或者一个有着大红色叉的对话框,上书“找不着对象”等等。这些错误有时候可能会很难复现,或者至少需要一些特别的设置才可以使之发生。 网络异常 网络异常非常常见,而且可以导致各种各样的异常行为。开发中,localhost或者办公室的千兆宽带往往很难看到一些仅仅会在4G或者更慢速网络中会出现的问题。而当多个页面请求中的某一个失败时才会出现的缺陷则更难以复现。 不过,Chrome提供的DevTools可以在很大程度上帮助你(不过你可能需要每隔一个月重新学习下这些Tab的布局和新功能,Chrome的开发团队非常乐于偷偷给DevTools上线一些新功能,并完全破坏掉之前的布局)。 假设你需要调试/复现一个关于loading控件的问题,这个缺陷仅仅在网络极差的情况下才可以复现。你可以使用Chrome自带的throttling来模拟这种极端的网络场景。而有时候,你需要模拟网络完全不可用的状况,可以勾选Offline. 除了这些常规操作之外,你可以以定义黑名单的方式来屏蔽掉一些指定的URL: 这样,发往黑名单中的URL被屏蔽,这样可以很方便的模拟出改服务不可用的场景。有时候,如果你在开发海外的站点,也可以通过屏蔽特定的URL,比如facebook或者twitter链接来查看该站点在国内的表现。 消除重现缺陷的其他blocker 现实中,大部分实际应用都有多个页面。如果缺陷在第N个页面上,而你每次都需要从第一步导航到第N步。而如果每一步都有一些必填字段要填写,那么要重现这个缺陷,特别是debug的时候就会变成一场噩梦:你修改了一行代码,live-reload自动刷新并将你重定向到第一页,然后你完成所有填写并到达要调试的组件,然后发现本应该是字符串的地方显示的是[Object Object]。 不过,如果你恰好使用React+Redux组合的话,Redux Devtools Extension可以节省你很多时间。通过使用这个插件,你可以将应用的状态导入/导出。 Setup非常容易,只需要在创建redux store的时候加上一行代码即可: const store = createStore( reducer, /* preloadedState, */ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); 这样你可以使用该插件将应用在某一时刻的状态导出到本地文件。在导出的文件中,redux-dev-tools会将应用的初始状态和所有发生过的事件(以及事件相关的数据)记录下来,以便重新播放。 当然了,如果你并没有使用React+Redux的组合,很多表单的auto-filler也是可以完成类似动作的。你可能需要花费几分钟来学习如何定义selector以及如何用快捷键来自动填写,不过一旦学会,它就可以节省你很多时间。 寻找根因 根据我的经验,很多缺陷都并非发生在逻辑错误上(比如倒置的if-else,没有跳出的while等)。相反,很多时候错误会发生在错误的mapping,空值的保护不足等场景。 非法数据 实际应用中,每个集成点都潜在的会有这样的问题。比如UI到BFF,BFF到下游的系统等。代码中的很多配置,或者不一致的惯例也会导致类似的问题。...

June 18, 2019 · 2 min · 邱俊涛 | Juntao Qiu

一个输入框你要做一周?

How long does it take for adding a InputBox? When the Product Owner told you it’s a small change, don’t trust her. An estimation session After iteration planing meeting, after all the stories had been walked through, PO turned to you, pretending it was just a impromptu chat, asking how long does it take for adding a input box to one page. What he described was a “simple” input box for user to enter his/her address along with other personal information and persist to backend....

June 17, 2019 · 11 min · 邱俊涛 | Juntao Qiu

节奏大师:BA

节奏大师:BA 良好运作的团队都是相似的,而问题团队则各有各的问题 如果你走进任何一个流畅运转的敏捷团队,你事实上会发现人们做的事情都很简单,端到端的交付能力,清晰的验收条件,明确的优先级,充足但是不会让人焦虑的backlog,甚至还有友善的、喜欢开玩笑的团队成员。他们会从简单的事情入手,逐步的加强其功能,在过程中还会伴随着重构,甚至部分重写的发生,不过人们有充分的信心,代码的质量也由于一直在维护的大量测试保证。 如果你问这个敏捷团队里任何一个人这样一个问题:“你觉得敏捷的核心理念是什么?”,你会惊讶于答案的种类之多。有人认为各种工程实践比如结对编程,TDD,持续集成等至为关键;另一些人则认为迭代会议,故事墙,尽量频繁的showcase是重中之重;还有人会更抽象的将敏捷的核心描述为拥抱变化,响应变更等。这些回答当然都没有错,每个人在实施过程中,都自然会形成自己对敏捷实践的理解和看法。而在我眼中,敏捷的核心可以归纳成四个字:“渐进增强”。 渐进增强 这里的“渐进增强”可以理解为:先让一部分需求高优先级,而且想清楚了的需求先做起来,在做的过程中让团队建立起自己的节奏和工作文化,最后带动其他需求也被按部就班的完成,最终实现共同富裕……。而这篇文章要讨论的正是:如何让渐进增强在团队里变为可能?特别是在很多项目的各种范围都是固定的前提下。 要让这样的渐进增强变为可能,你事实上需要预先付出一些额外努力的,比如: 需求拆分 正确使用INVEST原则 过程可视化 文化建设 这些额外的努力事实上都需要团队里的BA作为主力来驱动的。就像田忌赛马一样,不同的拆分方法和需求的释放方式可能带来截然相反的结果。不过在进入如何实现的细节之前,我们先来看看节奏在任何一个团队中的重要作用。 反馈,节奏与心流 1975年,心理学家米哈里·齐克森米哈里(Mihaly Csikszentmihalyi)正式将心流概念化并通过科学的方式来研究。 心流(英语:Flow),也有别名以化境(Zone)表示,亦有人翻译为神驰状态,定义是一种将个人精神力完全投注在某种活动上的感觉;心流产生时同时会有高度的兴奋及充实感。 我在《反馈拯救世界》中讨论过反馈机制和心流(Flow)之间的关系。它是一种奇妙而值得追求的境界,或者心理状态,也是知识工作者所一直追求的状态。在这样顺畅的状态下工作,不但个人会获得空前的满足感,而且团队从客观上来看,会更加的高效,成员会更加的团结,不论你将什么样的需求交给他们,他们总是会顺利的将其完成。 要进入理想的,忘我的心流状态,齐克森米哈里提到至少需要满足这三点: 有清晰的目标 有明确且事实的反馈 能力和挑战的平衡(都处于比较高的状态) 既然节奏的建立如此关键,而BA又是建立这种机制的关键,那么如何在实际中实施呢?我们从一个典型的场景来入手 需求拆分 在我看来,通过传统的工具比如INVEST(+SMART)对需求进行合理的拆分,就已经可以在很大程度上确保在团队正确的道路上行进了。当然,前提是你选对了正确的工具,并且完成了正确的拆分。这里面其实包含了两个问题: 怎样就算把INVEST做对了? 按照教科书般的INVEST划分之后不工作怎么办? 对于INVEST的正确使用,包括诸如用户故事多大比较合适,常见的拆分模式如工作流,业务规则变种等相关的文章,已经可以算是前人之述备矣,此处不再赘述。在这里我只关注第二个问题:即,在你已经可以熟练运用INVEST拆分比较复杂需求,并且在大部分场景下都可以采用合适的模式,但是这种拆分和开发团队的能力结构又不很契合的场景。 前后端分离 有时候你会发现,你从书上读来的敏捷实践在你的团队里不工作。比如需求要纵向的、端到端的划分。然而实际运作中,在实际进入开发阶段之前,很可能最终的视觉设计已经基本定稿,甚至一些典型页面已经向客户(如果是产品的话,此处替换为产品经理)汇报过了。而且很多时候客户只关注最后结果,过程中的半成品性质的汇报往往只是走过场,最终的验收客户往往会产生新的想法,但是为时已晚。 在这种范围相对固定的场景下,似乎用前后端分离的拆分方式似乎可以更快完成任务,也更合乎直觉,毕竟有专门的UI Dev可以很轻松的将高保真转换为HTML/CSS/JS。而那些关注性能,关注高并发/高可靠的后端开发者似乎也没有必要参与其中。事实上,我见过的很多项目正是这样运作的,而且看起来这种分法在工作内容相对固定的项目中也是可以工作的。 当然,这种划分存在一些无法回避的问题: 前端为了不被阻塞,会开发一套mock 后端需要一个机制来确保实现之后通知前端以保持一致 需要额外的测试来保证集成的正确性 集成会被延后 需求无法端到端交付(必须至少等到最晚的一端完全实现) 如果操作不当,很容易出现前端做完在等后端,或者后端等前端的现象。由于变更的不透明性,又很容易产生相互指责,内部消耗。即使我们有着精湛的工程技巧,比如通过mock后端,契约测试等手段可以使得过程不至于太痛苦,但是在开发过程中,由于迟迟不能集成并做实际演示对于客户和开发团队来说,都会显得难以放心。 端到端交付 另一方面,如果你考虑实施端到端的拆分和交付方式,完整的交付一个功能点。不过这种方式的一个重要的blocker是团队成员能力的不均衡分配(也可能是团队成员的兴趣所在),而且这一矛盾随着前后端的不断精细化变得更加明显。在端到端的划分中,我们往往需要开发同时具备前后端开发的能力,退一步讲也应该具有与前/后端同事结对完成特性的能力。 此外,这种划分方式则需要面对另外一些问题: 各有专长的前后端开发如何合作 单个用户故事交付时间可能过长 开发人员能力磨合/提升需要时间 乍看起来,这两种做法看似互相矛盾,无法调和。甚至在很多情况下,如果从客观的结果上来看,两种做法可能产生的物理结果是一样的:都按时的,按质量的完成了需求。不过,我觉得前后端分离的拆分方法中忽略了一个重要的点:开发体验。就我自己而言,我痛恨那种上不着天,下不着地的开发体验:你负责的永远是系统的中间一环,你有依赖的上游,也有依赖你的下游,每个功能都永远无法真正知道有没有人用,会不会给人们带来价值。因此在项目中,我尽量会尝试端到端的贯穿一个需求,最好可以从界面到数据库表。 我觉得即使在极端的场景下,也应该采用端到端拆分和交付的方式来工作。首先,团队不再以技术为分界线来看待用户故事,而是以功能(或者说业务价值)来划分。毕竟,不管是Fixbid还是人天的项目,客户都是以功能收费的嘛。一个功能可能在实施的时候需要不同的技术细节来支撑,但是功能本身应该被作为原子级别的需求,而不是物理上前后端的割裂。其次,如果从项目交付的另一个成果:人员的能力提升来看,端到端交付方式的问题就反而会变成优势。在一个项目结束之后,前端掌握了一些后端语言/工具的使用,甚至Cloud的维护;而后端则从前端了解到JavaScript中的测试框架,组件化等知识。 此外,你事实上只需要做很小的调整,就可以让团队获得很好的开发体验。即使项目范围在一开始就基本固定,即使关键页面的设计稿都已经经过汇报。仅仅做好符合INVEST原则的将需求拆分就可以很大程度上帮助团队形成高效的,顺畅而稳定的交付节奏。 INVEST原则 INVEST是渐进增强的基础,没有合适粒度、相互独立的用户故事划分,要进行迭代式的渐进增强就成了空中楼阁。事实上,这个基本功需要在很多层次去刻意练习。INVEST事实上在敏捷实践中影响深远:过小的划分会引入更高的管理成本,而过大的划分则可能导致优先级难以界定,而且很容易影响士气:没有人愿意看到在墙上挂一周的卡。 和现实世界中的很多事情一样,对于一名BA来说,划分出合理大小的用户故事需要很多方面的平衡,有时候简直是一门艺术。我们可以通过一个简单的例子来稍作解释 个人主页 比如,我们要实现Jigsaw的个人主页,页面有很多个部分组成:导航,提示信息,项目历史清单,用户Profile入口等等: 假设我们的第一个用户故事:显示提示信息Heading(如下图红框圈定部分所示)。 这个用户故事需要从后台读取数据,并展现在客户端。数据可以从后端数据库中读取,也可以从后台文件中读取,这个取决于后台数据存储的技术有没有选定。作为第一个用户故事,它可能还会关联一个技术卡:搭建前后端的基础代码,比如用create-react-app创建一个前端工程,用gradle创建一个后端工程之类。要不要独立成一个单独的卡可能取决于团队的能力结构。 作为一个有业务价值的用户故事,这个需求需要满足: 端到端实现(即,连通从页面到数据库) UI和Mockup基本一致 另一个好处是它要求开发者在做的过程中建立基本的反馈机制,比如TDD、结对编程/Code Review Session等细节,并体验Kick Off,Desk Check等实践在团队里是如何工作的等等。换句话说,就是尝试从一个简单、实际的需求入手,建立一个可以运作的反馈机制。一旦熟悉了如何做一个用户故事,那么进一步稍微复杂的用户故事(还记得吗?控制节奏)就会相对顺畅很多。毕竟一回生二回熟嘛。...

December 19, 2018 · 1 min · 邱俊涛 | Juntao Qiu