Liangshan

Inner peace.

我的工程师公理(译)

上次提到过,我不喜欢写一些时效性很强的内容(强调我不是没时间写博客)。一年多过去了,终于又看到一篇值得翻译的文章,网上能找到另外一篇现成的中文翻译,是一个简化的版本。我还是决定完整的翻译一下,不过每一条标题我认为都应该简短精炼,所以有些没有完全按照原文翻译。

这篇文章涵盖了编程的经验、对编程这份工作的理解、什么是职业态度等方面。其中的一些观点我深表赞同,有些观点超越了我认知的层级,将我带到更高的视角中。所以我个人非常推荐这篇文章。


公理(Axiom) 是一个奇特的词,一层层挖掘词源最终找到了一个古希腊的词根 ἀξίωμα,称之为“被认为合适或值得的”。我喜欢这个概念,并且我认为这个列表中的每一项都是值得思考。

当然,它们仅仅是我个人的公理,基于我自己的经验而来。大家的经验或许和我不同。

无论如何,我很乐于分享这个列表,并进行一些简要的说明。其中一些见解可能会相当常见,但我也希望大家能产生不同的见解或发表有趣的不同看法。

第一条 变化是永恒的

这条应该不会有什么争议。几乎任何事情都是一直在变化的,甚至包括变化频率本身。我们需要知道的是应对变化的能力是至关重要的,除此以外应对变化的结果(时间成本、费用成本、质量、可靠性)也是很重要的竞争力。

第二条 产品是资产,代码是负债

你的产品解决了客户的问题,创造了价值,因此是你的资产。而代码是创造产品过程中要复出的代价。代码越多,就需要花更多精力去阅读、测试、维护和理解。尤其是考虑到第一条,这显得更为重要。要保守的接受新代码。最好的代码就是那些不用写的代码。

译者注:这一条需要反复品味,没吃过很多亏写不出这一句:)尤其作为团队管理者或者架构师,要时刻留意需求的实现方式。要选用最适合当前产品所处阶段的、综合成本最小的方式来完成。

第三条 不要过早抽象

除非你非常有信心,并且确定抽象可以解决已经实际存在的问题,否则不要做,等待更多信息。在此之前,重复代码可以避免依赖问题,这使得代码可以更轻松的修改或删除,而不互相影响。过早的抽象只会因为依赖关系和不再直接而增加复杂度,并且成为快速响应变化时的瓶颈。

第四条 代码应该可以轻松删除

写可删除的代码,很大程度上和解耦是一个意思。当然并不是说所有代码都需要简单的可删除,但最小化依赖、精确定义过的接口从而得到清晰的边界、合理的整体设计可以使得每个部分都变得更容易删除。我曾经听人使用“花费的代码(code spent)”这种表达,用来代替我们常说的“写过的代码(code written)”,我个人很喜欢这种说法。我也认可删除代码可以降低未来成本。

译者注:老实说本人没有非常理解 code spent 和 code written 的差异。

第五条 现存代码的影响力

存在即表明正确性及合理性。我们总是希望如此,但也不全对。我们需要有自信去修改旧代码,同时审视是否应该这么做。不要让旧代码的存在使得我们怀疑它们无法被移除。如第四条所说,如果系统设计的足够好,就会让我们更容易判断是否还需要现存的代码。

译者注:这一条说的有点啰嗦,其实就是辩证的看待是否要对旧代码动刀子。一般来说已经写好的代码都是有历史原因的,不要在不了解的情况下轻易去动,但也不是说永远都不要碰它们。

第六条 偶发复杂性是最大的风险之一

偶发复杂性特指本来可以避免的,由于糟糕的设计、错误的决定、没有选择适合的实现方式而导致的系统复杂性。如果不以最简洁为目标,这种复杂性会随着系统的增长从而最终导致方方面面的负面效应,甚至到很难理解整个系统的程度。2006 年的论文《Out of the Tar Pit》是关于这个话题值得一读的内容。

译者注:偶发复杂性(Accidental complexity)是相对于必要复杂性(Essential Complexity)的概念。简单来说就是世界上有两种复杂,一种是这个事本来就很复杂,一种是人为的搞复杂了。“Accidental complexity”特指后者,相关概念最早出现在《人月神话》中著名的“No Silver Bullet”章节。

第七条 卓越的技术有可能被糟糕的个人行为所掩盖

除非你是完全一个人工作,否则重要的不只是解决技术问题,写出良好代码的能力。如果你让身边的人都感觉不舒服或者更低效,这反而是更严重的问题。就像你要学习写好的代码,你也得学着做一个“好人”。同理心是做好这一点很重要的因素,要认识到每个人都不同。关心,理解,帮助他人,寻求帮助,变得友善。做一个大家都喜欢跟你一起工作的工程师。

