Skip to content
All articles

Written for Humans, Inherited by Machines: SOLID in the Agent Era

Dean Jain

Dean Jain

Senior Staff Software Engineer · Enterprise AI, Data & Cloud Architect

· 11 min read

SOLIDOOPDesign PrinciplesClean CodeAgentic AI

The five SOLID principles, and what each one became once the reader stopped being human. Same rules, new scarce resource.

I wrote this article in May 2014 for one kind of reader: a human, holding roughly seven things in working memory, trying to change code without breaking it. That reader is no longer the main audience.

Most code today is written, read, and modified by AI agents, and the strangest thing has happened to SOLID (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion). Every one of those five rules was derived from a human cognitive limit, so the honest expectation was that they would age out. Agents don’t get tired. They don’t lose their place. They don’t need small files to stay sane. They should have retired the whole rulebook.

They didn’t. The principles got a second justification that nobody in 2014 could have predicted.

---
config:
  theme: dark
  fontSize: 17
  themeVariables:
    fontFamily: "Comic Sans MS, Comic Neue, Chalkboard SE, cursive"
---
flowchart LR
    P["🧱 Low coupling<br/>high cohesion"]:::gov
    P --> H["🧠 2014: fits in<br/>human working memory<br/>(7 ± 2 chunks)"]:::obs
    P --> A["🤖 2026: fits in<br/>an agent's context window<br/>(and its blast radius)"]:::good
    H --> W["Same principle,<br/>new scarce resource"]:::warn
    A --> W
    classDef gov fill:#E0D6F5,stroke:#9B7EDE,stroke-width:2px,color:#0F172A
    classDef obs fill:#AED6F1,stroke:#2E86C1,stroke-width:2px,color:#0F172A
    classDef good fill:#BFEFC8,stroke:#3FA34D,stroke-width:2px,color:#0F172A
    classDef warn fill:#FFE6A8,stroke:#E0A106,stroke-width:2px,color:#0F172A

Figure 1: The principles were derived from human cognitive limits, but coupling and cohesion are substrate-independent. They describe how change propagates, and they bind whichever mind is doing the changing.

SOLID was written for human working memory. It accidentally optimized for AI / Agents.

Summary

  • The bottleneck moved. The principles didn’t. For humans the scarce resource was comprehension. For agents it’s the context window and the verification budget. The same rules fall out of both constraints.
  • SOLID, re-read for agents: SRP is blast-radius containment, OCP became a literal protocol, LSP matters more because your type system is a prompt, ISP is context economy, DIP is the verification seam.
  • Two things genuinely aged. The inheritance-centric framing is dead, and DRY weakened while YAGNI got stronger.
  • One question is still open. If AI pushes regeneration cost toward zero, “design for change” migrates from code up to specs and tool contracts. SOLID wouldn’t die; it would change address.
  • The original 2014 article follows, unchanged the maintenance-cost argument, the five principles in plain language, and the companions around them (KISS, DRY, YAGNI, composition over inheritance, dependency injection). If you want the definitions before the reinterpretation, start there.

1. The bottleneck moved. The principles didn’t.

Every design principle is a response to a scarce resource. Get the resource wrong and the principle is arbitrary; get it right and the principle is inevitable.

For a human in 2014 the scarce resource was comprehension. You could hold about seven things in your head at once, so any module that demanded more than that was, functionally, unmaintainable. Low coupling meant a change stayed small enough to reason about. High cohesion meant everything you needed to know was in one place.

For an agent in 2026 the scarce resources are the context window and the verification budget. And here is the accident of history: rules invented to fit code into human heads turn out to be exactly the rules that fit code into a transformer’s context. Low coupling means a change fits in one window. High cohesion means retrieval finds everything relevant in one place.

Coupling and cohesion were never facts about people. They are facts about how change propagates through a graph of dependencies. They bind whichever mind is doing the changing.

2. SOLID, re-read for agents

The five, each with its 2014 definition and its 2026 job:

  • Single Responsibility (a class has one reason to change) is blast-radius containment. An agent edits far more code per day than any human ever did, with far more confidence than is warranted. One-responsibility modules mean each confident machine edit can only break one thing, and can be verified by one test suite.
  • Open/Closed (open for extension, closed for modification) became a literal protocol. “Extend without modifying” is now the architecture of agent ecosystems: MCP (Model Context Protocol) tools extend a host that stays closed; plugins add behavior without touching the tested core. The safest instruction you can give an agent, add new code, don’t edit the old, is the Open/Closed principle wearing a 2026 name.
  • Liskov Substitution (a subtype must be usable anywhere its base type is) matters more than it ever did, because your type system is a prompt. An agent doesn’t read your implementation; it reasons from signatures and contracts. A subtype that quietly violates its base contract used to confuse a developer eventually. Now it poisons machine reasoning immediately, silently, and at scale.
  • Interface Segregation (many small interfaces beat one fat one) is context economy. Every method on a fat interface is context the agent must load and a call it might hallucinate. Small interfaces aren’t just cleaner; they’re cheaper to reason about per token.
  • Dependency Inversion (depend on abstractions, not concretions) is the verification seam. Depend on abstractions and you can hand an agent a sandbox: inject fakes, run its code against contracts, and check the result before it touches anything real.

