Map
โ†‘Sean Goedecke's Blog

In Defense of Not Understanding Your Codebase

Wiki summarysoftware-engineeringllmcoding-agentsoftware-quality โ†ณ show in map Markdown
title
In Defense of Not Understanding Your Codebase
type
summary
summary
Goedecke's case that partial understanding is the honest baseline in large systems, and one engineering value traded against others
tags
software-engineering, llm, coding-agent, software-quality
created
2026-07-18
updated
2026-07-18

Sean Goedecke (see sean-goedecke-blog) splits engineers into two camps by the kind of codebase they work on. On small codebases with low team turnover โ€” Redis, a game like The Witness โ€” you can hold the whole thing in your head, and people there tend to say full understanding is non-negotiable. On large codebases with high turnover โ€” Google search, GitHub โ€” nobody keeps it all in their head, and people there accept that you work locally with a partial picture. The first camp writes most of the online discourse about engineering, so its standard gets treated as the standard. Goedecke wants to defend the second camp: partial understanding is not a failure state, it is the best available state in a big system.

His main target is Peter Naur's Programming as Theory Building. Goedecke agrees with Naur's central claim โ€” the real product an engineer builds is a mental theory of the program, and code is a lossy by-product of that theory. Where he breaks with Naur is the prescription that follows. Naur argued that a theory can't be reconstructed from code and documentation alone, so a team that has lost the theory should throw the program away and rebuild from scratch rather than try to revive it. Goedecke calls this dead wrong for large systems, on two grounds. You can't rewrite a big system from scratch because it holds thousands of quirks and edge cases nobody can re-derive; real rewrites proceed by carving the existing system into chunks and replacing one at a time, which is itself a long sequence of changes to the old code. And abandoned codebases get revived constantly โ€” he's personally taken over orphaned systems, learned one end-to-end flow, and branched out from there until he could work in them safely. Rebuilding a theory from the code is slow but possible, which is exactly what Naur said couldn't be done.

Goedecke's sharpest line is that in a sufficiently large codebase everyone runs on an incorrect theory of the program. Since you can't wait for someone with perfect knowledge to hand you the answer โ€” that person doesn't exist โ€” you make your most educated guess, act on it, and own the consequences. He also floats a size argument against Naur: in 1985 a "large program" was a few hundred thousand lines, small enough that a clean rewrite reusing the old tests was plausible. GCC went from roughly a hundred thousand lines in 1987 to over fourteen million by 2015. The theory-building advice may have aged out of its own scale assumptions.

The framing that ties it together: maintaining a theory of the codebase is one engineering value among many, not a sacred one. Plenty of ordinary things degrade your theory โ€” other people committing to the same code, mandated accessibility and data-protection work, colleagues changing teams, security upgrades, new dependencies โ€” and none of them are considered wrong. You trade the theory off against speed, legal compliance, or politics the same way you sometimes ship deliberately slow code to hit a deadline. "Pure" engineers prefer a complete mental model because it's more fun and feels more like real engineering, which Goedecke thinks is a fine reason to keep a small side project โ€” but at work you're paid to adopt the employer's set of values, and total understanding is not always top of that list.

Where LLMs come in

LLMs appear in one paragraph, near the end, almost as an aside โ€” and yet they're the reason the piece reads as an agent-era argument. The common complaint is that LLMs are bad because they interfere with theory-building. Goedecke calls that too simplistic: they're a double-edged tool that makes a deep mental model harder to form but lets you build a usable partial theory fast and act on it. He says he's still working out the tradeoff. Notably he refused to tag the post as AI on his own blog, and was annoyed that Lobsters filed it under vibecoding on the strength of that single paragraph โ€” a point the commenters seized on (see below). This is consistent with his other LLM writing, like steering-vectors-interesting-again, where the interest is in specific mechanisms rather than the general AI discourse.

The argument sits in productive tension with the skepticism collected elsewhere in this vault. skill-atrophy-supervision-paradox and agentic-coding-is-a-trap warn that heavy agent use erodes the code-reading and critical-thinking skills you need to supervise agents, so leaning on partial understanding is exactly the habit that hollows you out. Goedecke isn't really contradicting that. His claim is descriptive โ€” partial understanding is unavoidable at scale โ€” where the skill-atrophy worry is about a trajectory: partial understanding you built by reasoning through a system is not the same as partial understanding you accepted because an agent produced code you never read. His own revival stories cut against pure delegation: he learns an orphaned codebase by tracing one flow end-to-end before changing anything, which is the deliberate skill-building the critics want to preserve. The honest synthesis is that "you don't need to understand everything" and "you need to understand enough to reason locally and catch the agent when it's wrong" are both true, and the interesting question is where the floor sits. clean-code-coding-agents makes the adjacent point that code structure matters more with agents, not less, because a partial theory is only workable if the code supports local reasoning. 1password-agentic-refactoring is the concrete case: the durable win there came from agent-built deterministic analysis tools and a chunk-at-a-time decomposition, not from agents writing code nobody understood โ€” the same carve-it-up method Goedecke describes for rewrites.

Interesting from the discussion

The Lobsters thread pushed on the argument from several angles.

The top comment (typesanitizer) argued the title oversells a milder point โ€” "you need to make progress with partial understanding in large codebases" โ€” and that theory-building isn't actually in tension with it once you separate a theory's breadth (how many parts you can answer questions about) from its depth (how complex a change you can make to one part while keeping it correct). Rewriting a module is building a narrow-but-deep theory of that module. He also flagged that Naur assumes the "given problem" still exists as a spec, when for most large codebases the closest surviving artifact is the test suite, and that continuous delivery โ€” holding a pager for an always-running service โ€” changes the rewrite calculus in ways 1985 didn't have to weigh.

mcherm connected the piece to local reasoning, the ability to work on one part without understanding the whole, and argued CS has chased exactly this from the start: structured programming, avoiding globals, functional programming's elimination of side effects, and OO all exist largely to make local reasoning possible. That reframes Goedecke's point as old news dressed in agent-era clothes โ€” partial understanding works because decades of language design went into making it work.

jmillikin added the mobility angle: even a 300,000-line expert may be dropped into a different 300,000-line codebase next week, so you survive on shared idioms, tooling that fails loudly when you misuse an API, and instinct about how large programs should be shaped โ€” and there's little point maintaining understanding of codebases you've left. He drew a useful line between expertise (how a parser works, how to write one without introducing vulnerabilities) and trivia (which file build_huffman_tables() lives in). mxey pushed back that this undersells specialized expertise, and that the real LLM worry isn't whether one person holds the whole codebase but whether the code is even written so a human can still work on it โ€” thousands of unreviewed AI-generated commits being the failure mode.

andyc granted the point but warned against nihilism: partial understanding is the only possible state at scale, yet institutions should keep striving for more understanding, and "you're paid to do a job" shouldn't become cover for shipping rot because you hold a monopoly (Windows and macOS offered as systems arguably rotting from accumulated complexity). alurm countered that "always strive for more understanding" can be weaponized into immobility, where nothing ships because good-enough is never allowed. coxley made the operational version: the gap between assumed and correct theory widens on its own as a codebase grows, and the common cost is teams running "false experiments" โ€” trying X, having it fail because the system wasn't set up for X to make sense yet, and wrongly concluding X doesn't work.

A large sub-thread was pure meta: whether the vibecoding tag belonged on a post that only mentions LLMs once. simonw's aside โ€” that the tag makes more sense read as a trigger warning than a topic label โ€” got the most upvotes in that branch, which says something about how the Lobsters audience currently relates to LLM content.