sean goedecke

In defense of not understanding your codebase为不理解你的代码库辩护

As a software engineer, how well do you have to understand your own codebase?作为一个软件工程师,你需要对自己的代码库理解到什么程度?

My guess is that people who work on small codebases with low-turnover teams (say, Redis or games like The Witness) would say “obviously you have to understand it completely, otherwise you can’t do good work”. I’d also guess that people who work on large codebases with high-turnover teams (say, the Google web search backend or GitHub) would say “obviously you can’t understand it completely, you just have to do the best you can in your local area”.我的猜测是,那些在小型代码库、低人员流动率的团队工作的人(比如 Redis 或者《见证者》这样的游戏)会说:“显然你必须完全理解它,否则你做不出好东西。”我还猜测,那些在大型代码库、高人员流动率的团队工作的人(比如 Google 网页搜索后端或者 GitHub)会说:“显然你不可能完全理解它,你只能在你的局部区域尽力而为。”

These are two largely different ways of programming with different methods, practices and cultures1. However, the first group is over-represented in online discussion about software engineering2. I want to defend the second group against the first. In many software engineering environments, there’s nothing wrong with being in a state of partial understanding. In fact, in large systems a partial understanding is the best you can do.这是两种截然不同的编程方式,方法、实践和文化都有所不同¹。然而,第一种群体在关于软件工程的在线讨论中占比过高²。我想为第二种群体辩护。在许多软件工程环境中,处于部分理解的状态并没有什么不对。实际上,在大型系统中,部分理解就是你能做到的最好程度。

Against “programming as theory building”驳“编程即理论构建”

The best articulation of the “you have to understand your codebase” side is Peter Naur’s famous paper Programming as Theory Building. I like this paper, but I think it goes too far in that direction. Naur’s core point is that when programmers work on a program, the code is really just a by-product, and the main product they’re working on is their “theory of the program”. That’s made up of their intuitive sense of what’s happening and why, which can only be partially captured by code or documentation. If they lost the code, they could rewrite the program easily. If they lost their understanding (say, if the team experienced 100% turnover), they would struggle to make sense of the code.“你必须理解你的代码库”这一派最著名的表述来自 Peter Naur 的著名论文《编程即理论构建》。我喜欢这篇论文,但我认为它在那个方向上走得太远了。Naur 的核心观点是,当程序员在程序上工作时,代码实际上只是一个副产品,他们的主要产品是他们对程序的“理论”。这个理论由他们对正在发生的事情及其原因的直觉感受组成,这种感受只能被部分地捕捉到代码或文档中。如果他们丢失了代码,他们可以轻松地重写程序。如果他们丢失了理解(比如团队经历 100% 的人员流动),他们将难以理解代码。

So far, so good, but Naur goes further than this. He says that the theory should not be reconstructed from the code. According to Naur, you’re better off scrapping the program entirely and having a new team rebuild it from scratch, building up a new theory in the process3:到目前为止,一切都很好,但 Naur 走得更远。他说,理论不应该从代码中重建。根据 Naur,你最好完全抛弃程序,让一个新团队从头开始重建,在这个过程中建立一个新的理论³:

reestablishing the theory of a program merely from the documentation, is strictly impossible … [therefore] the existing program text should be discarded and the new-formed programmer team should be given the opportunity to solve the given problem afresh“仅从文档重新建立程序的理论是绝对不可能的……因此,现有的程序文本应该被丢弃,新组建的程序员团队应该被给予从头解决给定问题的机会”

Anyone who’s been an effective software engineer at a large company knows that Naur is dead wrong about this. There are at least two reasons.任何在大公司做过有效软件工程师的人都知道 Naur 在这方面大错特错。这至少有两个原因。

