Someone put together a clean collection of software engineering laws recently, and it hit the top of Hacker News with over 700 points. The comments section did what HN does: half the thread argued about whether these are really “laws,” the other half added more obscure ones, and a few people wrote thoughtful rebuttals.
I’ve been thinking about why this kind of list keeps getting traction. We’ve had Brooks’ Law since 1975 and Conway’s Law since 1967, yet people still share them like they’re fresh discoveries. The answer, I think, is that these laws aren’t really about software. They’re about the social and cognitive constraints that software development runs up against constantly, constraints that don’t change when you switch languages or adopt a new methodology.
The Ones That Actually Predict Things
Not all of these laws are equal. Some are retroactive explanations, catchy ways to name something you already noticed. Others are genuinely predictive, meaning you can apply them before a project starts and expect them to hold.
Hyrum’s Law is probably the most technically specific, and the most reliably predictive. It states: with a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what your spec says. Google engineers coined this one, and it shows. If you’ve ever tried to change the order of keys in a JSON response, or alter the format of a log message, and had something break downstream, you’ve lived it.
The practical implication is that your API surface is not what you documented. It’s every byte you’ve ever sent. This is why semantic versioning doesn’t fully solve backward compatibility, and why large platforms move so slowly: the behavior space is enormous and most of it is undocumented. Rust’s RFC process and the Rust API evolution guide both grapple with this directly, enumerating which changes are “minor” versus breaking in ways that go well beyond obvious signature changes.
Conway’s Law has held up just as well since Mel Conway described it in 1967: any organization that designs a system will produce a design whose structure is a copy of the organization’s communication structure. The modern variant, sometimes called the Inverse Conway Maneuver, is used intentionally in microservices architecture: you restructure your teams to match the architecture you want, and the architecture follows.
This one is counterintuitive enough that it feels like a trick when you first encounter it, but it follows from a simple observation. Engineers talk to the people nearest them. They build interfaces at the places where communication stops. If two teams share a boundary, a service boundary will form there. If one team owns both sides of a problem, they’ll probably solve it with an in-process call instead of a network hop.
Hofstadter’s Law is the one everybody quotes because it’s self-referential in an amusing way: it always takes longer than you expect, even when you take into account Hofstadter’s Law. The humor obscures a real point about cognitive bias. We estimate based on the happy path, then add a buffer, and the buffer is always calibrated to our optimism rather than to actual variance. The fix isn’t to multiply your estimate by 1.5 or 2 or pi. The fix is to decompose the work further and estimate at a lower level of abstraction, where surprises are smaller in absolute terms.
The Ones That Get Misapplied
Postel’s Law, the robustness principle, is the most contested one in current practice. It comes from the original TCP/IP spec, written by Jon Postel: be conservative in what you send, be liberal in what you accept. This sounds reasonable until you consider what it produces at scale.
If every consumer accepts loose input and every producer sends strict output, you end up with a system where nobody can tell what the actual contract is. The Hyrum’s Law problem gets worse, because producers have accepted malformed inputs long enough that someone started depending on that acceptance. When you try to tighten the contract, things break.
The browser ecosystem is the most visible example of this playing out badly. Because browsers accepted malformed HTML for decades (tag soup rendering was the norm), you ended up with the HTML5 parsing spec needing to formally specify error recovery behavior, because the error recovery itself had become load-bearing.
Modern API design has largely moved the other direction. Protocol Buffers, gRPC, and OpenAPI schemas all push toward strict validation at the boundary. The Stripe API rejects unexpected fields rather than ignoring them. The robustness principle remains useful when you’re integrating with systems you don’t control, but as a design philosophy for new systems, it tends to create ambiguity that costs you later.
Goodhart’s Law is the one that management theory has been fighting forever: when a measure becomes a target, it ceases to be a good measure. In software, this shows up most visibly in test coverage metrics. A team that’s required to maintain 80% coverage will hit 80% by writing tests that exercise code paths without asserting anything meaningful. The coverage number goes up, the confidence it was supposed to represent does not.
The same thing happens with story points, velocity metrics, bug counts, and deployment frequency. Each of these can be a useful signal when it emerges naturally from how a team works. Once it becomes a target with consequences, behavior optimizes around the measurement rather than the underlying goal.
The Folk Wisdom Ones
Brooks’ Law (adding manpower to a late software project makes it later) is probably the most cited and most ignored law in the industry. The reason it gets ignored is that it runs against the instinct to do something visible when a deadline is at risk. Adding people looks like action. The law’s explanation is mechanical: new people require onboarding time from existing people, and coordination costs scale faster than linear with team size.
The deeper lesson that often goes unmentioned is in the original Mythical Man-Month text: Brooks was not saying you should never add people to a project. He was saying that the relationship between headcount and output is non-linear, and that the non-linearity is especially punishing late in a project when the cost of the knowledge transfer is highest relative to the remaining runway.
Gall’s Law is worth knowing if you’re designing anything intended to be complex: a complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. This is close to the principle behind the success of Unix pipes, HTTP, and Lisp: start with a minimal, coherent core and let complexity emerge from composition.
Kernighan’s Law rounds out the cognitive section: debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are by definition not smart enough to debug it. The implication isn’t that you should write dumb code. It’s that the cost of cleverness is paid later, at a moment of stress, by a version of you (or a colleague) who doesn’t have full context.
Why These Persist
A proper empirical law, in the scientific sense, has predictive power that has been tested and confirmed. Most software engineering laws are not that. They’re pattern matches against common experience, named so that the pattern is easier to communicate.
What makes them useful is exactly this communicative function. When a team is debating whether to add three new engineers to a project that’s two weeks behind schedule, being able to say “Brooks’ Law” is a shorthand that brings a half-century of failed attempts into the room without requiring everyone to rediscover the argument from first principles.
The danger is treating them as physical laws rather than as heuristics with failure modes. Conway’s Law predicts architecture from org structure, but it doesn’t mean you’re stuck with that architecture permanently. Hyrum’s Law is about the inevitable, but the rate at which it bites depends heavily on how many users you have and how fast you move. Goodhart’s Law tells you why metrics get gamed, but some metrics are harder to game than others.
The site that compiled this collection has done something genuinely useful: put them in one place, sourced them, and made them searchable. The HN discussion added a few that belong on the list, including Zawinski’s Law (every program attempts to expand until it can read mail) and Wirth’s Law (software gets slower faster than hardware gets faster).
The reason lists like this keep circulating is that the constraints they describe are structural, not incidental. They’re not bugs in how we build software. They’re features of how humans collaborate, communicate, and reason under uncertainty. Switching your stack doesn’t make Conway’s Law go away. Adopting agile doesn’t defeat Hofstadter. These laws persist because the things they describe are upstream of any particular tool or methodology.
Knowing them doesn’t make you immune to them. But it gives you a vocabulary for recognizing them when they show up, which is usually the first step toward doing something about it.