有一种观点认为,使用更好的编程语言就会让软件开发变得更容易、更高效。在汇编或 Fortran 时代,这种观点无疑是对的。然而,现代的编程语言已经足够好了,目前的主要困难和改进机会出现在其他方面。编程仍然很难,但导致这种困难的原因却与所使用的编程语言无关。

安达尔定律


当你有一系列需要完成的连续性任务时,安达尔定律就会发挥作用。安达尔定律告诉我们,通过加快其中的一个任务来加快整个系列任务的速度是有硬性限制的。


例如,烧开水需要 10 分钟,然后煮意大利面也需要 10 分钟。即使你能够找到一种可以更快把水烧开的方法,你做一顿饭的时间也绝对不会少于煮意大利面所需的 10 分钟。


一般的公式是这样的,如果某项任务的总时间占比是 p,那么你永远都无法获得大于 1/(1-p)的加速。假设任务的一部分占用了 90%的时间,那么 p=0.90,将这个部分优化到零时间,将使整个工作速度提高 1/(1-0.90)=10 倍。


安达尔定律的关键点在于,你所能获得的加速受到了你所优化部分的大小限制。


编程之所以困难,原因有很多。为了简单起见,我们可以把困难的事情想象成必须按顺序完成的任务。毕竟,人类并不擅长多任务处理。在任意一个时刻,你要么在使用构建工具,要么在阅读文档,要么在编写代码或者参加会议。你一次只能应对一项任务,所以安达尔定律大致适用。如果你设法将构建时间降到零,那么项目的完成时间也只会短一点点。你的效率仍然受限于完成项目所需的其他事项。


过去,把一个程序翻译成计算机可以运行的东西是非常困难的。很久以前,需要先将程序转成 1 和 0,然后再一个一个地输入到计算机。我不知道这需要花费多少时间,但为了便于讨论,我们假设需要占用 90%的时间。这意味着,如果有一种更好的方式来告诉计算机该做什么(比如 Python),那么编程效率将提高 10 倍。


现在,我们有了更好的编程语言,可以花更少的时间告诉计算机该做什么,生产力得到提升。将程序转成代码不再需要 90%的时间,现在只需要 10%的时间。这意味着我们可以从中获得的最大改变是 1.11 倍,这比以前可以获得的加速少 81 倍!


这是因为软件开发其余的 90%任务都是困难的任务,一个更好的编程语言不会让它们变得更容易。

如何做到“没朋友”


我的意思是,编程的难度与编程语言无关。为了理解其中的原因,我们先假设不需要操心与计算机相关的东西,你只需要告诉你的朋友要做些什么。你不能作弊,不能让他们依赖常识性的东西,你必须替他们做出所有的决定。


你会发现,你需要花很多时间来解释背景。你的朋友需要了解计算机程序要处理的东西在现实世界中是怎样的,以及程序应该要做什么。你需要解释各种缩略词和术语,需要讨论一些外部因素。


你的朋友需要知道所有可能出现的情况,有大量的小细节需要处理,例如用户不能在购物车中输入负数个产品。用户可能会尝试做出所有可能的行为,会发生各种可能的事情,例如包裹在运输过程中弄丢了,你会发现有大量的边界情况需要告诉你的朋友。


向你的朋友解释这一切是很困难的。首先,你需要了解与项目相关的所有现实世界的细节(产品可能缺货,可能有折扣,等等)。其次,你需要做出程序在各种可能的情况下应该做什么的决定。第三,你需要以一种你的朋友能够理解的方式与他们交流。这意味着你需要组织好你的想法,让它们易于理解。如果你写过文章或博客,你就会知道传达大量的信息并不是一件容易的事情!


请注意,到目前为止,这些任务都还没有涉及计算机,当然也就不涉及编程语言。了解现实世界,了解程序应该做什么,以及如何组织这些想法,这些都是非常困难的任务。

描述与规范


这里有一个容易就掉进去的陷阱,人们很容易忽略描述与规范之间的区别。例如,当你做出一个描述(“一辆红色的汽车”),你可以测试一个东西是否符合这个描述(“是的,它是红色的,但它不是一辆汽车”),但这不足以告诉你如何制造一辆汽车,因为制造汽车需要制造汽车的规范。


创造出一样东西需要做很多决定。如果你把每一个决定的结果都写下来,就有了一个(未组织的)规范。开发程序需要你做出这些决定,所以仅仅进行描述是不够的——你需要一个规范。当你做出一个描述(“它需要把文件列出来”),认为它就是一个规范,却忽略了你还需要做出其他无数个细小的决定(“它应该以什么顺序列出文件?它们应该按照自己的路线走下去吗?”)。