First, you simply can’t rebuild large software systems from scratch. Sufficiently large systems (if they have users) contain thousands of weird cases and quirks that cannot be reimplemented. Even a team that’s intimately familiar with the system couldn’t do it: there’s just too much stuff to juggle. Successful rewrites always start by carving out the existing codebase into small isolated chunks, then rewriting one chunk at a time. In other words, rewriting a software system involves making a bunch of changes to the old system. If you can’t change the old system, you certainly can’t replace it with a new one.首先,你根本无法从头重建大型软件系统。足够大的系统(如果有用户)包含成千上万的奇怪案例和特性,无法重新实现。即使是一个非常熟悉该系统的团队也无法做到:要处理的东西太多了。成功的重写总是从将现有代码库分割成小的孤立块开始,然后一次重写一个块。换句话说,重写一个软件系统涉及到对旧系统进行一系列修改。如果你不能改动旧系统,你当然不可能用一个新的系统来替换它。

Second, abandoned systems are revived all the time. In a tech company with hundreds of millions of lines of code and thousands of engineers, it’s not uncommon for a codebase to have nobody left who’s familiar with it4. All it takes is a few people to quit at the wrong time, or for a codebase to be unmaintained for a year. Not only have I seen other teams do this, I have personally taken ownership of abandoned codebases, figured them out, and gotten to a point where I could effectively work with them. It takes time, but building a new theory of the codebase is possible. You start by understanding one flow end-to-end, then slowly branch out from there, making careful changes as you go.其次,被遗弃的系统经常被复活。在一家拥有数亿行代码和数千名工程师的科技公司,一个代码库没有任何人熟悉它的情况并不少见⁴。只需在错误的时间有几个人辞职,或者一个代码库一年无人维护。我不仅看到过其他团队这样做,我本人也曾接手被遗弃的代码库,弄懂了它们,并达到了能够有效处理它们的程度。这需要时间,但构建一个新的代码库理论是可能的。你从理解一个完整的流程开始,然后慢慢向外扩展,边做边小心地修改。

In sufficiently large codebases, everyone operates with an incorrect theory of the program. The defining feature of modern software systems is that they’re just way too big for anyone (or even a whole team) to keep in their head: nobody understands it all. To be effective, you have to figure out a way to work with a merely partially-correct theory. This is why I keep going on about taking a position and confidence. If you’re not sure about something, you can’t just sit back and wait for someone with a perfect understanding to come and give you the answer. If you’re a competent engineer, that person is you. You have to grit your teeth, make your most educated guess, and then deal with the consequences.在足够大的代码库中,每个人都在用一个不正确的程序理论工作。现代软件系统的定义性特征是它们太大,以至于没有人(甚至整个团队)能装进脑子里:没有人能理解全部。为了有效工作,你必须找到一种方式,用仅仅部分正确的理论来工作。这就是为什么我一直强调采取立场和信心。如果你不确定某件事,你不能只是坐着等待一个完全理解的人来给你答案。如果你是一个称职的工程师,那个人就是你自己。你必须咬紧牙关,做出你最合理的猜测,然后处理后果。

To be generous to Naur, it’s possible that in 1985 the average size of a program was several orders of magnitude smaller than today, and that when Naur writes about “large programs” he’s not talking about tens of millions of lines of code. Naur’s first example of a large program is a 200,000 line industrial monitoring program, and his second example is a compiler. In 1987, the first version of the compiler GCC was about a hundred thousand lines of code; in 2015 GCC was over fourteen million lines. I can believe that rewriting one or two hundred thousand lines of code is relatively straightforward, particularly if you get to reuse existing tests. Not so for one or two million.对 Naur 宽容一点,可能 1985 年时程序的平均大小比现在小几个数量级,当 Naur 谈论“大型程序”时,他指的不是几千万行代码。Naur 的第一个大型程序示例是一个 20 万行的工业监控程序,第二个示例是一个编译器。1987 年,编译器 GCC 的第一个版本大约有十万行代码;到 2015 年,GCC 超过了 1400 万行代码。我相信重写一两万行代码相对简单,特别是如果你能重用现有的测试。但一两百万行就不一样了。

