The Assumption That Worked at Ten
Every system carries a set of assumptions baked in at the moment it was designed, and almost none of them are written down, because at the time they weren’t assumptions — they were just facts. Ten users meant you could loop over all of them without thinking about it. A list that fit in memory meant you never needed to think about paging it. One region meant you never needed to think about which one a request landed in. These weren’t decisions so much as unexamined truths of the environment the system was born into, and they get compiled directly into the code’s structure rather than stated anywhere a future reader could find them.
The trouble is that scale doesn’t revoke an assumption with a warning. The loop over all users still runs correctly at ten thousand; it’s just slower, and it stays slower in a way that’s easy to miss because “slower” doesn’t look like “broken” until it crosses some threshold nobody marked in advance. The list that fit in memory still fits until the day it doesn’t, and until then the code that assumed it looks exactly like code that was carefully bounded, because there’s no visible difference between an assumption that’s still valid and one that’s quietly become false. Growth erodes these foundations invisibly, and the first sign is usually a production incident rather than a code review comment, because nothing in the system’s outward behavior changes until the assumption’s actual limit is crossed.
This is why “it worked in testing” and “it worked in production for a year” are both weaker guarantees than they feel like. Neither one tells you the assumption is sound — only that you haven’t yet exceeded whatever threshold it depends on. A piece of code can be correct, well-tested, and quietly running on borrowed time, and none of the usual signals — passing tests, clean logs, satisfied users — will tell you which category you’re in. The absence of a problem is not evidence the assumption holds; it’s only evidence you haven’t yet reached the point where it doesn’t, which is a much weaker claim than it sounds like when you’re the one relying on it.
The practical response isn’t to design every system for arbitrary future scale — that’s the speculative-generality trap from an earlier thread, paying a certain cost today for a benefit that may never arrive. It’s to make the assumptions visible while they’re still true, so that crossing their limit is a known event rather than a surprise. A comment that says why a loop is safe today — “bounded by team size, currently under 50” — does more good than the loop being fast, because it tells the next person exactly which fact to watch and exactly when to come back and reconsider. An assertion that fails loudly when a list exceeds an expected bound turns a silent overrun into an immediate, diagnosable signal instead of a slow degradation discovered during an incident. The goal isn’t predicting the future correctly; it’s making today’s true-but-temporary facts legible enough that someone can notice when they stop being true.
None of this is really about scale in the narrow sense — it’s about respecting that every piece of working code has a domain where it’s actually correct, and that domain is usually narrower than “forever” even when nothing in the code says so. The discipline is to know your own assumptions well enough to name them, and to build in a way that surfaces their violation rather than absorbing it silently. Code that works today because ten was small enough is not wrong — it was right for the world it was built in. The mistake is only in forgetting that the world it was built in was never guaranteed to be the world it would keep running in.