译:编程 40 年

原文:https://liw.fi/40/
作者:Lars Wirzenius
译者:ChatGPT 4 Turbo

编者注:人很重要,软件只是乐趣。

引言

10 PRINT "HELLO"
20 GOTO 10

1984 年 4 月,我的父亲为他的家庭办公室购买了一台计算机,一台 Luxor ABC-802,配备有 Z80 CPU、64 千字节的 RAM、80×25 文本模式的黑底黄字屏幕,或约 160×75 像素的图形模式,并且有两个软驱。它的 ROM 中内置了 BASIC,而且完全没有游戏。如果我想玩它,我必须学习如何编程,并编写我自己的游戏。我学会了 BASIC,接下来的几年里,我还学会了 Pascal、C 等更多语言。我找到了我的激情所在。我当时 14 岁,我知道我长大后想做什么。

我在学习编程时,我认为真正理解计算机是如何工作的、编程语言是如何工作的以及像文本编辑器这样的各种工具是如何工作的非常重要。我想磨练我的技艺,生产出人类所能产出的最好的代码。我错了。

这篇文章是我希望在学会编码基础知识后被告知的内容的浓缩。然而,我被告知,在我二十五岁时,我已经太老,无法作为程序员工作。我被告知我至关重要的是尽可能快地学习尽可能多的算法、数据结构和语言。毕竟,编程是年轻人的游戏,需要能够熬夜到天亮,每晚都比别人更多地写代码。这是成功的唯一方式。结果,这些都不是真的:无论是关于年轻的部分,失眠的部分,尤其是关于性别的部分。

当我写这篇文章时,距离我第一次编写计算机代码将近四十年了。我设法通过开发软件自给自足,而且我每天仍然在编写代码。我想不出我还愿意做其他什么生活谋生的事情。我指不出巨大的成功和令人印象深刻的壮举,但我希望能够在这个行业生存几十年,足以让我有足够的资格谈论软件开发。

这篇文章讨论了关于如何成功构建软件我所学到的一些事情。这些都是我个人经验中的学习;我不是研究者,鲜有参考来源,这些大部分并没有证据支持。这篇文章基于我自己的经验,如果你不同意,那也没关系。

我在这篇文章的目的是让读者去思考、去研究、去学习、去琢磨。我的目的不是告诉读者如何思考、什么该思考、事情如何、或是给出关于构建软件过程中每一个问题的答案。

核心技能

有趣且重要的软件超出了任何一个人在合理时间内单独构建的能力范围。这意味着构建软件的基础、关键、核心技能是沟通和协作。

仅仅了解计算机如何工作、如何使用编程语言、了解算法和数据结构或如何使用参与软件构建的各种工具是不够的。你还需要知道如何与他人交流去学习构建什么样的软件,它必须做什么,可以接受的努力是多少,如何管理工作以及还有很多其他事情。你必须知道如何与他人合作构建一些比你们任何一个人都要伟大的东西。如果你和你的团队能做得好,它将比你们各自总和还要伟大。团队合作可以是一种力量的倍增器。

这些是难以学习的技能。我发现它们比构建软件的任何技术部分都要难。

即使在只有我一个人的项目中,至少也涉及三个人:过去的我、现在的我和未来的我。

  • 过去的我是一个懒惰且粗心的家伙,总是留下一团糟。
  • 现在的我做得非常出色,但不得不应对过去的我所做的所有愚蠢事情,并需要安抚未来的我。
  • 未来的我是一个自大而固执的势利小人,对他而言没有什么事情会完全让人满意。

让这三个人相处得足够好以完成任何事情,对我来说一直是一场挣扎。我用我的日志来应对。我在日志中写下我做了什么、为什么以及结果如何。我试图写得即使我累了,或刚刚经历了一次短期的神经清除器记忆抹除,也能理解。我还计划明确的迭代,有具体的任务,并且我使用 GTD 系统来跟踪那些还需要完成的事情。

关于生产力

