21
Programming as theory building is true now more than ever

2026-02-21 · 1446 words

These past two days have been something of a whirlwind, as I made a couple of important life decisions. The decisions were, in a strange way, very small, but in a “marble rolling down a saddle” sort-of way that might compound and drastically change the side of the saddle my timeline will spill down… we shall see!

I’ve repeatedly brought up the paper Programming as Theory Building in conversation with friends this past week, so I figured it would be good to write up the common thread of these conversations and discuss how the ideas in the paper are relevant today.

The paper was written in 1985 by Peter Naur. The main thrust of the argument is that when you program, you are doing two things:

  1. Producing the artifact of text, or source code, on disk.
  2. Producing the model of a system in your mind. This is “theory building”.

Programming, Naur argues, should be framed as theory building. The real value of what is produced is precisely the model that lives in the programmer’s mind. The mental model is what allows you to debug, extend, and collaborate on a codebase; not the source code.

Why the theory building angle is useful

I really like the way a few insights that naturally follow as a consequence of thinking of programming as theory building. I’ll run through them now.

First, a bug occurs when your mental model of the system’s behavior disagrees with what the code does. As programs do exactly what you write down, a bug isn’t wrong code; it’s doing exactly what you wrote. A bug happens when your code doesn’t match your theory, or your theory doesn’t match the code. Fixing a bug, then, requires either refining your mental model or refining the code. Often it’s both. In this sense, to a customer, if an app doesn’t work the way they expect it to, that’s a bug, to them. UX bugs occur when we do a poor job of theory building with our user.

Second, source code is a medium of reflection against which further theory can be built. When you have an abstract notion of a system, you can only hold so much of the theory in your head, or in design documents. Producing source code grounds theory, and allows the programmer to explore and discover the right way to extend their existing theories, so new systems can be developed and features can be implemented.

Finally, theory building provides a good framework to think about working on code with others. As the source code is not the artifact, and rather the theory is, a lot of programming on a team boils down to communicating the theory you have built up, and asking good questions to learn the theory your teammates have built up. Different team members hold expert theories for different parts of the codebase. Becase of this, interfaces are clean boundaries where you can communicate a crisp and neatly-packaged theory to a teammate, which packages complexity to become a sharp tool with which to carve whatever code they need to write. Documentation, pair programming, demonstrations are all means through which a body of theory is built and communicated.

Pair programming is great for theory building

In high school, I wrote a lot of code on my own. The first time I really had the opportunity to work on a team on projects with a bus factor bigger than one was when I was an intern at Zed, in 2022. I spent a lot of time pair programming with Antonio, who taught me a lot about Rust and software engineering.

I now realize that Antonio spent so much time pair programming with me because he was very invested in theory building: both in improving my understanding of the theory behind the editor and the tools we were using, and in improving his understanding of the code I was writing, so he could carry the theory we built together forward after my internship ended.

I’m really grateful for this mentorship and I still think back to a lot of the lessons I learned about e.g. async Rust minutiae Antonio patiently taught me along the way.

Vibecoding is antithetical to theory building

Now, the AI tie in. Great. Sure. I saw it coming from a mile away. Here it is: Sometimes AI tools can produce a better code artifact; however, better code that was generated doesn’t help you, as the programmer, to build a better theory.

Taking a step back, the idea of programming as theory building doesn’t materially change because of AI tools that write code now exist. (A follow-on paper that thoroughly investigates the effects of AI tooling on theory building would be interesting to read.) The issue is that it’s easy to convince yourself that you’ve built theory when you haven’t: It’s easy to fall into the trap or classical fantasy of “texting your nerdy eager-to-please friend an app idea and watching it get written”.

First, if you want to be engaged in building theory when using tools, you need to be engaged. If you treat tools as teammates, they will invariably go off and build theory on their own. The model will not go out of its way to teach you its assumptions. You need to understand the theory that has been generated if you wish to extend the artifact of the code.

Second, AI tools have no qualms picking interfaces and invariants for you. The interfaces they choose will often be those interfaces that are the most plausible for what you are trying to do. However, these interfaces are not always the best interfaces for whatever it is that you are specifically working on. Sometimes they’re worse. Sometimes they’re better! Regardless of ontological merit, any interface sucks if you don’t understand it.

Finally, for both of these issues, it is not enough to simply read and write more documentation, as Naur argued back in 1985. Documentation is a poor substitute for theory. AI generated documentation for AI generated code does not necessarily build the right theory in your mind.

Lessons for collaborating on code

With these problems in mind, what can we do? I write here in the context of AI tools that write code, but this is true of collaboration with human teammates as well. (A lot of these ideas are lessons I indirectly learned through Antonio, though we were not using AI generated code at the time.) I’ve learned a few things:

First, you should really care about good, pedagogical documentation and good API design. When you design an interface, you have the opportunity to both build your theory of the system and specify the exact interfaces and invariants you require (instead of letting a teammate, or some other collaborator, choose for you). I find that writing system documentation, writing out types, writing an API, writing property-based tests by hand, before letting an AI implement what is left, produces better results.

Second, you should pay extra attention and slow down when you realize your theory for something you are working on is slipping. When working with AI collaborators, it is easy for experienced engineers to vibe-code a simple app, because the theory is something they already understand fully. The issue arises when a programmer then starts working on extending or developing new aspects of the system without having the symbolic grounding to close that feedback loop, and develop the theory you require.

Finally, you can always ask these tools, as you would a colleague, to be watchful of when your theory is slipping, and to help you understand what you’re missing. AI tools can generate lots of ungrounded theory, yes, but they can also work to answer questions and determine the correctness of your existing theories.

One last note

Importantly, while talking about theory building in the context of AI tools, I should mention that I believe it to be bad to “go off to build thought palaces alone”. Humans communicate differently with other people than they communicate with AI. Having real-life people care about what you’re working on, anxiously engaged with your code, provides the human perspective you miss out on when writing software alone (or with a tool).

That’s it for today! I probably won’t blog tomorrow because it’s Sunday. If you’d like a story about someone who wrote no code but was great at collaborative theory building, you should read:

Padded so you can keep scrolling. I know. I love you. How about we take you back up to the top of this page? All prose on this website is written by me, Isaac. I feel very strongly about preserving my voice, and will not use AI to publish prose under my name.