There’s a key piece of magic in the engineering of the Internet which you rely on every single day. It happens in the TCP protocol, one of the fundamental building blocks of the Internet.
互联网工程中蕴藏着一项关键的魔法,而你每天都在依赖它。这项魔法就体现在 TCP 协议中,它是互联网的基本组成部分之一。

TCP is a way to transmit data that is reliable. By this I mean: if you send a message over a network using TCP, it will arrive, and it won’t be garbled or corrupted.
TCP 是一种 可靠的 数据传输方式。我的意思是:如果你使用 TCP 通过网络发送消息,消息一定会到达,而且不会出现乱码或损坏。

We use TCP for many things like fetching web pages and sending email. The reliability of TCP is why every email arrives in letter-perfect condition. Even if it’s just some dumb spam.
我们使用 TCP 来做很多事情,比如获取网页和发送电子邮件。TCP 的可靠性确保每封邮件都能完美无瑕地送达,即使是垃圾邮件也不例外。

By comparison, there is another method of transmitting data called IP which is unreliable. Nobody promises that your data will arrive, and it might get messed up before it arrives. If you send a bunch of messages with IP, don’t be surprised if only half of them arrive, and some of those are in a different order than the order in which they were sent, and some of them have been replaced by alternate messages, perhaps containing pictures of adorable baby orangutans, or more likely just a lot of unreadable garbage that looks like that spam you get in a foreign language.
相比之下,还有一种名为 IP 的数据传输方式,它 不太可靠 。没有人能保证你的数据一定能到达,而且数据在到达之前就可能出现混乱。如果你用 IP 发送大量消息,不要惊讶只有一半能收到,而且有些消息的顺序与发送顺序不一致,有些消息甚至被替换成了其他消息,这些消息可能包含可爱的小猩猩图片,但更有可能的是,它们只是一堆乱码,就像你收到的那种外语垃圾邮件一样。

Here’s the magic part: TCP is built on top of IP. In other words, TCP is obliged to somehow send data reliably using only an unreliable tool.
神奇之处在于:TCP 是建立在 IP 之上的。换句话说,TCP 必须 使用一种不可靠的工具来 可靠地发送数据。

To illustrate why this is magic, consider the following morally equivalent, though somewhat ludicrous, scenario from the real world.
为了说明为什么这是魔法,请考虑以下来自现实世界的、道德上等同但又有些荒谬的情景。

Imagine that we had a way of sending actors from Broadway to Hollywood that involved putting them in cars and driving them across the country. Some of these cars crashed, killing the poor actors. Sometimes the actors got drunk on the way and shaved their heads or got nasal tattoos, thus becoming too ugly to work in Hollywood, and frequently the actors arrived in a different order than they had set out, because they all took different routes. Now imagine a new service called Hollywood Express, which delivered actors to Hollywood, guaranteeing that they would (a) arrive (b) in order © in perfect condition. The magic part is that Hollywood Express doesn’t have any method of delivering the actors, other than the unreliable method of putting them in cars and driving them across the country. Hollywood Express works by checking that each actor arrives in perfect condition, and, if he doesn’t, calling up the home office and requesting that the actor’s identical twin be sent instead. If the actors arrive in the wrong order Hollywood Express rearranges them. If a large UFO on its way to Area 51 crashes on the highway in Nevada, rendering it impassable, all the actors that went that way are rerouted via Arizona and Hollywood Express doesn’t even tell the movie directors in California what happened. To them, it just looks like the actors are arriving a little bit more slowly than usual, and they never even hear about the UFO crash.
想象一下,我们曾经有一种方法,把百老汇的演员送到好莱坞,那就是把他们塞进车里,开车横跨美国。有些车出了车祸,可怜的演员们因此丧命。有时,演员们会在路上喝得酩酊大醉,剃光头或者纹上鼻梁纹身,结果变得太丑,无法在好莱坞发展。而且,演员们到达的顺序经常和出发时的顺序不一样,因为他们走的路线各不相同。现在想象一下,有一种叫做“好莱坞快车”的新服务,专门负责把演员送到好莱坞,并保证他们(a)准时到达,(b)按顺序到达,(c)身体状况完美。神奇之处在于,“好莱坞快车”除了把演员塞进车里开车横跨美国这种不太可靠的方式之外,没有任何其他运送演员的方法。“好莱坞快车”的运作方式是:检查每位演员是否完好无损地到达,如果发现有演员身体不适,就打电话给总部,要求派他的孪生兄弟代替。如果演员到达的顺序错了,“好莱坞快车”也会重新安排他们的顺序。如果一架大型不明飞行物在前往 51 区的途中坠毁在内华达州的高速公路上,导致道路无法通行,所有原计划前往 51 区的演员都会被改道绕道亚利桑那州,而好莱坞快运公司甚至不会告知加州的电影导演们发生了什么。在他们看来,这只是演员们到达的速度比平时稍慢了一些,他们根本不会 知道 不明飞行物坠毁的消息。