译者注:工程师不只要变的专业,还要具备一定的职业素养。这确实是很多人都会忽略的问题。

第八条 对写代码的人友善

代码本质上只是反应了我们所知所想的某个瞬间。代码并不能代表你,你可能写了这段代码,即使只过了三分钟你也已经成长了,但代码没有。针对一段代码好坏的讨论,永远都不应该牵扯到写它的人。要学会保持职业的态度。谈论代码,或者谈论问题,但不要牵扯到谁写的。讨论时更多的使用“我”而不是“你”。有时候我甚至会假设是我自己写了这段别人的代码,这样可以帮助避免显得听起来是在针对人。

第九条 保持尊重和耐心

我们每个人都是从某一处开始起步,假设你身边是一群有耐心的、希望你能成功的人,肯定比一群让你自己感觉不属于这里的人舒服多了。如果你感觉这条让你很挣扎,试着想象一个新手工程师肯定在某一方面比你强,比如外语或者做饭或者某项运动。想象跟他互换角色,你会喜欢他们如何对你?再次重申:做一个大家都喜欢跟你一起工作的工程师。

译者注:这里我想补充一点,不只是对新手工程师,还包括对其他部门不懂技术的同事,不要展现出高高在上的技术大神的感觉,言语中透露出“你什么都不懂还来跟我讨论”的鄙夷之情。要用对方听得懂的方式交流,耐心的交流。

第十条 真正的权威来自知识而非职位

专业知识和对于问题、领域、客户的理解,远比名片上的前三个词重要——即使是有水印的那种。深入理解事情的运作方式,建立一套扎实的基础知识,权威会随之而来。

译者注:“即使是有水印的那种” 出自一部美国电影:American Psycho,影片中几个商务人士在炫耀各自的名片。

“Look at that subtle coloring. The tasteful thickness.”

“Oh my God. It even has a watermark.”

第十一条 教是一种变相的学

如果你觉得你懂些什么,试着教一下。通常试着给别人解释你知道的事情会迫使你了解的更清楚。同样的,把事情写下来也有类似的效果。我自己有无数次的以为自己很懂,但当我开始给别人讲解时却发现并没有。

第十二条 让周围的人一起变强

团队不会因为一个厉害的人而变得强大。强大的团队一定是因为每个人都在挑战自己不断进步。当你学到什么好东西,分享它从而让你身边的一人一起变强。如果每个人都这么做,所有人都会从中受益。这会有趣的多。另外一个理由是第十一条。

第十三条 等的越久知道的越多

我仍在学习这一点并努力的避免内心中想要快速做决定的直觉。事实上,处理不紧急的事情时拖延的越久,做决定时掌握的信息越多。当然不能总是拖延做决定,但至少你可以思考当下先不知道答案是否可行。

第十四条 好的类型系统值得加大比重

我的职业生涯在静态和动态语言之前来回切换过数次,我目前认为好的类型系统值得为它花上一些功夫。那是因为好的类型系统不会有太多开销。假如类型系统设计的好的话,用起来的感觉会非常接近动态语言(通过推理和流分析来实现),同时会避免整整一大类问题,因为编译器可以更快更好的处理这些问题。Rust 中的 ownership 就是一个很好的例子,证明这个思路发展的很好,甚至比过去几年设想的还要更进一步。

第十五条 正确的团队成员胜过一切

团队里有一群想要一起创造出伟大产品的人,会使得很多其他问题变得简单。这里的“正确”一次是很主观的,且依赖于所处的环境。至少同理心,尊重和友谊一直是我参与过的伟大团队的常识。

第十六条 适当的守旧

过时的技术意味着更老但理解的更深。在如何高效的使用,更好的了解故障模式,更容易找到人和资源应用这些技术方面,有着前人辛苦积累的宝贵经验。我非常喜欢 Dan McKinley 提出的创新代币的说法。你只有三个创新代币,用来构建新东西,那些理想情况下会使得核心更牢固的新东西,超过三个的话稳定性和成熟度的风险就会增加。

译者注:这是另外一个需要辩证看待的问题,就如同前面提到的要不要重构现有代码一样。适度的引入新技术,旧技术栈有它自己的优势。这个问题比较见仁见智了,我自己是保守的改革派。

第十七条 谨慎的扩张团队

这句话改编自一句名言,你当前团队的情况可能会不太一样。从我的职业生涯来看,更小的团队往往更高效。当然了,我们需要找到一个平衡点,取决于每个团队所面临问题的重要性和复杂度。通常来说,更小的团队意味着更少的沟通开销,更少的误解,更容易让每个人都发声。并且在一个小团队中,每个人都感觉更个性化,也更有责任感,我喜欢这样。

