十年编程反思:一些不好的建议

这篇文章是一个系列的一部分 –十年编程反思。 SHI对十年编码的反思

像许多程序员一样,我基本上是自学成才的。我很少与比我更有经验的人一起工作,尤其是在我职业生涯的早期,我花了很多时间与其他 20 多岁的年轻人一起工作,他们也只有几年的经验。因此,我们都从互联网上找到的建议中学会了如何编程,尤其是通过 reddit 和 hacker news 等网站分享的帖子。


像许多程序员一样,我基本上是自学成才的。我很少和比我更有经验的人一起工作,特别是在我职业生涯的早期,我花了很多时间和其他20多岁的年轻人一起工作,他们也只有几年的经验。所以我们都从互联网上找到的建议中学习如何编程,特别是通过reddit和hacker news等网站分享的帖子。从那时起,我的大部分进步得益于忘掉这些东西。事后看来,我在网上读到的大多数关于如何编程的文章和讨论都对我写出好用的代码的能力是有害。
从那以后,我的大部分进步都是在忘却所有这些事情。事后看来,我在网上读到的大多数关于如何编程的文章和讨论都对我成功地生成工作代码的能力有害。

这并不是说大多数程序员都是糟糕的程序员。只是,好的程序员不会自动提出好的建议,或者好的建议会比坏的建议更广泛地分享。
这并不是说大多数程序员都是糟糕的程序员。只是好的程序员不会自动产生好的建议,或者好的建议会比坏的建议被更广泛地分享。

生搬硬套
背景很重要。

选择悖论是一个广为流传的民间故事,讲的是一个实验,在这个实验中,超市陈列的果酱种类越多,购买量就越少。给出的解释是,选择是有压力的,所以有些人,面对太多可能的果酱,左右为难无法选择,最后空手而归。这个实验经常被新闻和媒体引用,通常描述为“科学家发现选择对你有害”这样的描述。
选择悖论是一个广为流传的民间故事,讲的是一个实验,在这个实验中,超市陈列的果酱种类越多,购买量就越少。给出的解释是,选择是有压力的,所以有些人,面对太多可能的果酱,会完全弹出来,回家没有果酱。这个实验经常被新闻和媒体引用,通常带有”科学家发现选择对你有害”这样的描述。

但是,如果你去一家大型超市,你会看到大约1200万种果酱。难道他们没有听说过果酱实验吗?吉姆·曼齐(Jim Manzi)在 《不受控制》中提到:
但是如果你去一家大型超市,你会看到大约1200万种果酱。他们没听说过果酱实验吗?Jim Manzi在《Uncontrolled》中写道:

首先,请注意,所有的推论都是建立在总共购买了三十五罐果酱的基础上的。其次,请注意,如果果酱实验的结果是有效的,并且具有作为经济或社会政策基础所需的普遍性,那么这意味着许多商店可以淘汰75%的产品,并导致销售额增长900%。这将是一个相当令人震惊的结果,表明理论可能存在问题。
首先,请注意,所有的推论都是建立在购买了总共35罐果酱的基础上的。其次,请注意,如果果酱实验的结果是有效的,并且具有作为经济或社会政策基础所需的普遍性,那么这将意味着许多商店可以减少75%的产品,并使销售额增加900%。这将是一个相当令人震惊的结果,并表明测量可能存在问题。

[…]在最初的实验中,研究人员自己对他们明确的普遍性主张很谨慎,并且已经投入了大量的精力来寻找选择超载持续发生的条件的确切问题,但是推广者将从一家商店在两个星期六的一次优惠券加展示促销中得出的结论进行了压缩,直到关于该商店果酱产品选择的影响的断言,到果酱产品选择对美国所有杂货店的影响,到关于产品选择对每家商店所有零售产品的影响的说法,最终到关于选择对社会的好处的相当夸张的说法。但正如我们所看到的,在不同情况下的50个实验中检验这种说法给这个断言泼了很多冷水。
[…]在最初的实验中,研究人员自己对他们明确的普遍性主张很谨慎,并且已经投入了大量的精力来寻找选择超载持续发生的条件的确切问题,但是推广者将从一家商店在两个星期六的一次优惠券加展示促销中得出的结论进行了压缩,直到关于该商店果酱产品选择的影响的断言,到果酱产品选择对美国所有杂货店的影响,到关于产品选择对每家商店所有零售产品的影响的说法,最终到关于选择对社会的好处的相当夸张的说法。但正如我们所看到的,在不同情况下的50个实验中检验这种说法给这个断言泼了很多冷水。