That is, approximately, the magic of TCP. It is what computer scientists like to call an abstraction: a simplification of something much more complicated that is going on under the covers. As it turns out, a lot of computer programming consists of building abstractions. What is a string library? It’s a way to pretend that computers can manipulate strings just as easily as they can manipulate numbers. What is a file system? It’s a way to pretend that a hard drive isn’t really a bunch of spinning magnetic platters that can store bits at certain locations, but rather a hierarchical system of folders-within-folders containing individual files that in turn consist of one or more strings of bytes.
这大致就是 TCP 的奥妙所在。计算机科学家喜欢称之为 抽象 :它简化了底层运行的复杂机制。事实上,很多计算机编程工作都是构建抽象层。什么是字符串库?它是一种让计算机能够像处理数字一样轻松地处理字符串的方法。什么是文件系统?它是一种让硬盘驱动器看起来不像一堆旋转的磁性盘片,而是一个层级分明的系统,其中包含文件夹,每个文件夹又包含一个文件,而每个文件又由一个或多个字节字符串组成的方法。

Back to TCP. Earlier for the sake of simplicity I told a little fib, and some of you have steam coming out of your ears by now because this fib is driving you crazy. I said that TCP guarantees that your message will arrive. It doesn’t, actually. If your pet snake has chewed through the network cable leading to your computer, and no IP packets can get through, then TCP can’t do anything about it and your message doesn’t arrive. If you were curt with the system administrators in your company and they punished you by plugging you into an overloaded hub, only some of your IP packets will get through, and TCP will work, but everything will be really slow.
回到 TCP 的话题。之前为了简单起见,我撒了个小谎,现在你们有些人可能已经气得冒烟了,因为这个谎言快把你们逼疯了。我说 TCP 能保证你的信息一定能送达。实际上并非如此。如果你的宠物蛇咬断了连接你电脑的网线,导致 IP 数据包 无法 通过,那么 TCP 也无能为力,你的信息自然也送不到。如果你对公司的系统管理员态度不好,他们惩罚你把你的电脑插到一个过载的集线器上,那么只有一部分 IP 数据包能通过,TCP 虽然还能工作,但网络速度会非常慢。

This is what I call a leaky abstraction. TCP attempts to provide a complete abstraction of an underlying unreliable network, but sometimes, the network leaks through the abstraction and you feel the things that the abstraction can’t quite protect you from. This is but one example of what I’ve dubbed the Law of Leaky Abstractions:
这就是我所说的 “抽象泄漏” 。TCP 试图对底层不可靠的网络进行完全抽象,但有时,网络会穿透这种抽象,让你感受到抽象无法完全保护你免受其害的种种问题。这只是我称之为“抽象泄漏定律”的一个例子:

All non-trivial abstractions, to some degree, are leaky. 所有非平凡的抽象在某种程度上都是有缺陷的。

