本文作者:李光毅
本文来源于我去年翻译的图书《编程原则》(Understanding Software)中的译者序,我真诚推荐你阅读完这篇文章甚至是该书。我推荐的理由和我翻译的动机相同,因为它恰到好处地提炼出了我长期工作中积累下来赖以受用的经验,其中不乏有邪恶和残酷的一面。虽然书是以作者自己的经历出发,但也让我感同身受。相信它一定会对你有所启发。
它是一本关于什么的书?
在还没开始翻译之前,我对于这篇译者序就已经有了规划,很简单:先聊聊为什么翻译这本书,再大致把书里内容叙述一遍,最后重点推荐一些有价值的章节。但是在后续润色译稿的过程中(其实也相当于重读了),发现当初的设想是不可能办到的,因为整书涉及面太广了:调试代码、测试策略、团队协作、效能提升甚至还包括待人接物,可以说无所不谈。
此时你肯定也疑惑了,所以整本书究竟想说什么?难道没有一个统一的主题成为它们装订成册的理由吗?
有,整本书用两个字总结就够了——原则。
整本书涵盖的是所有你在开发中可能会运用到的各式各样的原则。在本书的前言和第一篇中作者就开宗明义指出,本书的目的是帮助你成为一名更好的开发者。希望书中这些作者在他过往工作中总结下来的经验,能够让你在成长的路上少走弯路。
多谈原则
关于如何对待编程领域内的知识,两种极端态度我都见过:有人只看结果,他们只关心“写代码”而对“写好代码”一无所知;第二类人深谙各种架构设计、整洁代码之道,但对于当下你代码中遭遇的实际问题,他们无法把方法论落地为解决方案。
在互联网公司的多年工作经验让我个人更习惯于从第一种人的视角看待问题,毕竟这是行业性质决定的,跑马圈地、快速扩张才重要,竞争不允许你有时间思考。如果能把行业和公司因素排除在外再看编码这件事,我作为程序员最大的疑惑是,为什么每一家公司内我接手的代码库都如此难以维护;为什么总有人写出500行代码的函数和1000行代码的组件;为什么每一个迭代的最后总是要加班加点,研发、测试、产品经理都叫苦不迭;为什么问题年复一年的在发生,却没有人想做些什么来改变现状。
DRY——Don't repeat yourself. 别忘了这可是我们自己说的。
大部分程序员热衷学习新技术和新框架,对阅读源码有发自内心的崇拜,这无可厚非。新技术有机会让工作事半功倍,功利点说也能给我们简历增添浓墨重彩的一笔。但技术背后的编写思路和演化至今的原因同样值得了解,它们和技术的语法本身同样重要。你仔细回想就不难发现,工具的好坏和你代码的好坏没有关系,从裸写 JavaScript 的年代,到 BackboneJS*,再到* React,你看到团队中能把代码写好的人真的是越来越多的吗?
不同行业对于软件质量的要求是不同的。且不说你所在的行业有没有意愿和资源解决这些问题,如果有,你应该去哪里寻找方案?
在我看来,前人留下的经验是最值得我们借鉴的宝贵财富,无论这种经验是来自传统软件行业还是互联网公司。我们当下遇到的多数问题,对于传统软件行业而言他们在十年前甚至二十年前就已经遇到了, 我不敢说在解决这些问题上它们走的有多远,但他们迈出的每一步都对后人产生了深远的影响。然而这些经验也并没有什么神秘的,其中有一部分经典就是你已经耳濡目染的各类编程法则和开发模式,而另一类更实际的内容就散落不同渠道的文字和口述当中,例如本书。
但让人望而却步的是,大部分原则听起来都过于抽象了,甚至是反直觉的。
我明白抽象带给人的挫败感,你肯定听说过不少抽象概念,甚至它们的名字可以信手拈来,什么可维护性、可扩展性、可读性、KISS、YANGNI等等,然而应该用什么标准衡量可读性的好坏?KISS应该如何在代码中实施?
反直觉的实践也比比皆是,如果我告诉你,假设你能在每一次正式开发代码前提前对代码做一些重构工作,那么无论是短期还是长期来看,你的整体付出的开发时间是会下降而不是上升的。你愿意相信吗?你敢于在工作中尝试吗?
遗憾的是有一些原则背后确实存在复杂的知识体系作为支撑,哪怕我用思维导图把背后涉猎的概念一五一十的列举出来,你的内心可能依然毫无波澜。因为其中的很多原则需要在你经历过相似代码的前提下才能心领神会,轮扁斫轮寓意也是在此。
而有的则通俗易懂,Uncle Bob Martin 在他的「The principles of OOD」一系列文章中,谈到过 糟糕设计(Bad Design)的几个特征:
僵化(Rigidity):代码难以修改,因为改动会影响到的地方太多
脆弱(Fragility):当你做出修改时,系统中预期之外的地方会遭到破坏
难以修改(Immobility):代码很难被复用,因为它与当前系统中的功能耦合在了一起
这一系列简单扼要的描述,就将编程中涉及到的原则,和代码中具体的症状联系到了一起。
学习这些知识难吗?想要了解它们很简单,但想要在编程中灵活运用它们则是另外一回事,毕竟提升编程技能靠的不是死记硬背,而是反复刻意的练习。可再困难,也会比将来回过头设法挽回代码造成的损失要简单。
如果他们错了怎么办?
我无法否认这种可能性。但大部分时候——我说的是大部分时候,技术的决策是专制的。如果我在这个技术领域有丰富的经验,如果我解决过足够多的问题,哪怕我只是在这个项目中待的足够久,那么对于当下任何一个新的问题,我给出的解决方案一定会更完善。
很多时候这些原则不一定是错的,而是反直觉到让你听上去以为它是错的。拿注释来说,很多程序员会认为注释是消除代码“恶臭”的灵丹妙药,但是
Martin Fowler 在《重构》里告诉你没事别写注释
Uncle Bob Martin 在《代码整洁之道》里告诉你没事别写注释
Jeff Atwood 在 codinghorror 技术博客里告诉你没事别写注释
那还有什么理由要继续写注释?
现在依然半信半疑的你有几个选择:
首先你可以去了解这些建议背后的动机。这些建议的背后都有支撑它们的理由。"start with why" 有助于你理解它们,神奇的让你从对立面转向同一阵线。
但有一些知识可能是无法追溯的口口相传,或者只是团队中留下的实践。面对这种情况你需要的是“信仰之跃(Take a leap of faith)”。也就是说你此时你需要无条件的相信并遵循,然后日后再慢慢验证。
或者你还有一种选择,那就是置若罔闻,但可能需要承担后患无穷的代价。
如果你依然对书中的原则将信将疑的话,我不得不提我翻译这本书的另一个原因:书中很多内容与我在实际工作中总结出的经验不谋而合。
举一个例子。我个人在从独立的开发者视角,转向关注团队、项目、流程的视角的过程中,发现技术问题不再成为我眼中首当其冲需要解决的难题。
因为哪怕你找到了整治项目的灵丹妙药(某种最佳实践),也需要整个团队的力量来帮助你落地且一如既往的保持下去。项目里不需要英雄,即使团队中真的存在能写一手好代码的高手,他的幸苦结晶也很快就会被庸才们“孜孜不倦”的“劳动成果”所打败。
迫切的希望团队中有“救世主”角色的出现是一个危险的信号,而且通常这个时候救世主也派不上什么用场。接着灵丹妙药一说,你有没有考虑过团队里的每个成员能否“咽的下”这颗灵丹妙药?仅仅只有几个人能够理解这套方案并且在项目里实施起来是不够的,所以要怎么打造这颗灵丹妙药?底线在哪?说白了,底线就是团队能力的下线。
那么如何提升团队效能,如何帮助团队中的成员成长进而拉高团队的底线,都是会在本书中谈及的问题。
现实一些
还记得我在开头提及的第二种人吗?他们同样是危险的。如果抛开实现,单纯把问题抽象到某种高度,可能会让问题陷入到一种什么都解决得了和什么都解决不了的极端局面中。前者相当于“不就是”,后者等同于“又怎样”。万事万物都可以套用“不就是”与“又怎样”的句式,这样的描述看似所向披靡,仔细想想却破绽百出。
“不就是微前端嘛”——不好意思你说的是哪一种微前端?不同框架下组件间的通信问题是如何解决的?构建时集成流水线的粒度如何?组件间互相依赖的版本管理策略如何?
“又怎样”的心态更是可恶,当代码稍有改善时,就会有悲观主义者“友善”的“提醒”你,这种杯水车薪的改善又能怎样呢?你不是第一个人,但现在整个代码库依然身陷囹圄。
这种思考问题的方式一点都说不通,为什么代码腐化是一个持续的过程,但为什么我们却想要在某个时间段内一劳永逸把问题解决?
代码库满目疮痍是常态,问题在于你要如何挽救它,从哪里开始挽救它。他们没有错,无论我们是引入 committer 机制还是代码评审会议,总有一天会无法坚持下去,最坏的情况无非是我们没有让它变得更好,但也保证了在尝试的过程中它不会变得更差。
上一小节中开发人员的能力困境“不就是”温伯格的咨询第二定律:不管一开始看起来什么样,它永远是人的问题。
它够不够抽象?够不够深刻?够不够有哲理?够。但说实话对于解决我们当下的问题并没有帮助。如果瓶颈真的在于团队的成员上,我们想知道的是,我怎么样才能提升团队成员的编程能力?再实际一些,作为一个创业公司,我无法提供非常有竞争力的薪水来招到顶级的人才;或者迫于交付压力我无法花太多的时间在培训、代码审查、重构上,那么我的代码应该如何被拯救?
在我看来本书的魅力正是摆脱了高高在上的姿态且在“说人话”,作者并非只是凭空扔给你一组金句之后让你慢慢参悟。对于一些概念,甚至是常见的概念,作者会进行澄清和定义。如果他提出的是一条建议,那么他还会解释这条建议的来龙去脉和它所适用的范围。对于在实施过程中可能遭遇的阻碍,他也做出了适当的预测和解决办法。
我不敢苟同他在书中提出的每一个观点,最终是否适合与你还需要你自行判断,毕竟在这个领域内没有银弹。但相信这些内容能够给你带来启发。自经典,才有可能超越经典。
免责声明:本文内容仅表明作者本人观点,并不代表Thoughtworks的立场