We've Seen This Before: What SOA Teaches Us About APIs in the Age of Agents

SOA's real lesson for agent-era APIs is that structural contracts are not enough. Interfaces need meaning, behaviour, authority and governance built into the contract.

In the first article in this series , I argued that agents do not replace APIs. They expose the quality of the APIs underneath them.

That should feel familiar, because we have been here before.

The current wave of AI agents, MCP, and tool-driven architectures follows a recognisable pattern. We are being told that systems will become more composable, more interoperable, and more reusable. Software will call other software dynamically. Capabilities will be discovered and invoked at runtime. Integration will become less rigid because the caller can decide what to use as it goes.

That sounds new because the tooling is new. The architecture story is not.

We said much the same thing about Service-Oriented Architecture.

SOA, at least in its enterprise form, did not deliver on that promise. The easy explanation is to blame the technology: SOAP, XML, WS-* and all the ceremony around them. That is convenient, but it misses the more useful lesson. The problem was not simply how services communicated. The problem was how they were designed, and what we thought a contract actually meant.

That matters now, because agents are pushing us back into the same territory, only faster.

SOA Didn’t Fail At Interfaces

SOA gave us strong interface definitions. WSDL could describe operations, inputs, outputs, schemas, and types. For the time, that was a serious attempt to make enterprise systems talk to each other in a more formal way.

On paper, it looked like interoperability.

In practice, it often wasn’t.

The contract could tell you the shape of the message, but not enough about the meaning of the message. It could tell you that an operation existed, but not always what business intention sat behind it. It could describe fields and types, but not the assumptions the service was making, the states it expected, or how it behaved once you stepped outside the happy path.

Two systems could integrate perfectly at a structural level and still misunderstand each other completely.

That was the deeper failure. SOA did not fail because interfaces were useless. It failed because the interfaces were not rich enough to survive outside the context in which they had been created.

The Known Consumer Problem

Many SOA services were not really general-purpose capabilities. They were services extracted from, or designed for, a particular application, process, or integration.

That meant the formal interface was only part of the contract. The rest lived in shared context. The consuming system knew the expected sequence. The teams knew which fields were safe to use. The people involved understood which status values were meaningful, which edge cases mattered, and which calls were technically possible but operationally foolish.

The service looked reusable because the interface was published. But reuse outside the original context still required human negotiation.

This is the same problem we see with many APIs today. The interface is technically available, but its correct use depends on assumptions that were never made explicit. Once the consumer is no longer the one you designed for, the weakness becomes visible.

A specific example from the SOA period has stayed with me. Architects designed what should have been a public CRM capability: a customer API that could have served as a stable enterprise surface for more than one consumer. In practice, it was shaped around the first integration. The request model, sequencing, optional fields, and error handling reflected the needs of the two systems being connected rather than the wider business capability.

It had the language of a reusable service, but the design of a point-to-point connector.

Agents make that weakness more serious because they are, by definition, not the original known consumer.

One concrete version of this is the end-to-end customer journey. I have written before about lead-to-cash as a process that looks tidy on a diagram but crosses marketing, fulfilment, billing, payments, operations, and multiple system owners. In the SOA and ESB world, the process model could give the impression that the journey had a single contract. In practice, the contract often lived in the routing rules, service assumptions, ownership boundaries, and exception handling around the services.

The interface did not carry all of that. The process did.

Structure Is Not Meaning

The most important distinction is between structural precision and semantic clarity.

SOA was often structurally precise. A schema could be strict. A service could reject invalid messages. Types could be checked. The system could be correct in a narrow technical sense.

But a field called status could still be ambiguous. Does it describe the current business state, the last completed process step, the result of a validation check, or the state visible to a particular user role? What transitions are valid? What side effects happen when it changes? Which system is the source of truth?

Those answers were rarely in the contract. They were in documentation, project memory, support teams, or the heads of people who had been around long enough to know.

That is not a small gap. It is the difference between being able to call a service and being able to use it correctly.

Governance And Operational Reality Parted Ways

SOA also teaches a second lesson, which is that governance cannot just be process layered on top of weak contracts.

Organisations recognised that service reuse and change management were difficult, so they introduced versioning rules, approval boards, central registries, and service governance processes. The intention was sensible: bring order to a growing service estate.

The deeper problem was that governance and operational reality parted ways.

The SOA registry was supposed to be the source of truth, but it could not express enough of the contract. It could describe the service, the endpoint, the schema, and perhaps some ownership metadata. It could not reliably express the business meaning, assumptions, side effects, operational constraints, or safe usage patterns.

So architects and teams did what they had to do: they wrote documentation around the registry.

That helped for a while, but it created a new problem. The API, the registry, and the documentation were now three separate representations of the same contract. They changed at different speeds, were owned by different people, and predictably began to drift.

Once that happened, governance stopped being a reliable description of reality. It became another artefact to reconcile.

I saw the operational version of this in audit and compliance work. The specialists I worked with were not interested in the service diagram as the final truth. They wanted the raw data, because committed data in the database was the only durable evidence of what had happened. Queue state, process state, and integration logs mattered, but they were not a substitute for a governed source of record.

The lesson is not that governance is bad. The lesson is that governance has to live in the design of the contract itself. If meaning, authority, behaviour, and change expectations are not part of the interface, a process document will not rescue it.

The Illusion Of Reuse

SOA promised reusable services. What many organisations got instead were services that were technically reusable but practically tied to their first use case.

That distinction matters.

Technical reusability means another system can call the service. Practical reusability means another system can call it safely, understand what it means, depend on its behaviour, and evolve with it over time. SOA often delivered the first and assumed the second would follow.

It did not follow.

Reuse still depended on humans understanding the semantics, uncovering hidden assumptions, and negotiating change. The contract had not carried enough of the work.

Why Agents Make The Old Problem New Again

Agents change the consumption model.

Instead of a known front end or a controlled integration, we now have consumers that may discover tools dynamically, compose operations at runtime, and act without the shared background knowledge of the teams that built the API. An agent does not know what you meant. It knows what you exposed.

That turns old weaknesses into more immediate risks. Ambiguous fields become incorrect decisions. Hidden assumptions become failed workflows. Unclear behaviour becomes unpredictable action. A contract that was merely inconvenient for human developers can become dangerous when an autonomous caller starts composing it with other capabilities.

This is why the move from SOAP to REST, from XML to JSON, or from WSDL to OpenAPI does not by itself solve the problem. The modern stack is cleaner and more pleasant to work with, but most API contracts still describe structure better than meaning. They tell you about endpoints, payloads, response codes, and schemas. They say much less about intent, domain context, behavioural guarantees, authority boundaries, and side effects.

That is the pattern we risk repeating.

The Real Lesson

SOA standardised how services talk. It did not standardise what they mean.

That is the lesson worth carrying forward.

Agents are not sending us into an entirely new architectural problem. They are making an old one harder to ignore. If the contract does not contain enough meaning to survive outside its original context, then dynamic composition will not make the system more reliable. It will make the misunderstanding faster.

The next question is therefore not whether we need APIs. We do.

The question is what an API contract needs to contain when the caller is no longer a known application, a known team, or a known workflow.

And beneath that, there is a deeper issue: our APIs expose data structures, but not meaning.