Theory building is one tradeoff among many理论构建只是众多权衡之一

LLMs are often cited as a tool that’s bad because it impedes the ordinary process of theory-building. I think this is overly simplistic. Like many software tools, LLMs are a double-edged sword: they make it harder to construct a detailed mental theory of the software, but they allow you to build a partial theory quickly and they can help you leverage that partial theory more effectively. This is a complex tradeoff that I’m still thinking about.LLM 经常被引述为一种不好的工具,因为它妨碍了通常的理论构建过程。我认为这过于简单化了。像许多软件工具一样,LLM 是一把双刃剑:它们使得构建软件的详细心智理论变得更加困难,但它们允许你快速构建一个部分理论,并帮助你更有效地利用那个部分理论。这是一个复杂的权衡,我还在思考。

Setting LLMs aside, I’m confident that it’s silly to say that anything that interferes with your theory of the software must be bad. Here is a partial list of other things that make it harder to maintain a theory:撇开 LLM 不谈,我确信说任何干扰你对软件理论的东西都一定不好是愚蠢的。这里有一份其他妨碍理论维持的事物的部分列表:

  • Other people being allowed to write code in your codebase允许其他人往你的代码库里写代码
  • Having to implement legally-required features like accessibility and data protection必须实现法律要求的功能,比如可访问性和数据保护
  • Allowing your colleagues to quit their jobs or move between teams允许你的同事辞职或在团队之间调动
  • Having to upgrade software versions for security patches必须升级软件版本以获得安全补丁
  • Bringing in libraries or other dependencies引入库或其他依赖

Like most things in software, “maintaining a theory of the codebase” is one value among many. Sometimes it’s the most important value and you sacrifice other values for it; other times you trade it off for speed, or legal compliance, or for political reasons5.像软件领域的大多数事情一样,“维持代码库理论”只是众多价值之一。有时它是最重要的价值,你会为它牺牲其他价值;其他时候你为了速度、法律合规或政治原因而牺牲它⁵。

Almost all engineers — particularly “pure” engineers — prefer to maintain an accurate mental model of their software. It’s more fun, less stressful, and feels more like “real engineering”. That’s why many engineers take up open-source projects in their spare time in order to work on small codebases by themselves: in order to do engineering work where they can maintain an accurate Naur theory of the codebase. I don’t think there’s anything wrong with that.几乎所有的工程师——尤其是“纯粹”的工程师——都更喜欢维护他们软件的准确心智模型。这更有趣,压力更小,感觉更像“真正的工程”。这就是为什么许多工程师在业余时间参与开源项目,以便在小型代码库上独自工作:为了做那种他们能维持准确 Naur 理论的工程工作。我不认为这有什么不对。

However, at work you are paid to do a job. In other words, they pay you money to adopt their set of engineering values. It’s hopefully well-understood that however much you might personally care about performance, sometimes you have to write slow code at your job (for instance, to get a project done on time, or to accommodate some awkward requirement). Maintaining a theory of the codebase is the same kind of thing. 然而,在工作中,你是拿钱干活的。换句话说,他们付钱让你接受他们那套工程价值。希望大家都清楚,无论你个人多么在意性能,有时你必须在工作中编写慢速代码(例如,为了按时完成项目,或者为了适应某些尴尬的需求)。维持代码库理论也是同样的事情。