3. What actually aged

Two things did not survive the decade.

  1. The inheritance-centric framing is dead. Go and Rust shipped without inheritance and nobody misses it. Composition won so completely that LSP survives only in its reframed form: behavioral contracts, not subclassing.
  2. DRY weakened while YAGNI got stronger. Humans built speculative abstractions to save future typing, but agents type for free, so the wrong abstraction now has zero payoff and its full complexity cost. “Duplication is cheaper than the wrong abstraction” was a hot take in 2014. It’s the default in 2026.

4. Open question: the disposability inversion

The original article rests on one premise: maintenance dominates cost, so optimize for change. But if AI pushes regeneration cost toward zero, a different future appears. Don’t refactor code, re-derive it from the spec. Code becomes a build artifact; the spec becomes the source.

If that future fully arrives, “design for change” collapses at the code level and migrates up a level, to modular specs, bounded contexts, and tool contracts. SOLID wouldn’t die. It would change address.

That future isn’t here. Production systems still accrete; agents still patch rather than regenerate; and regeneration itself needs deterministic verification, which needs modular, testable structure, which is the article below all over again. The empirical test settles it for now: point an agent at a clean, well-factored repo, then at a spaghetti monolith. Every practitioner in 2026 knows which is which.

Twelve years ago these principles helped humans hold systems in their heads. Today they decide whether machines can hold your system in theirs. The SOLID rules survived the reader (Humans -> Machines).

Here is the original, unchanged. Judge for yourself how much of it needed rewriting.


The original article, May 2014

Summary (as published)

  • Optimize for change, because maintenance is where the money goes. A design’s value is how easily it absorbs the next requirement.
  • SOLID, in plain terms: one reason to change (S), extend without modifying (O), subtypes substitutable (L), small focused interfaces (I), depend on abstractions (D).
  • The meta-goal is low coupling + high cohesion. Modules that don’t depend on each other’s internals, with each module doing one coherent thing.
  • Favor composition over inheritance (“has-a” over “is-a”) and dependency injection (depend on interfaces, not concretions).
  • Plus the timeless ones: KISS (Keep It Simple, Stupid), DRY (Don’t Repeat Yourself), YAGNI (You Aren’t Gonna Need It), separation of concerns and remember the system exists to provide value, so every decision answers to that.
---
config:
  theme: neutral
  fontSize: 16
---
flowchart LR
    INIT["Initial development<br/>(the small slice)"]:::neutral --> MAINT["Maintenance<br/>(most of the cost)"]:::warn
    MAINT --> G["So: design for change"]:::good
    classDef neutral fill:#FFFFFF,stroke:#333333,stroke-width:1px,color:#111111
    classDef warn fill:#F0F0F0,stroke:#333333,stroke-width:1px,color:#111111
    classDef good fill:#DAE8FC,stroke:#333333,stroke-width:1px,color:#111111

Figure 2: The fact that justifies every design principle. Most of a system's cost is maintenance, so the goal is code that's cheap to change.

Design principles can feel like dogma until you anchor them to one fact: the majority of software’s cost is not in its initial development it’s in the ongoing maintenance, fixing bugs, adapting to new platforms, repaying technical debt, and adding features. Once you accept that, the purpose of every principle becomes clear: make the code cheap to change. SOLID (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) is the famous five, but they all serve that single goal, and underneath them sits an even simpler idea low coupling, high cohesion. Here are the object-oriented (OO) principles that actually earn their keep.

1. The point of principles: cheap change

The first principle behind all the others: a software system exists to provide value to its users. Before adding a requirement, a feature, or a clever abstraction, ask “does this add real value?” if not, don’t. The second reality is the cost curve: you’ll spend far more of the system’s life changing it than building it. So “good design” operationally means design that’s easy and safe to change.

That reframes SOLID from a rulebook into a toolkit for changeability, and it explains the principle beneath SOLID the balance of coupling and cohesion:

  • Coupling is mutual interdependence between modules the probability that module B breaks when you change module A. Lower is better.
  • Cohesion is how well a single module’s responsibilities form one meaningful unit. Higher is better.

Every SOLID principle is, at bottom, a move toward lower coupling and higher cohesion which is to say, toward code where a change stays local instead of rippling. Keep that lens and the five principles stop being abstract and start being obviously useful.

2. SOLID, in plain language

---
config:
  theme: neutral
  fontSize: 16