Abstractions fail. Sometimes a little, sometimes a lot. There’s leakage. Things go wrong. It happens all over the place when you have abstractions. Here are some examples.
抽象会失效。有时失效程度较轻,有时则很严重。会出现信息泄漏。事情会出错。当你使用抽象时,这种情况会无处不在。以下是一些示例。

  • Something as simple as iterating over a large two-dimensional array can have radically different performance if you do it horizontally rather than vertically, depending on the “grain of the wood” — one direction may result in vastly more page faults than the other direction, and page faults are slow. Even assembly programmers are supposed to be allowed to pretend that they have a big flat address space, but virtual memory means it’s really just an abstraction, which leaks when there’s a page fault and certain memory fetches take way more nanoseconds than other memory fetches.
    即使是像遍历一个大型二维数组这样简单的操作,如果采用水平方向而非垂直方向,性能也会截然不同,这取决于“数组的纹理”——一个方向可能导致比另一个方向多得多的缺页,而缺页本身就很慢。即使是汇编程序员,也被允许假装他们拥有一个大的扁平地址空间,但虚拟内存实际上只是一种抽象,当发生缺页时就会出现内存泄漏,而且某些内存读取操作所需的时间会比其他操作长得多。
  • The SQL language is meant to abstract away the procedural steps that are needed to query a database, instead allowing you to define merely what you want and let the database figure out the procedural steps to query it. But in some cases, certain SQL queries are thousands of times slower than other logically equivalent queries. A famous example of this is that some SQL servers are dramatically faster if you specify “where a=b and b=c and a=c” than if you only specify “where a=b and b=c” even though the result set is the same. You’re not supposed to have to care about the procedure, only the specification. But sometimes the abstraction leaks and causes horrible performance and you have to break out the query plan analyzer and study what it did wrong, and figure out how to make your query run faster.
    SQL 语言旨在抽象化查询数据库所需的具体步骤,您只需定义所需内容,数据库会自动处理查询过程。但在某些情况下,某些 SQL 查询的速度会比其他逻辑等效的查询慢数千倍。一个著名的例子是,即使结果集相同,某些 SQL 服务器在指定“where a=b and b=c and a=c”时的速度也会比仅指定“where a=b and b=c”时快得多。您本不应该关心具体过程,而应该只关心查询语句。但有时这种抽象会泄露性能,导致糟糕的性能,这时您就不得不使用查询计划分析器来研究问题所在,并找出提升查询速度的方法。
  • Even though network libraries like NFS and SMB let you treat files on remote machines “as if” they were local, sometimes the connection becomes very slow or goes down, and the file stops acting like it was local, and as a programmer you have to write code to deal with this. The abstraction of “remote file is the same as local file” leaks. Here’s a concrete example for Unix sysadmins. If you put users’ home directories on NFS-mounted drives (one abstraction), and your users create.forward files to forward all their email somewhere else (another abstraction), and the NFS server goes down while new email is arriving, the messages will not be forwarded because the.forward file will not be found. The leak in the abstraction actually caused a few messages to be dropped on the floor.
    尽管像 NFS 和 SMB 这样的网络库允许你将远程机器上的文件“当作”本地文件来处理,但有时连接会变得非常慢甚至中断,导致文件不再像本地文件那样工作。作为程序员,你必须编写代码来处理这种情况。“远程文件与本地文件相同”的抽象概念存在 漏洞 。这里有一个针对 Unix 系统管理员的具体例子。如果你将用户的家目录放在 NFS 挂载的驱动器上(一种抽象),并且你的用户创建了.forward 文件来将所有电子邮件转发到其他地方(另一种抽象),而当 NFS 服务器在新邮件到达时宕机,由于找不到.forward 文件,邮件将无法转发。这种抽象概念的漏洞实际上导致了一些邮件丢失。
  • C++ string classes are supposed to let you pretend that strings are first-class data. They try to abstract away the fact that strings are hard and let you act as if they were as easy as integers. Almost all C++ string classes overload the + operator so you can write s + “bar” to concatenate. But you know what? No matter how hard they try, there is no C++ string class on Earth that will let you type “foo” + “bar”, because string literals in C++ are always char*’s, never strings. The abstraction has sprung a leak that the language doesn’t let you plug. (Amusingly, the history of the evolution of C++ over time can be described as a history of trying to plug the leaks in the string abstraction. Why they couldn’t just add a native string class to the language itself eludes me at the moment.)
    C++ 字符串类旨在让你把字符串当作一等公民来处理。它们试图抽象掉 字符串本身难以处理 的事实,让你像处理整数一样轻松地操作它们。几乎所有 C++ 字符串类都重载了 + 运算符,这样你就可以写出 s + “bar” 来进行字符串连接。但是你知道吗?无论他们如何努力,地球上没有任何一个 C++ 字符串类允许你输入 “foo” + “bar” ,因为 C++ 中的字符串字面量始终是 char* 类型,而不是字符串。这种抽象机制出现了一个语言无法修复的漏洞。(有趣的是,C++ 的发展历程可以被描述为一部不断尝试修复字符串抽象漏洞的历史。我目前仍然不明白为什么他们不能直接在语言中添加一个原生的字符串类。)
  • And you can’t drive as fast when it’s raining, even though your car has windshield wipers and headlights and a roof and a heater, all of which protect you from caring about the fact that it’s raining (they abstract away the weather), but lo, you have to worry about hydroplaning (or aquaplaning in England) and sometimes the rain is so strong you can’t see very far ahead so you go slower in the rain, because the weather can never be completely abstracted away, because of the law of leaky abstractions.
    下雨天你开不了快车,即使你的车有雨刷器、前大灯、车顶和暖气,所有这些都让你不必在意下雨的事实(它们让你感觉不到天气的存在),但是,你还是得担心水滑(在英国是水滑),有时雨势太大,能见度很低,所以你只能在雨中慢行,因为天气永远无法被完全消除,这是因为抽象的泄漏定律。

