Design Signals: How to Spot Agency in Code

There’s a reason most legacy systems feel heavy.

It’s not because they’re old.

It’s not because they lack patterns.

It’s not even because they’re poorly tested.

It’s because they lack agency.

And once you learn to see that, you can’t unsee it.

Why Most Legacy Code Is Procedural in Disguise

I’ve reviewed thousands of systems over the years. Most of them claim to be object-oriented.

They have classes.

They have methods.

They have dependency injection.

But they aren’t object-oriented.

They’re procedural code wearing object costumes.

You can spot it immediately. The logic lives in service classes. The objects are data containers. Decisions are centralized. Behavior is orchestrated from the outside.

Everything asks everything else for information.

“Tell me your state so I can decide what to do.”

That’s procedural thinking.

In real object-oriented design, objects don’t expose their guts so others can make decisions. They own decisions. They act.

That’s agency.

Agency as the Missing Concept in OO

Agency is the idea that an object has responsibility and authority within a boundary.

It doesn’t just hold data.

It knows something.

It decides something.

It protects its invariants.

When agency is present, code feels calm.

You don’t see giant conditionals switching on types.

You don’t see long chains of getters.

You don’t see orchestration bleeding across layers.

Instead, you see:

  • Clear responsibilities
  • Intentional messages
  • Localized decisions

An object with agency doesn’t say, “Here’s my state — you figure it out.”

It says, “Tell me what you need done.”

That shift changes everything.

Patterns as Signals, Not Recipes

One of the mistakes we made in our industry was turning patterns into recipes.

“Use Factory here.”

“Apply Strategy there.”

“Wrap that in an Adapter.”

Patterns aren’t recipes.

They’re signals.

They signal that something in the problem space requires separation of concerns. Or deferred construction. Or variation behind a stable interface.

When you see patterns as signals, you stop forcing them. Instead, you look for the forces in the problem.

That’s where agency becomes visible.

If an object doesn’t know enough to make its own decisions, maybe the decision belongs somewhere else.

If construction is tangled with use, maybe object creation needs its own boundary.

The pattern is not the goal.

The coherence is.

Demeter, Factories, and Where Decisions Belong

Take the Law of Demeter.

It’s often explained as a rule about not chaining calls.

But that’s superficial.

Demeter is really about agency boundaries.

When you write:

order.getCustomer().getAddress().getZipCode()

You’re not just violating a stylistic rule.

You’re reaching across multiple domains to extract state so you can decide something externally.

You’ve removed agency from the objects involved.

Factories work the same way. A good factory centralizes creation decisions so objects don’t need to know too much about their collaborators.

These aren’t “design tricks.”

They’re ways of restoring agency to the right places.

The question is always the same:

Where does this decision belong?

Why Refactoring Restores Freedom

When I refactor with teams, something interesting happens.

At first, there’s resistance.

“It works.”

“We don’t have time.”

“It’s just one more conditional.”

But as we extract responsibilities and move decisions to the objects that own them, something shifts.

The code breathes.

Tests become simpler.

Dependencies shrink.

Changes feel localized.

That’s not aesthetic preference.

That’s freedom.

Refactoring isn’t about prettiness. It’s about restoring agency. And when agency is restored, change becomes less frightening.

The system regains elasticity.

What AI Needs from Developers Now

AI can generate object-oriented syntax all day long.

It can produce classes.

It can apply patterns.

It can refactor mechanically.

But it cannot sense agency.

It doesn’t feel when a responsibility is misplaced. It doesn’t experience the discomfort of a leaky abstraction. It doesn’t notice when a decision has drifted too far from its domain.

That’s our job.

Right now, developers who collaborate well with AI aren’t the ones with the best prompts.

They’re the ones who can see structural signals.

They can say:

•“This decision doesn’t belong here.”

•“This object is just a data bag.”

•“This variation needs a boundary.”

When you can see agency, AI becomes powerful. It can move quickly under your guidance.

When you can’t, AI amplifies procedural thinking at scale.

The future of AI-assisted development isn’t about speed.

It’s about perception.

And perception begins with seeing agency in code.

Where does your team stand with AI? Take the FREE AI Maturity Assessment

X