Readable code is good. Simple code is better. The problem is that most engineers treat these as synonyms, and that confusion quietly destroys codebases.

Readability is a surface property. It describes how easily a human can parse what code says. Simplicity is a structural property. It describes how few moving parts are required for code to do what it does. You can have one without the other, and mistaking them leads to a specific kind of architectural debt that is very hard to explain to a manager and very expensive to fix.

Readable Code Can Hide Enormous Complexity

Consider a function named getUserPermissions(). Clean name, obvious intent, easy to read. Now imagine that function makes three database calls, checks a feature flag from an external service, falls through four conditional branches depending on account type, and silently returns an empty array on any network error. The function reads fine. Its complexity is catastrophic.

This is the trap that fluent naming and clean formatting set for us. A well-named abstraction invites trust. That trust can be completely unearned. Rich Hickey made exactly this point in his 2011 talk “Simple Made Easy,” distinguishing between things that are easy (familiar, close at hand) and things that are simple (not interleaved, not complex). Readable code tends toward easy. Simple code takes deliberate work.

When teams optimize for readability without tracking structural complexity, they produce codebases where every individual function looks fine and the overall system is impossible to reason about. The complexity doesn’t disappear. It just hides behind good naming.

Two diverging paths representing readable versus structurally simple code
Readability and simplicity often look similar at the start and diverge sharply over time.

Abstractions That Read Well Often Couple Things That Shouldn’t Be Coupled

The instinct that produces readable code is the instinct to name things well and wrap logic in descriptive functions. This is genuinely good. But it creates pressure toward abstractions that feel clean without being clean.

A PaymentProcessor class that handles validation, currency conversion, fraud detection, and logging is readable in the sense that its name suggests what it does. It is not simple. It has four distinct responsibilities tangled together. Every change to fraud detection rules now requires you to reason about payment validation. Every new currency touches the same class as your logging configuration.

Simple code keeps things that change for different reasons in genuinely separate places, not just in separate methods within the same class. That separation is often less immediately readable because you have to follow more indirection to understand the full picture. The payoff is that changes stop cascading unpredictably through the system.

The Cognitive Cost Compounds Over Time

Readable code reduces the cost of initially understanding a piece of logic. Simple code reduces the cost of reasoning about the system as it changes. For any codebase that lives longer than a few months, the second cost dominates.

Studies of software maintenance consistently find that the majority of engineering time goes into understanding and modifying existing code rather than writing new code. The exact figures vary by organization, but the direction is not in dispute. Code is read far more than it is written. But “read” here means something harder than parsing a function name. It means understanding what happens when you change something, what depends on what, and what can safely be ignored.

Readable code helps with the first minute of understanding. Simple code helps with the next year of evolution. Optimizing for the first minute at the expense of the next year is a bad trade that is very easy to accidentally make.

The Counterargument

The reasonable objection here is that readable code is a prerequisite for simplicity. You cannot refactor a codebase toward simplicity if you cannot understand what it currently does. Readability is the entry point.

This is true, and the argument in this piece is not that readability doesn’t matter. It is that readability alone is insufficient, and that treating it as the primary goal produces specific failure modes. Code reviews that focus on naming conventions and formatting while missing that a function now has eleven distinct code paths are optimizing for the wrong thing. Style guides that enforce consistent indentation while allowing unbounded coupling are solving the easier problem.

Readability and simplicity reinforce each other when you pursue both deliberately. The mistake is treating the first as evidence of the second.

What Simple Code Actually Requires

Simplicity requires decisions that readable code does not force you to make. Where does this logic live? What does this module need to know about? What are the actual change boundaries in this system? These are harder questions than “does this function have a good name?”

Some concrete signals that code is genuinely simple: changing one thing rarely requires changing many others. New engineers can form accurate mental models of the system from its structure, not just its naming. The test suite is fast because components don’t drag in unrelated dependencies. Deleting a feature is hard for business reasons, not technical ones.

Readable code is table stakes. It tells you the code was written by someone who cared. Simple code tells you the code was designed by someone who understood the problem deeply enough to resist the easy solutions. These are different skills, and conflating them means only practicing one of them.