软件开发领域对于生产力的定义并不明确:时间容易衡量,但输出却不是。大多数人对自己认为的生产力有至少一个模糊的概念。然而,有一些已知的因素会影响生产力 —— 这些因素通常被忽略。

要做好工作,你需要照顾好自己。你需要睡得好。你需要吃得好。如果你累了,或者压力太大,你就会犯错误和做出错误的决定。这些会导致你做出糟糕的工作。这不是道德问题,但你可能需要修正自己的错误。如果你做好了工作,你会更享受生活和工作。

Hillel Wayne 已经讨论过睡眠,并提供了来源。推荐阅读:

你还需要一个有助于你做好工作的环境。它安静吗?有打断吗?家具舒适,并不会长期伤害你吗?你能否获得必需品,比如厕所和茶?你能从工作区休息一下吗?你能去散步吗:散步有助于思考。这是一个不完全的列表。

此外,你需要以适合你的方式管理自己和你的工作。最佳方式取决于你是谁,你的偏好和经验,以及你所做的工作类型。没有一个单一的解决方案能够适应所有人的所有时间。

对我自己而言,我发现应用 David Allen 的 Getting Things Done 系统效果很好,但无论你做什么,你都需要知道你今天和不久的将来需要实现什么,你需要安排事情,以便你可以一次专注于一件事情。注意,我使用 GTD 系统的部分原因是知道我可以偷懒;我不打算在所有时间里尽可能地保持高效。

追踪你所完成的事情可能是值得的。反思这些可以给你一种成就感,并让你看到你所取得的进步。这可以是一个激励和士气提升器。

多任务对于计算机来说没问题,但你的大脑一次只能思考一件事情,并且在上下文切换上表现得非常糟糕。

关于治理

当一群人合作时,他们需要尽早建立的一件事就是治理:基本上,这个群体如何做决定,以及如何改变决定?决定的范围从根本到平凡:谁在群里?它是民主、等级制还是其他结构?谁有发言权?群组可以得到什么饮料?网站应该使用什么字体?

治理是困难的,但当它明确时会更容易。对责任和权力的不确定性会导致混乱和争吵,这些可以撕裂一个团队。

所有的团队最终都会有冲突。管理并解决差异名义上是管理层的工作,但实际上这需要每个人的参与。这需要的技能似乎很少教给程序员,这是一个遗憾。

推荐阅读:

关于政治和伦理

软件开发始终是一种政治和伦理行为。每当你在构建软件时,你都必须做出大量的决定:

  • 软件应该做什么?
  • 使用它会需要哪些资源?
  • 它应该如何被使用?
  • 使用它需要哪些能力?
  • 哪些事情易于做?
  • 哪些事情难于做?
  • 软件会赋予用户权力,还是迫使他们为他人的利益做事?

所有这些决定都有后果,这将使某些人受益于其他人。因此,它们具有政治和伦理方面,需要被考虑。

例如,如果你制作的东西可以用廉价的手机使用,你就让世界上大多数人都能使用它。如果你使它需要昂贵的硬件,你就排除了许多人,特别是穷人。有时这是不可避免的,并且是你构建软件要解决的问题所固有的,但它仍然是一个选择。

可能对于某个特定的决策并没有所谓的正确或错误选择,但总有后果。你必须考虑这些后果,决定它们是否可以接受,以及对谁而言是可以接受的。

通常,你构建的软件会反映出一些价值观。确保你知道你是否认同这些价值观。

推荐阅读:

论伦理和软件自由

自由和开源软件是道德类型的软件。软件自由对于受计算机使用影响的人们的福祉至关重要,无论他们是否直接使用计算机,或者其他人使用计算机做影响他人的事情。

我从 1986 年开始写自由软件,当时我第一次在 BBS 上阅读了 GNU 宣言。(“开源”这个术语是同义词:我不关心两者之间的差异。)

我职业生涯的很大一部分涉及构建开源软件。我对自由和开源软件而非非自由软件的偏好是明确的,并且有详细记录。

