OOPs at Scale

The idea

When I started out as a developer, the most powerful thing I learned was object-oriented programming. Encapsulation, abstraction, inheritance, polymorphism. Four words that shaped how I wrote code.

Years later, as a solution architect, I stopped writing classes and started drawing systems. And I noticed something: I was still doing exactly the same thing. Same principles, bigger boxes.

OOP was never really about classes. It was about managing complexity by hiding it behind well-defined boundaries. Change the size of the box and the principle survives intact.


The same principles, three altitudes

PrincipleIn a class (Java)In a component / serviceIn a system or organisation
Encapsulationprivate fields, state changed only via methodsA service owns its database; no one reaches into itA domain owns its data and rules; other domains ask, they don’t reach in
AbstractionAn interface exposes what, not howAn API contract hides the implementation behind itA capability (“Order Management”) hides dozens of services and legacy stacks
Inheritance / reuseBase classes, shared behaviourShared libraries, base images, platform servicesReference architectures, golden paths, shared platform teams
PolymorphismOne interface, many implementationsOne API, many backends (legacy and new, swapped by config)One customer journey served by different regions, products, or billing engines

Same sentence, three altitudes: depend on the contract, not the thing behind it.

Team structure follows the same rule. A team that owns a domain end-to-end is a well-encapsulated object. A team organised across domains is a class with public mutable fields — everything leaks, and every change needs a meeting.


The last few decades were just OOP zooming out

This is the part I find most interesting. If you line up the major shifts in our industry, they aren’t unrelated inventions. They’re the same design move, applied at a larger and larger scale.

Objects → Components. We encapsulated state and behaviour in a class. Then we drew a bigger box around a set of classes, gave it a versioned interface, and called it a component or library.

Components → Web services (SOA). The next box crossed the network. Now the interface was a contract over HTTP, and the implementation could be written in another language, on another machine, by another team. Pure abstraction — the caller stopped caring.

SOA → Microservices. We made the boxes smaller and insisted each one own its own data. That’s encapsulation, enforced at deployment time. The reason microservices work when they work is the same reason a class with private state works.

Servers → Cloud. Compute, storage, and networking got interfaces. “Give me a machine” became a method call. The data centre became an object you program against, and you stopped knowing — or caring — which rack your workload landed on.

Scripts → Infrastructure as Code. Infrastructure became a type. You declare the desired instance, the platform reconciles reality to match. Declarative over imperative is just abstraction applied to operations.

Containers and orchestration. A container is an encapsulated runtime. Kubernetes is polymorphism for workloads — one scheduling interface, many implementations underneath.

Event-driven architecture. Message passing was Alan Kay’s original point about objects. Kafka is that idea at organisational scale: producers and consumers that know a topic contract and nothing else about each other.

Every one of these steps did the same three things: draw a boundary, publish a contract, hide the inside. That’s it. That’s the whole industry.


Why this matters

If the principles are scale-invariant, then experience compounds in a way that’s easy to underestimate. The instinct you built arguing about whether a method belongs on this class or that one is the same instinct you need when deciding which domain owns a piece of data. The code review question “why does this class know about that?” is the architecture review question “why does this service need that database?”

And the failure modes rhyme too. A god class becomes a monolith. Tight coupling becomes a distributed monolith. Leaky abstraction becomes a shared database. Inheritance abuse becomes an over-general platform nobody can change.

So when you look at a system diagram, ask the questions you’d ask of a class:

  • What does this own, exclusively?
  • What does it promise, and what does it hide?
  • Who depends on its internals instead of its contract?
  • Could I replace the inside without anyone noticing?

If the answers are good, the scale doesn’t matter.


Closing

We keep inventing new names — services, containers, platforms, domains, meshes. Underneath, we’re still doing what OOP taught us on day one: draw the right boundary, expose the smallest useful contract, and hide everything else.

The diagrams get bigger. The principles don’t change.

Leave a comment

Create a website or blog at WordPress.com

Up ↑