第十八条 适度休息

我很高兴看到“996让你成功”这种态度的淡化,人们花更多精力用于追求精神健康和快乐。没有充分休息的时候将无法感受到快乐,而不快乐何来最佳状态?也就是说,为了达到最佳状态,必须得到一定的休息。我认为休息也是工作的关键部分,就像体育锻炼一样。

译者注:鎴戝幓浣犲ぇ鐖风殑

第十九条 至少有两个选择再做决定

当你脑子里听到叮的一声,这时你意识到你找到了解决问题的方法。或者这只是一个很琐碎的小问题,没什么其他要做的了。但如果这个问题并非小问题,就值得思考一下是否有更佳的答案。

为了避免那种找到方案的兴奋感驱使你直接使用第一个冒出来的想法,试着找到至少一个其他答案。找其他解决方法的过程往往会迫使你想的更多,而一旦有了两个方案你就要考虑如何做取舍。这整个过程会帮助你更清晰的认识问题。

第二十条 发表意见时不要让人以为你只是固执己见

表达自己的想法是很重要的,团队应该永远都有这么做的空间。但有条界限分割着正常表达和给人固执己见的感觉。在一个团队中,每个人都感觉自己能挑战一个决定或者改变某些事,这是非常健康的。

我曾经收到过一个很不错的建议,就是在你发表看法的时候带上百分比。“我有九成把握觉得使用 Visual Basic 不是个好主意”。即使是 95%,至少给其他人留了一个质疑和沟通的窗口,也给你自己一个接收新信息来降低自己确定性的机会。

第二十一条 学会说我不知道

老实说,我们没人知道我们真正在做什么。至少我不知道。我们所处的行业变化太快,有太多刚刚过时的东西,同时又有太多新冒出来的东西。我们每天都在学习,也没有一个所谓差不多了的标准答案。我们的价值并不是知道所有的事情,我们的价值是不断的学习,去发现并解决问题,然后探索新的问题。

假如有人跟我说“我不知道”我会很兴奋,因为这是一个一起探索问题一起学习的好机会。不要因为好像只有你一个人不懂就隐藏起来。其实通常是别人也不知道,但你的诚实会带领大家都公开的加入进来一起寻找答案。

第二十二条 动手折腾,然后扔掉

快速的试错几次比想要一开始就弄对要快多了。有时候探索问题最好的方式就是在它周围来回动手尝试,尽可能的学习。

有时候你还没有真正理解某个问题的领域,但直接动手试一下,也许会发现很多高层次的设计思想和文档中忽略的细节。尽情去试错,等到得出答案就可以把这些临时的内容一起丢掉,这种方式你会学的非常快。

译者注:这种方式不仅适用于开发团队,更广泛的说适用于互联网开发的几乎所有场景,比如可以快速实现一个原型让需求方体验从而尽快发现改进的方向,也可以快速上线一个版本,让用户告诉你改进是否有效。

第二十三条 重视管理状态

每个应用程序都有状态,但如何管理状态的方式会导致结果大相径庭。不太好的做法会使得状态管理成为整个系统复杂度的重要来源,这通常由于在问题没有变的更糟糕的之前没提前思考这个问题。

有很多策略可以提供帮助,比如在给定的环境中使用特定的方法来处理状态,或是使用函数式编程或其他方法,以围绕状态应该如何变化创造出更严格的约束。无论怎么做,都应该花时间仔细考虑这一点。

译者注:这一点上大前端(即非后端的统称)工程师应该更有体会。

第二十四条 一切都是权衡

大多数时候当你做了一个决定,就意味着有意无意间在其他决定之间权衡。有时候这种权衡很明显,但也有时离我们眼前所见隔了几层。要保持思考每个决定的权衡点,即使有些并不明显。

这里有个很好的例子就是 Go 语言。Go 的类型系统还很简陋(当前来说),它还只是一门很小的语言。这里的权衡是什么呢?由于它的还很小,以及对于各种花式写法的限制,导致我的代码和其他人的代码看起来很相似,这会提高我的生产力。不像我以前写其他语言时的体验,看了别人写的东西就会心想:“我必须尽快重构”。永远都要做出权衡,找到这些要权衡的点,你就会做出更好的决定。

第二十五条 能方便修改的设计就是好设计

根据第一条经验,意味着我们必须很好的处理变化才能成功。这些变化不仅仅是指那些推着我们往前走的外部变化,还包括了内部的变化,比如新功能和规模的变化带来的挑战等等。

好的系统设计应该能够尽可能的包容我们要经常变化的需求,而不用每次都从头开始。换言之,我们需要更改或删除的部分越少,越快能够面对变化,越是好的设计。