在过去三十年中,关于自由和开源软件的伦理以及与其他类型的软件的比较,以及许可证的具体问题,已经产生了相当于整个图书馆的文本。我不打算在这里对这些建议进行概述,也不会推荐阅读资料。

我想明确我的立场,以防这对读者很重要。然而,这篇文章的很小一部分,包括伦理主题,与软件自由相关,并且同样适用于开发非自由软件。

论多样性和品质

人权至关重要。善待他人是正确的事情。不管你做什么,这一切都具有最高优先级。如果你不同意这一点,我对你没有希望。

在软件开发环境中,我发现构建高质量软件最关键的事情是在参与其中的人中拥有思想的多样性。项目中引入的思想越多样,做出的决策越有可能适用于更多的情况、更多的人,并且解决更多出错的问题。

关于多样性思维

多样性思维源于来自不同背景、经历过不同生活的各种人。有时差异在外部是可见的,有时则不然。

当有疑问时,选择不同。如果你因为他人与你不同而排除他们,很可能你的选择是错误的。

多样性不保证成功。没有什么能保证成功。然而,单一性保证了你得到片面的答案。有时这足够了,但往往并不够。

在多样化的环境中合作和沟通肯定更具挑战性。不要害怕这些。将挑战视为学习的机会,变得更擅长于你所做的事情。

人们会犯错误,你也会犯错误。当别人犯错时,采取宽容和友善的政策是一个好策略,并期望别人也同样对待你。惩罚他人的错误会让你孤单。

关于维护

在软件行业里,人们普遍认为,软件生产的大部分成本来自初始发布后的所谓维护阶段。初始开发可能需要一两年的时间,而维护则需要几十年。

人们会认为这将导致开发实践、架构决策和其他一切都会为了降低维护成本而进行优化。不幸的是,经济和其他激励措施往往促使人们做出相反的选择。在大多数公司,计划地平线倾向于是一个季度,或者最多一年。人们认为现在花费更多的努力来长远节省大部分劳动是不可接受的。

即使是个人的爱好项目,人们通常也会强烈渴望尽快让事情运行起来,而不是花时间去做好事情,但至少那是一种内在的渴望,而不是外在压力。即便如此,结果是相同的:当发现错误、需求变更,或者需要将软件适应周边系统或生态系统的变化时,软件更难以更改。

我并不孤独地看到了这个问题。我认为,除非从根本上改变经济激励机制,否则这个问题无法解决。这取决于每个开发者能否在可能的情况下,偷偷地尝试降低维护成本,作为一种预防性的游击维护。

项目的重大问题

我发现,在每个新项目开始时回答几个问题是重要的,但也是有帮助的。答案不必是最终的,随时都可以改变:如果你在学到新东西时不调整你的思维,那你就不擅长你所做的事情。答案即使是无聊的,只要是诚实的就可以。重点是让你开始思考这些问题。

  • 你为谁构建软件?谁对它的看法很重要?

    这会让你了解项目中的一些利益相关者。后续可能会有更多的利益相关者,但这是一个开始。知道谁是利益相关者,让你可以集中精力满足他们的需求。这有助于使项目成功。

    利益相关者可能是最终用户,也可能是 CEO。如果你认为是最终用户,其实是 CEO,你会做出让 CEO 不满意的决定,项目会失败,而你不会明白原因。反之亦然。

  • 你为什么构建这个软件?

    “这是我的工作”可能是一个非常实际的回答。这意味着如果赚钱的可能性降低,你需要重新评估一切。一个不同的答案可能更多关于激情或使命,这意味着你对变化环境的反应将会有所不同。这里没有错误的答案,但确保至少对自己诚实。

  • 这个软件应该做什么,大致描述?同样,它不应该做什么?

    这是关于知道你是在构建一个文字处理器还是一个电子表格。这不是关于详细的要求或验收标准。

  • 这个软件应该如何工作,大致描述?

    同样,是大局观。你是在做一个命令行工具,一个网络服务,一个操作系统,一个移动应用,还是别的什么?什么类型和大小的硬件?

  • 什么是重要的,什么只是锦上添花的?

    这关乎需求和验收标准。你需要了解它们才能构建出好的东西。把它们写下来。同时写下你如何验证自己满足了这些需求。