当你要开始写一个程序时,你不得不面对这样的现实,即你的规范实际上只是描述而已。电脑不会接受类似“画一个矩形”这样的描述,它需要知道矩形应该出现在屏幕的什么地方,应该多大,应该是什么颜色的。将想法变成代码,你还需要做出很多决定,而做出这些决定需要付出很大的努力。人们倾向于将这种努力归咎于编程语言,却不愿承认这样一个简单的事实:仅给出描述是很难得到规范的。

回到计算机上来


开发软件不仅仅是理解要做什么并将其转化为代码。计算机本身存在一些问题,这些问题也需要程序来解决。程序要能够在硬件和网络上快速运行,可能需要处理机器故障,而工具和协议的复杂性带来了更多问题。这些问题并非是在向计算机解释应该做什么的过程中造成的,它们都是原本就需要做出解释的事情。


你需要在脑子里“运行”部分程序。有时候,逻辑就像听故事一样容易理解,但有时候,事件顺序和状态跟踪复杂到无法放在脑子里。获得运行正确的程序——或者纠正它们不正确的行为——需要了解程序在各种情况下的状态。


写代码可以将你对程序的想法具体化,但程序不会保持一成不变。你会发现 bug,想要新的特性,需要改变现有的行为。一个解决方案在一开始可能行得通,但这并不意味着它将一直是可行的。最终,你需要花时间去预测未来,去收拾烂摊子,你会不可避免地发现自己并没有“千里眼”。

康威定律


如果你自己不写程序,就需要和其他人一起工作,这带来了一系列全新挑战。


所有参与项目的人都必须以某种方式组织起来,每个人都有自己的工作要做。你不希望人们互相妨碍,所以必须分工。合理的分工要求你对程序的结构有很好的理解,这个时候康威定律就发挥作用了。


如果你有多个团队,事情就会变得更加困难。每个团队都有不同的目标,因此会针对不同的事情进行优化。对其他团队有利的决定可能会阻碍你完成工作。了解其他人的立场,并找到一个好的妥协方案,这是一项艰难的任务,但你必须完成。


在大项目中,不可能有哪个团队(更不用说哪个人)能了解所有的东西,但你仍然需要弄清楚如何设计系统的各个部分,并让这些部分相互配合,这比你自己做出整个设计要困难得多。

“打破”定律


我们可以尝试找到安达尔定律无法发挥作用的地方。如果个体任务的速度不是完全独立的——如果你可以通过优化一个任务来加快另一个任务——那么就有可能找到对你有帮助的解决方案。


更好的编程语言和开发环境可能是我们可以加以利用的地方。如果程序可以由更少的人来开发——比如说两个人,而不是一个团队,或者一个团队,而不是一个部门——那就可以大大地减少组织方面的开销。如果你自己实现接口所有的代码,就不需要通过开会来决定如何设计接口。效率的提升不仅会降低编写代码的成本,还会改变工作的形式,从而降低其他任务的成本。也就是说,这里的限制变成了你可以在这条路上走多远,因为一个程序员不可能把所有跟实现业务相关的东西都装进脑子里。


迭代速度是另一个可以加以利用的杠杆。为了开发一个程序,你需要了解这个领域的问题和需要做出哪些决定。为了完成这些任务,你需要把收集到的所有细节都装到脑子里,然后形成一个心理模型。这是一种可行的方法,但可能不是最有效的方法。另一种方法是基于某些明显的细节建立一个小的心理模型,然后根据这个模型创建一个小程序来测试这些想法是否符合现实,并根据反馈进行迭代,每次都创建出更丰富和更准确的模型。这似乎更适合人们实际的学习方式。为了让这种方法奏效,你需要能够快速测试想法并获得反馈。理想的状态是,在你完成输入后,新代码就可以开始运行。通过改变开发环境来实现更快的迭代周期,将使开发人员从第一种方法转到第二种方法,帮助他们更好地理解问题。


一种表达能力更强的编程语言是否能有效地提高生产力,对于这一点我并不是特别乐观。我希望的是能有更好的开发环境。如果我们有更好的工具来理解现有的代码,有更快的开发迭代周期,繁琐的工作变得更少,就可能会改变软件的开发方式,并以一种复合而非递减的方式给我们带来回报。


原文链接:


http://jeremymikkola.com/posts/2021_01_01_why_software_development_is_hard.html