One reason the law of leaky abstractions is problematic is that it means that abstractions do not really simplify our lives as much as they were meant to. When I’m training someone to be a C++ programmer, it would be nice if I never had to teach them about char*’s and pointer arithmetic. It would be nice if I could go straight to STL strings. But one day they’ll write the code “foo” + “bar”, and truly bizarre things will happen, and then I’ll have to stop and teach them all about char*’s anyway. Or one day they’ll be trying to call a Windows API function that is documented as having an OUT LPTSTR argument and they won’t be able to understand how to call it until they learn about char*’s, and pointers, and Unicode, and wchar_t’s, and the TCHAR header files, and all that stuff that leaks up.
抽象泄漏定律之所以存在问题,其中一个原因在于它意味着抽象并没有像预期那样简化我们的生活。当我培训 C++ 程序员时,我当然希望永远不必教他们 char* 和指针运算。如果我可以直接讲解 STL 字符串就好了。但总有一天,他们会写出类似 “foo”+“bar”这样 的代码,然后就会出现一些非常奇怪的问题,到那时我还是得停下来,给他们讲解 char* 的相关知识。或者,总有一天,他们会尝试调用一个 Windows API 函数,该函数的文档中明确指出它有一个 OUT LPTSTR 类型的参数,但他们只有先了解 char*、指针、Unicode、wchar_t、TCHAR 头文件以及所有这些会泄漏出来的抽象概念,才能理解如何调用它。

In teaching someone about COM programming, it would be nice if I could just teach them how to use the Visual Studio wizards and all the code generation features, but if anything goes wrong, they will not have the vaguest idea what happened or how to debug it and recover from it. I’m going to have to teach them all about IUnknown and CLSIDs and ProgIDS and … oh, the humanity!
教别人 COM 编程的时候,如果我能直接教他们怎么用 Visual Studio 的向导和所有代码生成功能就好了,但如果出了什么问题,他们根本不知道发生了什么,也不知道怎么调试和恢复。我得教他们 IUnknown、CLSID、ProgID 等等……唉,真是太折磨人了!

In teaching someone about ASP.NET programming, it would be nice if I could just teach them that they can double-click on things and then write code that runs on the server when the user clicks on those things. Indeed ASP.NET abstracts away the difference between writing the HTML code to handle clicking on a hyperlink () and the code to handle clicking on a button. Problem: the ASP.NET designers needed to hide the fact that in HTML, there’s no way to submit a form from a hyperlink. They do this by generating a few lines of JavaScript and attaching an onclick handler to the hyperlink. The abstraction leaks, though. If the end-user has JavaScript disabled, the ASP.NET application doesn’t work correctly, and if the programmer doesn’t understand what ASP.NET was abstracting away, they simply won’t have any clue what is wrong.
在教授 ASP.NET 编程时,如果我能直接告诉别人如何双击元素,然后编写代码在用户点击这些元素时在服务器端运行,那就太好了。事实上,ASP.NET 已经抽象化了编写处理超链接点击 事件 的 HTML 代码和处理按钮点击事件的代码之间的区别。问题在于:ASP.NET 的设计者需要隐藏 HTML 中无法通过超链接提交表单这一事实。他们通过生成几行 JavaScript 代码并将 onclick 事件处理程序附加到超链接上来实现这一点。然而,这种抽象存在漏洞。如果最终用户禁用了 JavaScript,ASP.NET 应用程序将无法正常工作;如果程序员不理解 ASP.NET 抽象化的内容,他们就根本不知道问题出在哪里。