edit: this post got some comments on lobste.rs. One interesting comment points out that the ability to reason “locally” about code (i.e. with a partial understanding) has been a core goal of CS from the beginning. This is also a good description of what I was trying to get at in How good engineers write bad code at big companies. Also, it’s amusing that this post was tagged as vibecoding because of one off-hand paragraph about LLMs. I still don’t think I’ll be tagging the post as AI on my blog.编辑:这篇帖子在 lobste.rs 上收到了一些评论。有一条有趣的评论指出,对代码进行“局部”推理(即部分理解)的能力从一开始就是计算机科学的核心目标之一。这也很好地描述了我之前在《优秀工程师如何在大公司写出烂代码》一文中试图表达的内容。另外,有趣的是,这篇帖子被标记为“vibecoding”,仅仅因为我有一小段关于 LLM 的段落。在我的博客上,我仍然不会给这篇帖子贴上 AI 的标签。


  1. I wrote about this at length in Pure and impure software engineering. I think many of the repeated arguments we have in the software industry are caused by the pure total-understanding culture coming up against the impure partial-understanding culture.我在《纯粹与不纯粹的软件工程》一文中对此进行了详细论述。我认为我们在软件行业中反复出现的许多争论,都是由纯粹的全理解文化与不纯粹的部分理解文化碰撞造成的。

  2. Open-source engineers are more excited to blog about their work, the raw engineering content is typically more impressive (because coordination problems dominate big proprietary systems), open-source projects can be legally written about while proprietary systems can’t, and even if you could do it legally, writing about large codebases is impossible because it requires too much specific context.开源工程师更愿意在博客上写他们的工作,原始的工程内容通常更令人印象深刻(因为协调问题主导了大型专有系统),开源项目可以合法地讨论而专有系统不能,而且即使你能合法地写,写关于大型代码库的文章也是不可能的,因为它需要太多特定的上下文。

  3. I re-read the relevant chapters of Ryle’s The Concept of Mind (which Naur cites throughout) and I think Ryle is more generous about theory-building. For Ryle, theory-building or know-how automatically happens as you do things. It’s fully consistent with Ryle to think you can pick up an existing codebase just from the code, purely by puzzling it out.我重新阅读了 Ryle 的《心的概念》的相关章节(Naur 全文引用),我认为 Ryle 对理论构建更加宽容。对 Ryle 来说,理论构建或“知道如何”在你做事时自动发生。完全符合 Ryle 的观点是,你可以仅仅从代码中接手一个现有的代码库,纯粹通过琢磨它来理解。

  4. Naur says: “Lest this consequence may seem unreasonable, it may be noted that the need for revival of an entirely dead program probably will rarely arise, since it is hardly conceivable that the revival would be assigned to new programmers without at least some knowledge of the theory had by the original team.”. If only!Naur 说:“为了避免这个后果看起来不合理,可以注意到,完全死掉的程序的重建几乎不会发生,因为让新程序员接手而丝毫不了解原团队的理论,这是很难想象的。”如果是这样就好了!

  5. Some engineers might say that maintaining a theory is the core value, because without it you can’t fulfill any of the others. I disagree. You could say the same thing about readability, or maintainability, or correctness, or a bunch of other engineering values. We trade off “core” values like this all the time.有些工程师可能会说,维持理论是核心价值,因为没有它你就无法满足其他所有价值。我不同意。你可以对可读性、可维护性、正确性或其他一堆工程价值说同样的话。我们一直在权衡这样的“核心”价值。


If you liked this post, consider subscribing to email updates about my new posts, or sharing it on Hacker News.如果你喜欢这篇帖子,考虑订阅邮件更新以获取我的新帖子,或者在 Hacker News 上分享。

Here's a preview of a related post that shares tags with this one.这里有一篇相关帖子的预览,它与这篇帖子共享标签。

Build agents, not pipelines构建智能体,而非管道

There are only two ways to use LLMs in a computer program: as part of a pipeline, or as an agent. In other words, either you express the control flow of the program in code, or you give a LLM tools and allow it to manage the control flow itself.在计算机程序中使用 LLM 只有两种方式:作为管道的一部分,或者作为智能体。换句话说,要么你用代码表达程序的控制流,要么你给 LLM 一些工具并允许它自己管理控制流。

Here’s how you might structure a trivial “summarize a bunch of information and email it to me” program as a pipeline:
Continue reading...
下面是你如何将一个简单的“汇总一堆信息并通过邮件发给我”的程序构建为管道:继续阅读...