作为一个实际的商业例子,即使是一个简化的因果机制,包括一个有用的前瞻性预测规则,也不太可能像“将QwikMart商店更名为FastMart将导致销售额上升”,而是更像“将QwikMart商店更名为FastMart在高收入社区的高交通道路上将导致销售额上升,只要商店关门刷漆不超过两天。“在开始测试之前,我们极不可能知道所有可能的隐藏条件,并且能够设计和执行一个发现这样一个充满条件的规则的测试。
作为一个实际的商业例子,即使是一个简化的因果机制,包括一个有用的前瞻性预测规则,也不太可能像“将QwikMart商店更名为FastMart将导致销售额上升”,而是更像“将QwikMart商店更名为FastMart在高收入社区的高交通道路上将导致销售额上升,只要商店关门刷漆不超过两天。”在开始测试之前,我们极不可能知道所有可能的隐藏条件,并且能够设计和执行一个发现这样一个充满条件的规则的测试。

此外,这些因果关系本身也可能经常发生变化。例如,我们发现,在一次测试中,一次特定的促销活动与没有促销活动相比,会带来净利润的增加,但到了明年,当大量的变化发生时–我们的竞争对手推出了新的促销活动,整体经济状况恶化,消费者流量从购物中心转移到了购物中心,等等–这条规则不再适用。为了扩展前面的比喻,我们正在通过我们的小腿撞到家具上来找到我们在黑暗的房间里的路,而未被观察到的小精灵则继续移动我们周围的家具。由于这些原因,仅仅进行实验,找到因果关系,并假设它是广泛适用的是不够的。我们必须运行测试,然后在实际实现中测量从这些测试中开发的规则的实际预测性。
此外,这些因果关系本身也可能经常发生变化。例如,我们发现,在一次测试中,一次特定的促销活动与没有促销活动相比,会带来净利润的增加,但到了明年,当大量的变化发生时–我们的竞争对手推出了新的促销活动,整体经济状况恶化,消费者流量从购物中心转移到了购物中心,等等–这条规则不再适用。为了扩展前面的比喻,我们正在通过我们的小腿撞到家具上来找到我们在黑暗的房间里的路,而未被观察到的小精灵则继续移动我们周围的家具。由于这些原因,仅仅进行实验,找到因果关系,并假设它是广泛适用的是不够的。我们必须运行测试,然后在实际实现中测量从这些测试中开发的规则的实际预测性。

每当我看到一篇博文时,我都会想到这一点,这篇博文的智慧来自单一领域的单一经验。“编程”涵盖了具有不同问题域、团队规模、管理结构、项目生命周期、部署规模、部署频率、硬件、性能要求、失败后果等的大量活动。我们应该预料到,任何给定的实践在所有这些情况下都是合适的,这是非常罕见的,更不用说我们可以从一些项目的结果中发现一般的最佳实践了。
每当我看到一篇博客文章,从一个领域的一次经历中得出精辟的智慧时,我就会想到这一点。”编程”涵盖了各种各样的活动,包括不同的问题领域,团队规模,管理结构,项目生命周期,部署规模,部署频率,硬件,性能要求,失败的后果等,我们应该预料到任何给定的实践在所有这些情况下都是合适的,更不用说我们可以从几个项目的结果中发现一般的最佳实践了。