---
flowchart TD
    SOLID["SOLID"]:::gov
    SOLID --> S["S Single Responsibility<br/>one reason to change"]:::good
    SOLID --> O["O Open/Closed<br/>extend, don't modify"]:::server
    SOLID --> L["L Liskov Substitution<br/>subtypes are swappable"]:::obs
    SOLID --> I["I Interface Segregation<br/>small, focused interfaces"]:::gate
    SOLID --> D["D Dependency Inversion<br/>depend on abstractions"]:::warn
    classDef gov fill:#FFFFFF,stroke:#333333,stroke-width:1px,color:#111111
    classDef good fill:#F0F0F0,stroke:#333333,stroke-width:1px,color:#111111
    classDef server fill:#DAE8FC,stroke:#333333,stroke-width:1px,color:#111111
    classDef obs fill:#DCDCDC,stroke:#333333,stroke-width:1px,color:#111111
    classDef gate fill:#E8E8E8,stroke:#333333,stroke-width:1px,color:#111111
    classDef warn fill:#C0C0C0,stroke:#333333,stroke-width:1px,color:#111111

Figure 3: SOLID five principles (Robert C. Martin), each a lever for lower coupling and higher cohesion.

  • Single Responsibility (SRP) a class should have exactly one responsibility, and therefore one reason to change. When a class does two things, a change to one risks breaking the other. (High cohesion.)
  • Open/Closed (OCP) open for extension, closed for modification. You should be able to add behavior without editing the existing, tested component usually by extending or plugging in, not by touching its guts.
  • Liskov Substitution (LSP) a subtype must be substitutable for its base type without breaking the program. If code expects a T and you hand it an S that inherits from T, it should still work correctly. Violate this and inheritance becomes a trap.
  • Interface Segregation (ISP) prefer many small, focused interfaces over one fat one. Don’t force a class to implement methods it doesn’t need; a “monstrous interface” couples clients to things they don’t use.
  • Dependency Inversion (DIP) depend on abstractions, not concretions. High-level code should reference interfaces/abstract types, not concrete classes, so you can swap implementations freely. This is the principle that decouples your modules at the seams.

Read them together and the pattern is unmistakable: each one either raises cohesion (SRP one job) or lowers coupling (OCP, LSP, ISP, DIP depend on stable abstractions, not volatile internals). That’s why they make code changeable.

3. The principles around SOLID

SOLID lives in a family of older, equally durable guidelines all pointing the same way:

---
config:
  theme: neutral
  fontSize: 16
---
flowchart TD
    G["Cheap, safe change"]:::gov
    G --> K["KISS · YAGNI<br/>simplest thing that works"]:::good
    G --> D["DRY<br/>single source of truth"]:::server
    G --> SC["Separation of concerns<br/>+ minimise coupling"]:::obs
    G --> CI["Composition over inheritance<br/>(has-a > is-a)"]:::gate
    G --> IOC["Inversion of control / DI<br/>depend on abstractions"]:::warn
    classDef gov fill:#FFFFFF,stroke:#333333,stroke-width:1px,color:#111111
    classDef good fill:#F0F0F0,stroke:#333333,stroke-width:1px,color:#111111
    classDef server fill:#DAE8FC,stroke:#333333,stroke-width:1px,color:#111111
    classDef obs fill:#DCDCDC,stroke:#333333,stroke-width:1px,color:#111111
    classDef gate fill:#E8E8E8,stroke:#333333,stroke-width:1px,color:#111111
    classDef warn fill:#C0C0C0,stroke:#333333,stroke-width:1px,color:#111111

Figure 4: SOLID's companions simplicity, DRY, separation of concerns, composition, and dependency injection, all serving cheap change.

  • KISS (Keep It Simple, Stupid) / YAGNI (You Aren’t Gonna Need It) / “simplest thing that works” don’t build what you don’t need yet; complexity is a future maintenance tax.
  • DRY (Don’t Repeat Yourself) keep a single source of truth so a change happens in one place, not seven.
  • Separation of concerns + minimise coupling split functionality into modules that overlap as little as possible.
  • Composition over inheritance compose when there’s a “has-a” relationship, inherit only for a true “is-a.” Inheritance is rigid and easy to misuse (it’s where LSP gets violated); composition is flexible.
  • Inversion of Control (IoC) / Dependency Injection (DI) “don’t call us, we’ll call you.” Inject dependencies (via interfaces) instead of hard-wiring concrete classes, so components stay swappable and testable. This is DIP made practical.

One caution that keeps principles from becoming dogma: avoid premature optimization and premature abstraction “make it work, make it right, make it fast,” in that order. Principles serve changeability; applying them speculatively to code that isn’t changing yet just adds complexity. And maintain a clear vision an empowered architect holding conceptual integrity prevents a system from becoming “a patchwork of incompatible designs held together by the wrong kind of screws.”

Why it matters: SOLID and its companions aren’t about writing “proper” OO for its own sake they’re about the economics of software, where change dominates cost. Aim for low coupling and high cohesion, let each SOLID principle nudge you there, prefer composition and injection so pieces stay swappable, and keep it as simple as the problem allows. Code built this way absorbs the next requirement cheaply which, since most of the cost is the next requirement, is the whole point.

References