The law of leaky abstractions means that whenever somebody comes up with a wizzy new code-generation tool that is supposed to make us all ever-so-efficient, you hear a lot of people saying “learn how to do it manually first, then use the wizzy tool to save time.” Code generation tools which pretend to abstract out something, like all abstractions, leak, and the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don’t save us time learning.
抽象泄漏定律指的是,每当有人发明出一种号称能大幅提升效率的炫酷代码生成工具时,总会有人说“先学会手动编写代码,然后再用这个炫酷工具节省时间”。代码生成工具,就像所有抽象一样,都会出现泄漏。而有效应对这些泄漏的唯一方法,就是学习抽象的工作原理以及它们抽象的内容。所以,抽象可以节省我们工作的时间,但并不能节省我们学习的时间。

And all this means that paradoxically, even as we have higher and higher level programming tools with better and better abstractions, becoming a proficient programmer is getting harder and harder.
这一切都意味着,矛盾的是,即使我们拥有越来越高级、抽象越来越好的编程工具,成为一名熟练的程序员也变得越来越难。

During my first Microsoft internship, I wrote string libraries to run on the Macintosh. A typical assignment: write a version of strcat that returns a pointer to the end of the new string. A few lines of C code. Everything I did was right from K&R — one thin book about the C programming language.
在我第一次微软实习期间,我编写了一些可以在 Macintosh 上运行的字符串库。一个典型的任务是:编写一个 strcat 函数,使其返回指向新字符串末尾的指针。只需要几行 C 代码。我所有的代码都直接来自 K&R——一本关于 C 语言的薄薄的书。

Today, to work on CityDesk, I need to know Visual Basic, COM, ATL, C++, InnoSetup, Internet Explorer internals, regular expressions, DOM, HTML, CSS, and XML. All high level tools compared to the old K&R stuff, but I still have to know the K&R stuff or I’m toast.
如今,要使用 CityDesk,我需要掌握 Visual Basic、COM、ATL、C++、InnoSetup、Internet Explorer 内部机制、正则表达式、DOM、HTML、CSS 和 XML。这些都是比以前的 K&R 工具高级得多的工具,但我仍然必须了解 K&R 的知识,否则就完蛋了。

Ten years ago, we might have imagined that new programming paradigms would have made programming easier by now. Indeed, the abstractions we’ve created over the years do allow us to deal with new orders of complexity in software development that we didn’t have to deal with ten or fifteen years ago, like GUI programming and network programming. And while these great tools, like modern OO forms-based languages, let us get a lot of work done incredibly quickly, suddenly one day we need to figure out a problem where the abstraction leaked, and it takes 2 weeks. And when you need to hire a programmer to do mostly VB programming, it’s not good enough to hire a VB programmer, because they will get completely stuck in tar every time the VB abstraction leaks.
十年前,我们或许会认为新的编程范式早已让编程变得更容易。的确,这些年来我们创建的抽象概念 确实 让我们能够应对软件开发中前所未有的复杂度,例如图形用户界面(GUI)编程和网络编程,而这些复杂度在十年前甚至十五年前是无法想象的。虽然这些强大的工具,例如现代面向对象的表单语言,能够让我们以惊人的速度完成大量工作,但突然有一天,我们需要解决抽象概念泄露的问题,而这却需要花费两周的时间。当你需要雇佣一名程序员来主要从事 VB 编程时,仅仅雇佣一名 VB 程序员是远远不够的,因为一旦 VB 抽象概念泄露,他们就会陷入彻底的困境。

The Law of Leaky Abstractions is dragging us down.
抽象概念泄漏定律正在拖垮我们。