@liuhui0803
2017-10-19T10:49:41.000000Z
字数 8376
阅读 2159
文化和方法
来源:First Round Review,翻译:大愚若智
Andrea Goulet和商业伙伴一起坐在她自家客厅里,正在一起随意地修订着战略规划,这时电视上开始播放起新一集的This Old House(译注:一部有关老旧住宅房屋改建的电视节目)。往往在这时候,不同想法会相互碰撞并产生一些新的念头。他们一直在设法与别人交流自己的价值主张:帮助其他公司清理遗留代码和技术债。就在此刻,他们对视一眼,觉得为自己的主张找到了一个完美的类比。
“我们意识到自己的工作远非清理老旧代码那么简单,实际上我们就像是在改建房屋那样重构软件,借此延长软件寿命,改善运行效果,提供更多功能,”Goulet说,“这不禁使我思考企业具体该如何通过修补代码来提高生产力。就像你可能会给房屋铺设新的屋顶,借此让房屋升值,虽然听起来不怎么酷,但这样的工作必不可少,而很多人的做法完全是错的。”
目前,她是Corgibytes的CEO,这家咨询公司主要承担应用的重构和现代化等项目。她曾经见识过各种各样破旧的系统、遗留的代码以及繁重的技术债,感觉人们都在以数字化的方式囤积各种“古董”。因此Goulet认为初创公司需要转变自己的心态,不要斥资积累技术债,而是要创造技术财;不应继续用老旧的代码将就,而应特意进行重构。她将通过本文介绍自己提出的新方法,以及将不可能变为可能的做法,当然这一切都离不开高水平的工程师。
有关遗留代码,最主流的定义来自Working Effectively with Legacy Code一书的作者Michael Feathers:遗留代码就是无测试覆盖的代码。这样的定义总好过大部分人想当然的认为:真正古老陈旧的系统。但在Goulet看来这两种定义都还不够。“遗留代码与软件的年龄无关,就算两年前开发的应用也有可能已经处于遗留状态,”她说,“重点在于软件进一步完善的难度。”
这意味着代码的编写可能不够清晰,缺乏解释,完全不包括有关你的想法和决策过程的实际产物(Artifact)。单元测试就是一种这样的产物,描述基本原理的文档和创建代码的理由也是这样的产物。如果当你想要完善代码的时候完全无法了解开发者的想法,那么你就遇到了遗留代码。
遗留代码并非技术问题,而是沟通问题。
如果你和Goulet一样沉浸在遗留代码的世界中,你可能也会观察到一种相当具体并且隐晦的康威定律(译注:详见:http://www.infoq.com/cn/articles/every-architect-should-study-conway-law),每次交流中都会面临类似的情况。
“这个定律认为,你的代码库实际上会严格反映组织内部的交流结构,”Goulet说,“如果要修复遗留代码,那么首先必须解决运维方面的问题。很多人会忽略这一点。”
Goulet和她的团队像考古学家那样对一个遗留项目进行了细致的拆分,他们希望找到上文提到的那种“产物”,借此了解当时的开发者到底是怎么想的。所有这些产物结合在一起为新决策提供了必要的情境。
最重要的产物是什么?组织有序、意图明确、简洁的代码。举例来说,如果为变量使用诸如“foo”或“bar”这样普通的名称,六个月后再看这些代码你自己也会一头雾水。
如果代码可读性不高,此时最有用的产物就是源代码控制系统,因为这种系统可以保存代码的所有改动历史,让开发者更方便地更改自己的代码。
“我的一个朋友说,对于提交时的备注,摘要应精炼如推特,描述应详尽如博客,”Goulet说,“你本来可以借此机会将自己的想法与代码的改动紧密结合在一起,这个过程花不了太多时间,但可以为项目的后来者提供大量宝贵信息,然而很少有人这样做。我们经常听到开发者抱怨自己遇到的代码‘一塌糊涂’,想要看看这些代码到底是谁写的,最后发现竟然是他们自己。”
自动化测试还可以帮助我们进一步形成必要的基本原则。“很多人如此喜欢Michael Feathers对遗留代码的定义其实是有原因的,”Goulet解释说,“测试套件,尤其是配合行为驱动的开发实践一起使用,例如编写场景时,可以帮助我们很好地理解开发者的意图。”
我们得到的经验很简单:如果希望尽可能避免遗留代码,就需要密切注意细节,让代码在以后能更易懂,更易于操作。一定要编写并运行单元、接受度、批准以及集成测试。一定要解释你的批注,让以后的自己(以及别人)更容易理解你当时的想法。
话虽如此,我们总是会遇到遗留代码。原因有很多,有明显的,也有出乎意料的。
早期的初创公司通常会面临巨大的压力,必须让功能尽快发布。开发者在交付方面会遇到巨大的压力,这时测试工作往往会扔在一旁。Corgibytes的团队就曾遇到过很多公司在多年的成长过程中根本不考虑测试问题。
没错,当你只是在发布原型产品时,确实不必非要进行测试。可一旦你已经有了产品和用户,就需要在维护和不断的完善方面进行必要的投入了。“很多人说‘维护工作不用担心,功能最关键!’,”Goulet说,“如果你也这样做,那么很快就会在扩展的道路上遇到阻碍,导致无法与对手竞争。”
原来热力学第二定律同样也适用于代码:熵总是飞速增加的。我们需要不断与技术债造成的混乱斗争,而遗留代码也是随着时间流逝会遇到的一种债。
“这里也可以列举有关房屋的比喻。你必须不断地将餐具、吸尘器之类的东西从垃圾中挑出来妥善保管,”她说,“如果不这样做,问题会变得越来越棘手,最终只能求助于危险品处理专家。”
Corgibytes接到过很多来自CEO的电话,例如有人说,“三年前,新功能上线需要花费两周时间,现在竟然需要12周,我的开发者效率太低了。”
技术债始终可以反映出运维中存在的问题。
很多CTO都遇到过这样的问题,但很难说服他们的同事相信花钱修复已经存在的问题是值得的。这有点像回溯,不会有什么激动人心的,或者新的产出。很多公司始终不愿意解决技术债问题,直到问题已经严重到开始影响日常工作,而这时候要解决起来成本就更高了。
如果将技术债重组成积累技术财富的机会,可能很难得到CEO、投资人以及其他利益相关者的认可。技术债重组这个术语正是敏捷开发教练Declan Whelan新近创造的。
“我们不能继续将‘债’看作不好的东西。当你处于产品设计和构建的早期阶段时,技术债其实非常有用,”Goulet说,“当你解决了一些债务问题后,自己也会更有干劲。例如当你给房间新安装一扇窗户时,当然需要花点钱,但每个月也许能节约数百元电费。代码也是如此。但你获得的不只是效率,随着时间流逝,生产力也会大幅提升。”
只要你发现自己的团队生产力降低,那么就有必要看看是否收到什么技术债的影响。
“我和大量求贤若渴的初创公司交流过,为了完成更多工作,他们雇佣了大量高薪工程师。”她说,“其实他们更应该研究一下怎样让现有工程师效率更高,解决哪些债务可以进一步提高生产力。”
如果转变角度并专注于创造财富,生产力将大有盈余,随后即可将其重新投入更多债务和遗留代码的解决过程中,进而形成良性循环。你的产品也将顺利启航,越走越远。
别再将软件看作项目了,应该将其看作一栋需要在里面住很久的房屋。
“这是一种重要的心态转变”,Goulet说,“可以帮你摆脱短期思维,比以往更关注维护。”
与房屋类似,软件的现代化和维护也可以分为两个方向:微小、表面的改动(“我新买了一块地毯!”),以及大型、成本高昂,并且最终将获得回报的投资(“我觉得水管需要更换了……”)。必须两手抓才能让产品始终维持最新状态,并确保团队顺畅运转。
这些都需要提前做好预算,如果不这么做,最终只能花更多钱。拥有一栋房屋,就免不了产生维护成本,但令人震惊的是很多公司不认为开展业务会产生维护成本。
因此Goulet提出了“软件重塑”这个术语。当你房屋里有东西故障后,你不会推到房屋从头开始新建。同理,如果遇到老旧破碎的代码,完全重写通常并不是最佳应对措施。
在需要“重塑”代码库时,Corgibytes通常会这样做:
重塑过程也会涉及DevOps领域。例如Corgibytes通常会建议新客户使用Docker,这样可以更快速简单地搭建新的开发环境。如果团队中有30名工程师,将初始配置时间由10小时缩短至10分钟就可以将更多时间用在重要的任务中。这类措施并非仅限软件本身,同时也会影响到软件的构建方式。
如果你知道哪些具体措施可以让代码更易于处理并提高效率,那么就应该将它们包含在每年或每季度更新一次的路线图中。不能指望这些措施能自发实现,但也不要为了能立刻全部实现而给自己太大压力。Goulet就发现很多初创公司会因为一开始就强调100%测试覆盖而导致工作进度大受影响。
具体来说,每个公司都应该考虑下列三类重塑工作:
接下来分别看看这些工作。
“我们的一位客户正在进行B轮融资,他们说人才招募速度不够快。我们帮他们实施了一套自动化测试框架,3个月里团队生产力实现了翻倍,”Goulet说,“现在他们可以直接去跟投资人说,‘打造精益的团队,效果远远好过雇佣更多人员’。”
自动化测试实际上是大量单一测试的集合。你会通过单元测试复查每一行代码,你会通过集成测试确保系统不同组件能够良好配合,你还会通过接受度测试确保功能可以按照预期工作。当你通过自动化脚本的方式编写这些测试后,就可以一键点击让系统自行进行测试,而不需要通过手工操作完成所有这些任务。
产品上市前就这样做似乎有些为时尚早,但只要已经具备足够让你满意的产品,并且已经有了自己的用户,这样的做法就非常必要了。
持续交付是指将与交付有关,原本需要手工执行的任务自动实现。这么做的目标在于在小型改动完工后尽可能快速部署,并建立尽可能短的反馈环。这种做法可以为公司提供巨大的竞争优势,尤其是在客户服务方面。
“假设每次部署时都是一个巨大的烂摊子,整个系统的熵完全失控了,”Goulet说,“我们曾遇到过需要花12小时以上时间的部署,因为环境实在是太乱了。遇到这种情况后,就无法足够频繁地部署了。新功能的发布也必须延期,因为整个过程实在是太痛苦。很快你就会落后,丧失竞争优势。”
其他可在持续改进过程中自动实现的常见任务还包括:
举个简单的例子,假设客户发送了一份Bug报告。随后,开发者修复Bug发布新版的效率当然是越高越好。但修复Bug时面临的挑战不在于改动代码的操作具体有多难,而在于系统配置有问题,开发者在解决问题之外,自己不擅长的工作中浪费了大量时间。
通过持续改进,我们就可以果断判断哪些任务是计算机最擅长的,哪些是人类最擅长的。对于计算机更擅长的任务,就可以自动实现。这样开发者就有精力处理那些更具挑战性的问题,而客户也能变的更开心,因为他们遇到的问题可以快速解决修复。积压的问题越来越少,你也可以将更多时间用于新的创新活动,进一步完善自己的应用。正是这样的变化确保了你可以通过技术创造财富。由于开发者只需要一步操作就能在修复Bug的同时发布新代码,因此也就有足够的时间和精力可以更频繁地发布。
“你应该不断问自己:‘如何为用户进一步改进?如何能做得更好?如何能变得更高效?’但不能止步于这些问题本身,”Goulet说,“只要这些问题有了答案,随后就需要问问自己如何能以自动化方式实现这样地改进或效率。”
Corgibytes每天都会遇到同一个问题:初创公司所搭建的环境使得开发者无法发挥自己的效率。CEO看着开发者,很好奇为什么不能提高发布频率。真相在于,他们被公司的文化拖了后腿。为了予力赋能工程师,必须以整体的方式看待他们的工作环境。
这方面Goulet引用了Robert Henri的一段话:
目标不在于创造艺术,而在于打造一种可以自发产生艺术的神奇状态。
“每个人都应该开始这样思考自己的软件,”她说,“你也可以塑造这样的文化。你的目标始终应该是打造可以促进艺术产生的土壤,这里说的艺术就是指简洁的代码,出色的客户服务,开心的开发者,良好的产品市场匹配,盈利能力等,这一切都是相互关联的。”
这种文化需要对技术债和遗留代码问题划分优先级,只有这样才能真正为开发者的高效工作奠定基础,而我们也只有借此才能在未来做出更醒目的成绩。如果不改变开发产品的环境,就无法重塑自己的产品。为此,首先就需要改变有关维护和现代化等工作的整体态度,最好能从CEO的层面自上而下做出这样的改变。
为了打造流动状态的文化,Goulet给出了下面这些建议:
如果高管和投资人面对这样的升级有所退缩,那么可以用客户服务的方式来“包装”一下,Goulet说,“可以告诉他们,通过这些改动塑造的最终产品如何为公司最重视的用户提供更出色的体验,这是你能提出的最有说服力的论点。”
整个业界会假定糟糕的工程师不愿意处理遗留代码,他们只希望构建全新的功能。很多人会说,把这些人安置在负责维护的部门纯属浪费。
这些其实都是误解。如果掌握正确的方式方法,你会看到大量技能娴熟的工程师都在忙于解决各种棘手的技术债,那么当你发现之后,如何让他们的工作更顺心呢?
“当我们在会议中演讲时,会向听众询问‘谁喜欢处理遗留代码?’,几乎每一次,只有不到10%的听众会举手,”Goulet说,“但当我和这些人交流时发现,这些都是最喜欢应对挑战的工程师。”
她接待过带着自行开发的数据库上门求助的客户,没有任何文档,完全无法解析出数据库的结构。而这就是她所谓的“修缮者”类型工程师的典型。现在,Corgibytes公司有一个完全由这类工程师组成的团队,这些工程师最喜欢的就是深入研究理解二进制文件。
那么该如何找到这样的精锐部队?Goulet试过多种方法,其中几种比较有效。
她建立了一个社区网站legacycode.rocks,主要用来宣传这样的观点:“长久以来,我们这些热衷于重构遗留代码的人都被视作二等开发者…… 如果你以此为豪,欢迎加入!”
“我开始收到很多人发来邮件说,‘哦天哪,我也是!’,”她说,“通过这些措施和重视重构工作的观点帮助我们吸引了很多适合的人员。”
她还会在招募工作中通过持续交付实践为此类开发者提供了需要的东西:大量细节和明确的指令。“一开始是因为我本人讨厌重复性的工作,如果我收到询问同一个问题的多封邮件,我就会把内容发布到网站上,如果要写文档我也会这样做。”
随着时间流逝,她注意到可以进一步优化申请过程,借此在招募工作的早期阶段找出最适合的求职者。例如,她的申请说明里提到:“CEO将审查您的简历,因此请务必将申请邮件抄送给CEO”,但并未明示CEO的性别。所有以“尊敬的先生”或“先生”开头的邮件都会被直接删掉。而这才只是她招募工作的开始。
“这么做是因为,我实在厌倦了无数次别人认为,作为软件公司的CEO,我肯定是男士,”Goulet说,“于是,有一天我突然在想,我应该把这种情况作为说明放在网站上,看看谁会注意到这个细节。让我吃惊的是,这样做不仅筛选掉了大量不严谨的求职者,同时还帮助我们更好地发现很多具备必要技能,适合处理遗留代码的人员。”
Goulet回忆到有位求职者在发给她的邮件中说,“我仔细看了你们网站上的代码(我喜欢这个网站,毕竟这就是我的工作),发现了一段很奇怪的内容,看起来是使用PHP写的,但似乎你们用了Jekyll,这是用Ruby写的。我很奇怪为什么会这样。”
最后发现原因在于,设计师提供给Goulet的HTML、CSS和JavaScript中有一个遗忘的PHP类名称,她本来打算解决掉的,但一直没有时间。于是她回复邮件说,“你在找工作吗?”
另一位求职者注意到她在说明中使用了CTO这个词,但她的团队中并没有这个职位(她的业务合作伙伴是Chief Code Whisperer)。再一次,对细节的重视、好奇心以及主动性,成功吸引了她的目光。
修缮者并非仅仅是细节为导向的,他们只是不由自主会注意到细节。
令人吃惊的是,Goulet也并非一直用这种“折磨人”的招募挑战大部分技术公司。“大部分人会直接通过我们的网站申请工作,但在希望扩大范围时,我们会使用PowerToFly和WeWorkRemotely。目前我确实不需要招募新人,他们有足够的时间去理解到底是什么可以让这些修缮者们脱颖而出。”
如果求职者通过了第一轮筛选,Goulet会让对方阅读Arlo Belshee撰写的这篇名为“Naming is a Process”的文章。文中详细探讨了处理技术债中遗留代码过程中需要注意的所有细节。她的要求就一句话:“阅读本文并谈谈你的想法。”
她希望能理解求职者的回复中隐藏的潜台词,以及愿意接受各种观点的人。将思想比较深入的求职者与经过判断认为只是想要找份工作的人区分开来,这种做法为她们提供了巨大的帮助。对于类似的招聘工作,她强烈推荐选择对业务运营最重要的信息,并且这些信息必须能展示出有激情、有方向,善于分析的人到底是怎样的。
最后,她还通过Exercism.io在现有团队成员和求职者之间建立了一个结对编程项目。Exercism.io是一种开源项目,可以让开发者学习如何用不同语言编程,并提供了一系列测试驱动的开发练习。结对编程活动的前半部分会让求职者选择要使用的语言,在随后的练习中,则由主持面试的人选择语言。他们会借此了解求职者面对“惊喜”会如何处理,灵活性如何,以及求职者是否愿意承认自己也有不懂的地方。
“当一个人由从业者真正变成一个大师之后,往往会坦率承认自己不懂的东西,”Goulet说。
让人用自己不熟悉的语言编程还有助于了解对方是否能“坚持不懈”。“我们希望能有人说,‘我打算和这个问题死磕,直到顺利解决。’也许第二天他们就会来找我然后说,‘终于搞定了。’这样的行为才能说明对方能否成为一个成功的修缮者。”
我们这个行业往往最看重创造者,以至于每个人都希望他们还能同时承担维护工作。这种想法是错的。最棒的修缮者永远无法成为最棒的创造者。
成功招揽了人才后,Goulet也了解该如何帮助他们获得成功。确保这类开发者开心、高效地工作,你可以这样做:
通过这些方法,目前已经有超过20位符合要求,对遗留代码富有激情的开发者正在等待Corgibytes的工作岗位。
大部分初创公司并不会考虑增长期结束后该怎么办,一些公司甚至觉得增长绝对不会停止。其实未必,哪怕你已经步入了下一个阶段:稳定。稳定,仅仅意味着你已经具备了创造技术财富所需的人员和流程,并正在以合适的优先级使用着它们。
“增长和稳定之间存在一个拐点,此时修缮者的作用将会激增,并且你会开始在待处理遗留代码的修缮者以及专注于新功能的创造者之间做好平衡,”Goulet说,“你有自己的系统,现在需要他们做好自己的工作了。”
这意味着要将更多预算分配给维护和现代化工作。“绝对不能将维护工作仅仅视作另一个单独的项目,”她说,“而是应该固化在公司的文化中,这一点对未来更辉煌的成功是至关重要的。”
最终,通过这些措施创造的技术财富将为团队塑造一种全新的开发者:新人有足够的时间和资源来探索新的领域、客户群体以及各种机会。当你有足够的资源帮助自己步入新的市场,并且更擅长自己现有的工作时,业务才能真正兴旺起来。
阅读英文原文:Forget Technical Debt — Here's How to Build Technical Wealth