计划与估算

对于非常近期之外的详细计划通常很难制定,且通常会失败。这包括估算工作将需要多长时间。我避免这样做。

计划和估算远非无用,也不应该被忽视。我进行计划和估算,但是在我学到的现实限制之内。具体来说,我发现几乎不可能制定超过一到两周迭代的详细计划。我所说的详细计划是指弄清楚每次坐下可以完成的任务的细粒度。

通常会发生一些意外的事情,使得几周以外的计划被破坏。可能是管理层改变了优先级,或者客户改变了主意,或者公司破产,或者被收购。可能是其他事情,但通常会发生一些事。如果迭代周期短,即使你所有的计划都必须被抛弃,你也不会损失太多。

如果你需要改变方向,你也可以更容易地做出反应。

我强调我在这里讨论的是详细计划。制定下个月或下个季度要做什么的计划是没问题的,只要所有涉及的人都知道这些计划很可能需要改变,并且计划保持适当的高层次。

打个比方:如果你计划进行一次横穿国家的公路旅行,你可能会想规划穿过哪些城市。你可能不想计划每一站加油或吃饭的地方。你想保留足够的灵活性,以便如果发生事故、天气或可能在路上突然出现的其他情况,你可以改变路线。

迭代是有效的。对于大型软件项目,别的方法似乎都不行。至少我从未见过其他方法有效。

对我来说,如果迭代是聚焦的,它们工作得就更好。有一个明确的、清晰的目标有助于减少范围蔓延。它集中工作,旨在实现迭代目标,而不是在当下似乎是个好主意的事情上。

当我计划一个迭代时,我会将事情拆分成可以在一次坐下来的时间内完成的小任务。我的任务估算分为四个档次:最多一刻钟、整小时、四小时或太长。我更喜欢较短的任务:它们更容易估算,更容易完成,通常破坏的东西更少。任何太长的任务都需要进一步细分。

有些任务是无法估算的。调试是一个典型的例子。你需要多长时间才能意识到以太网线路坏了,或者操作系统更新损坏了一些东西,或者找到某些其他不可预见问题的原因呢?调试是会发生的,你最好在计划中预留足够的余地来应对一些故障排除。

中断也会发生。有时你别无选择,只能立即反应并响应它们。为此留出空间。

用文字表达自己

你的记忆是短暂且易错的。你会忘记细节。你会忘记重要的事情。你会错误地记住事情。当你合作时,你和其他人会对所说的、达成的协议和决定有分歧。

我曾经参加过一个会议,有四个人,加上 CTO。CTO 禁止我们做笔记:看来那周的流行观点是,做笔记是让会议浪费时间的原因。会议持续了两个小时。之后,我们四个人大约有八种不同的意见关于什么被决定了。从未采取过任何后续行动。

写下来。这听起来像是一个愚蠢的琐事,乏味、枯燥和陈旧,但这是组织记忆的方式。写下来是一种容易被忽视的超能力。

你的团队应该保持会议记录,至少涵盖重要决策,以及会后每个人应该做什么。保持它们足够简短,以便容易编写和阅读。将它们存档在每个人随时都可以查看的地方。这对那些没能到场的人有帮助,可能是因为他们去度假了,或者是一年后才加入的。

关于会议,学习如何进行有效的会议。对我而言有效的方法是提前设置议程,并提供配套材料。为会议做好准备,以免浪费每个人的时间。有一个主持人来保持讨论的轨迹,和时间限制。你进行有效会议的过程可能有所不同,但你应该找到一个。

关于工作

进行更改时,一次只做一个更改。如果可以的话,将你正在做的更改分成更小的部分。小的更改更容易理解,且不太可能造成灾难性的后果。