(另见 泛化性危机
(See也就是普遍性危机)。

细节决定成败。
细节很重要。

隐性知识或隐性知识——与正式的、编纂的或显性的知识相反——是难以表达或提取的知识,因此更难通过写下来或口头化的方式转移给他人。这可以包括个人智慧、经验、洞察力和直觉。
隐性知识(英语:Tacit knowledge)是一种很难表达或提取的知识,因此很难通过写下来或用语言表达的方式传递给他人。它包括个人的智慧、经验、洞察力和直觉。

[…]说一门语言、骑自行车、揉面团、演奏乐器或设计和使用复杂设备的能力需要各种知识,这些知识并不总是明确知道的,即使是专业从业者,也很难或不可能明确地传授给其他人。
[…]说一种语言、骑自行车、做面团、演奏乐器或设计和使用复杂设备的能力需要各种各样的知识,这些知识即使是专业的从业者也不总是明确知道的,而且这些知识很难或不可能明确地传授给其他人。

维基百科
维基百科

编程实践大多是隐性知识。隐性知识不容易分享。专家会讲述一些听起来很简单的经验法则,但随后在特定案例中对其进行研究将很快发现大量例外和警告,这些例外和警告因具体情况而异。这些是从许多过去的经验中产生的,不能很好地概括到该经验体系的背景之外。
编程实践大多是隐性知识。隐性知识不容易分享。专家会给出一些听起来简单的经验法则,但随后在具体案例中对它们进行盘问,很快就会发现大量的例外和警告,这些例外和警告会根据具体情况的具体细节而变化。这些都是从许多许多过去的经验中产生的,并且在经验主体的背景之外不能很好地概括。

在不知道所有这些细节的情况下尝试应用经验法则往往会导致失败。诸如“不要重复自己”、“你不需要它”、“关注点分离”、“测试驱动开发”等短语最初是从一些有效的经验中产生的,但后来被过度概括和过度应用,没有任何原始的细微差别。
试图在不了解所有这些细节的情况下应用经验法则往往会导致失败。像”不要重复自己”,”你不需要它”,”关注点分离”,”测试驱动开发”等短语最初是从一些有效的经验中产生的,但是后来被过度概括和过度应用,没有任何原始的细微差别。

传达隐性知识的方式,如果有的话,是通过产生规则的经验体系。出于这个原因,我发现在具体的经验报告或观察人们实际工作方面更有价值,而不是写一般原则。
传达隐性知识的方法,如果有的话,是通过产生规则的经验主体。因此,我发现具体的经验报告或观察人们的实际工作比写一般原则更有价值。

混淆手段和目的
混淆手段和目的

目标始终是编写一个程序来解决某些问题,并且可以在其使用寿命内进行维护。
目标总是写一个程序,解决一些问题,并可以在其有用的生命周期内维护。

在许多情况下,像“编写短函数”这样的建议是一种可能有助于实现该目标的技术,但它本身并不是一个目标。然而,人类思维的某些特征使得这些伪目标很容易取代实际目标。因此,您可能会听到人们说某些 软件很糟糕, 因为它的功能很长,即使有证据表明它也恰好易于维护。
像”编写短函数”这样的建议是一种在许多情况下有助于实现这一目标的技术,但它本身并不是一个目标。然而,人类思维的某些特征使得这些伪目标很容易取代实际目标。所以你可能会听到人们说某个软件不好,因为它的函数很长,即使有证据表明它也很容易维护。

当手段和目的被混淆时,它通常还伴随着“应该”这个词和其他具有一定道德或卫生分量的类似短语(例如“正确的方法”、“干净的代码”与“代码气味”)。这剥夺了对特定项目的背景或目标的任何考虑。
当手段和目的混淆时,它也经常伴随着”应该”这个词和其他类似的短语,这些短语带有一些道德或卫生的重量(例如”正确的方法”,”干净的代码”与”代码气味”)。这就排除了对特定项目的背景或目标的任何考虑。

“如果你编写较短的函数,通常更容易在单元测试中隔离特定行为”是一个具体的声明,将行为和结果联系起来,你可以在特定项目的上下文中测试和评估。
“如果你写的函数更短,那么在单元测试中隔离特定的行为通常会更容易”是一个具体的说法,它将行为和结果联系起来,你可以在特定的项目环境中测试和评估。

“你应该写短函数”意味着如果你写长函数,你就是一个糟糕的程序员。无所适从。
“你应该写短函数”意味着如果你写长函数,你就是一个糟糕的程序员。谈话没有任何地方可以从那里进行。

不透明
没有梯度

科技公司通常通过使用简单的技术来解决业务问题来赚取令人难以置信的利润。这意味着这些公司的程序员可能会做出很多错误的决定,并采用糟糕的做法,但仍然会成功。无论你做什么,如果钱从天而降,那么没有太多的信息可以帮助发现更好的做法。
技术公司往往通过使用简单的技术来解决业务问题,从而获得令人难以置信的利润。这意味着这些公司的程序员可以做出很多糟糕的决定,采用糟糕的实践,但仍然可以成功。无论你做什么,如果钱福尔斯,就没有太多的梯度来帮助发现更好的实践。

例如 ,Slack 是一个非常成功的产品。但似乎每周我都会遇到一个新错误,使它对我来说完全无法使用,从 键入时每个字符需要几秒钟完全无法呈现消息。(另一方面,Discord 一直是可靠和活跃的,尽管从我高科技的谷歌搜索来看,它的员工数量是它的 1/3。因此,这并不是说聊天应用程序本质上是困难的。然而,Slack 的技术建议 很受欢迎 ,如果我在没有亲身体验结果的情况下遇到它,它可能看起来很有说服力。
例如,Slack是一个非常成功的产品。但似乎每周我都会遇到一个新的bug,使它完全无法使用,从输入每个字符时花费几秒钟完全无法呈现消息。(另一方面,Discord一直是可靠和活泼的,尽管从我高度科学的谷歌搜索来看,有三分之一的员工。因此,聊天应用程序并不是天生就很难。)然而,slack的技术建议很受欢迎,如果我没有亲身体验过它的效果,它可能会显得很有说服力。

错误的引导
梯度错误

像reddit、hacker news、twitter等社交网站在决定我自己和我认识的大多数其他程序员的大部分阅读材料方面占主导地位。这些网站强烈选择简短、易于阅读、娱乐性和伪争议性的观点。由于科技社区的快速发展,科技界也严重偏向于年轻和缺乏经验的人,因此投票机制并不是区分无聊的好主意和娱乐坏主意的有用机制。
像reddit、hacker news、twitter等社交网站在决定我和我认识的大多数程序员的大部分阅读材料方面占主导地位。这些网站强烈选择简短,易于阅读,娱乐和伪争议的意见。由于技术社区的快速发展,它也严重倾向于年轻和缺乏经验的人,因此投票机制并不是区分无聊的好主意和娱乐性的坏主意的有用机制。

我还怀疑这种动态有时是自我强化的。在看到许多投票率很高的评论支持某些特定的想法或座右铭之后,它开始听起来必须广为人知才能成为现实。因此,当您看到具有相同想法的新评论时,您会为他们给出正确答案而投票。这可以使想法持续存在,而不需要任何实际的成功来强化。(另见Applause lights。)
我还怀疑,这种动态有时是自我强化的。在看到许多高投票率的评论支持某些特定的想法或座右铭后,它开始听起来像是众所周知的真理。所以当你看到新的评论有同样的想法,你投赞成票,因为他们给出了正确的答案。这可以让想法持续下去,而不需要通过任何实际的成功来加强。(见(掌声

结论
什么是永恒

事后看来,回头看看帮助我完成工作的一小部分写作,它往往来自以下人员:
回头看看那些事后证明能帮助我完成任务的小部分写作,它们往往来自于这样的人:

  • 多年工作经验有几十年的经验
  • 具有实质性的技术成果
    有实质性的技术成果
  • 有分寸和深思熟虑,而不是高度自信
    是有分寸的,深思熟虑的,而不是高度自信的
  • 承认复杂性和微妙性,而不是用一种方法来统治它们
    承认复杂性和微妙性,而不是用一种方法来统治它们

例如,关于如何将代码组织成函数的主题, 对比 Martin Fowler
例如,在如何将代码组织成函数的问题上,对比Martin Fowler

如果你必须花精力查看一段代码来弄清楚它在做什么,那么 你应该 把它提取到一个函数中,并以“what”命名该函数。
如果你不得不花精力去查看一段代码来弄清楚它在做什么,那么你应该把它提取到一个函数中,并在这个函数之后命名这个函数。

任何超过六行代码的函数都开始闻到异味 […]
任何超过六行代码的函数对我来说都开始发臭[…]

VS约翰·卡马克
约翰·卡马克

我不打算发布任何命令,但我希望每个人都认真考虑其中的一些问题。
我不会发布任何命令,但我希望每个人都认真考虑其中的一些问题。

如果函数仅从单个位置调用,请考虑将其内联。
如果一个函数只从一个地方调用,考虑内联它。

如果从多个位置调用函数,请查看是否可以安排在单个unit完成工作,也许使用函数,并将其内联。
如果一个函数是从多个地方调用的,看看是否有可能将工作安排在一个地方完成,也许用标志,并内联。

如果一个函数有多个版本,请考虑使用更多(可能是默认参数)创建单个函数。
如果一个函数有多个版本,可以考虑创建一个带有多个(可能是默认的)参数的函数。

如果代码接近于纯函数式,很少引用全局状态,请尝试使其完全函数式。
如果工作接近于纯功能性的,很少涉及全局状态,那么尝试使其完全功能性。

当函数确实必须在多个地方使用时,尝试在参数和函数上使用 const。
当函数确实必须在多个地方使用时,尝试在参数和函数上使用const。

最大限度地减少控制流的复杂性和“area under ifs”,有利于一致的执行路径和时间,而不是“最佳”避免不必要的工作。
最小化控制流的复杂性和”如果条件下的区域”,有利于一致的执行路径和时间,而不是”最佳地”避免不必要的工作。

更复杂的是,“总是做,然后抑制或忽略”策略虽然对于高可靠性系统来说是一个非常好的主意,但在mobile等功率和热受限的环境中不太合适。
为了使事情变得更复杂,”总是做,然后抑制或忽略”的策略,虽然对于高可靠性系统是一个非常好的想法,但在功率和热量受限的环境(如移动的)中不太合适。

希望不那么糟糕的建议
希望我的建议不那么糟糕

在本系列的其余部分,我希望避免或至少缓和上述故障模式。
在本系列的其余部分中,我希望避免或至少缓和上述故障模式。

首先,让我明确一下我写作的背景:
首先,让我明确一下我写作的背景:

  • ~70% 是自雇人士,~30% 在小型早期公司工作。
    约70%为自雇人士,约30%在小型初创公司工作。
  • ~40% 生产代码 vs ~60% 研究代码(即没有严重的开发,没有长期维护)
    约40%的生产代码与约60%的研究代码(即没有严重的生产使用,没有长期维护)
  • ~30% 在代码库中,>100kloc 与 ~70% 的小型或绿色项目。
    在100kloc代码库中约占30%,而小型或绿地项目约占70%。
  • 主要是系统问题,尤其是数据库引擎和声明式语言。
    主要是系统问题,特别是数据库引擎和声明性语言。
  • 一些 UI 是有效的,但所有这些都是探索性的,而不是生产性的。
    一些UI工作,但所有这些都是探索性的,而不是生产性的。
  • 我从来没有维护过超过 2 年的代码库。
    我从来没有维护一个代码库超过两年。
  • 我从来不负责运行长期服务。
    我从来没有负责运行一个长期的服务。

我报告的任何事情对我来说都很好,都必须在这种有限的背景下考虑。
我报告的任何对我有效的事情都必须在这个有限的背景下考虑。

其次,我的目标是用多个例子来配合每个想法。这有助于传达实际的细节,并确保我谈论的是我实际做的事情,而不是我认为我应该做的事情。
其次,我的目标是用多个例子来陪伴每个想法。这有助于传达实际的细节,并确保我谈论的是我实际做的事情,而不是我认为我应该做的事情。

第三,在我能做到的地方,我包括一些反例,我曾经认为是个好主意的东西导致了问题,或者应用得不好。
第三,在我可以的地方,我包括了一些反例,我曾经认为是一个好主意,导致了问题或应用不当。

本文文字及图片出自 On bad advice

余下全文(1/3)
分享这篇文章:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注