自动化消除繁琐:运行测试、制作发布版本、打包、交付、部署等。尽可能早地做到这一点。建立一个流水线,你可以在其中进行更改,并确保软件仍然能够工作并且愿意的用户可以开始使用变更后的软件。你能使这个流水线越平滑,构建软件就越容易。

对每个项目使用版本控制。通过经常将更改合并到开发的主线上来实践持续集成。

开发一个你信任的自动化测试套件:如果测试通过,你就可以发布版本,或部署到生产环境。这通常意味着在项目早期就开始测试套件。确保自动化测试涵盖所有你关心的方面:目标是确保你提供给他人的东西按照预期的方式工作。同时更新生产代码和测试代码。

连续多次运行测试套件以识别不稳定的测试。不要容忍不稳定的测试。

即使以简单的方式,也要进行压力或负载测试。在你这么做之前,你不知道你的软件和系统能否处理 10,000 个并发用户。我的就不能。

在一份工作中,我们有一个系统在生产中运行良好了一段时间。我们将其部署给了一个流量更大的客户。我们的软件失败了。问题是我们用完了 TCP 端口号:我们为每个传入消息发起了一个 HTTP 请求,但我们没有重用底层的 TCP 连接。由于流量太大,所有可能的端口号都被使用了,然后一切都停滞了。解决办法是重用 TCP 连接,这个改变只需大约一行代码,然后一切都正常工作了。如果我们进行了即使是最简单的负载测试,就会自己发现这个问题。

如果可能的话,亲自使用软件。你会更好地理解你的用户。

观察其他人使用软件。你会更好地理解软件的尖锐角落。真实故事:对那些不参与软件开发的人来说,如果一个东西有三个不同的名字,使用软件会很难。看到有人因此苦苦挣扎,问题就变得痛 painfully 明显。

把测试者当作帮你发现错误的朋友。他们不是来羞辱你的。没有人是如此完美,从不犯错或忽视细节,或忘记整个功能。不要问我怎么知道的。

编码建议

简单、明显的代码更容易编写,更容易让它工作,更容易理解,也更容易改动而不至于破坏。尽可能地简化事物,但不要过度。只有当你证明了性能收益值得的时候,才牺牲简单性以追求性能。

复杂性是你以为的朋友,实际上是敌人。

耦合和内聚仍然是重要的概念。

在每个项目中,概念清晰性都很重要。同时,保持术语的一致性。混乱潜伏在清晰隐藏的地方。极大的混乱带来极大的烦恼和 bug 的确定性。

对用户来说,速度是一个特性,但对开发而言并非总是如此,因为太匆忙就留出太少时间思考。功能性可能是一种错误的特性。

推荐阅读:

  • 软件设计哲学 by John Ousterhout,我还没读过,只是浏览了一下,但一些我尊敬的人不断向我推荐。

发展职业生涯

你可以选择成为某个非常具体领域的深度专家,或者成为一个通才,或者某种混合型。明智地选择。可能没有错误的选择,但每个选择都有后果。

保持谦逊。当保姆,而非祖母。人们可能更尊敬强大的女巫,但他们更喜欢那些善良的。

保持开放和诚实。公平地对待他人。你不必相信因果报应,它也会起作用,所以让它对你有利,而不是对你不利。

帮助并提升他人。但与此同时,不要允许他人滥用或占你的便宜。你不需要接受无理。设定你的界限。

当你需要帮助,或当你陷入困境时,请求帮助。当帮助被提供时,接受它。

我不是谈论发展职业的正确人选,但当我做了上述事情时,事情通常最终会顺利发展。

要点

照顾好自己。睡觉。吃饭。锻炼。休息。放松。尽力照顾好别人。人很重要。软件只是乐趣。

致谢

感谢 Brennen Bearnes、Richard Braakman、Tyler Cipriani、Greg Grossmeier、Hackhörnchen、Soile Mottisenkangas、Daniel Silverstone 和 Enrico Zini 审阅这篇文章的草稿。任何错误都是我的。