ModalB

Software development5 min read

Microservices and API-First: building applications that hold up

Moving from a monolith that works to a distributed system that holds up under load is not a leap of faith, it is town planning. What matters: the API contract, how the domain is split, and observability.

By ModalB

Moving from a monolith that "works" to a distributed system that holds up under load is not a leap of faith, it is town planning. The API-First approach anchors the contract, microservices split the domain, cloud-native infrastructure brings elasticity — provided you measure, version and govern. The difficulty has shifted: it is no longer technological, it is operational and organisational.

Why the API comes first

Starting with the API means starting with the contract: you describe the resources, the verbs and the schemas, you have the scope validated by the teams that will consume the service, and only then do you write the code. The benefits are immediate: parallel work, fast feedback, and contract tests that become guard rails in continuous integration.

It is the opposite of the "code-first" reflex, where documentation trails behind the implementation and ends up lying.

API-First turns the API into a product: versioned, tested, documented — and therefore negotiable without drama.

In practice

  • Design first. Write the OpenAPI 3.x specification, have it reviewed, freeze the extension points, generate mocks so the front end and the integrations are not blocked.
  • Contract tests. Pact or Spring Cloud Contract, to check from both sides that nobody breaks the other's assumptions.
  • Living documentation. Swagger UI or Redoc plugged into the specification, published automatically on every commit.

Monolith or microservices: what you gain, what you take on

The monolith keeps real advantages: a single deployment, straightforward integration tests, no internal network latency. It reaches its limits when scalability varies sharply between domains, when release cadences diverge between teams, or when heterogeneous technologies become necessary.

Microservices bring deployment isolation, team autonomy and the ability to scale exactly where it is needed. They also introduce distributed complexity: fragmented data, transactions to compose, mandatory observability. This is not a neutral technical choice, it is an organisational one.

The principles that hold over time

  • Split by business domain, based on Domain-Driven Design bounded contexts — not by technical layers.
  • One database per service. Each service owns its data and exposes its model only through its API.
  • Explicit contracts. Semantic versioning, deprecation cycles announced in advance.
  • Automation. CI/CD, contract tests, security scans, declarative policies.

Choosing the exchange pattern to match the flow

Synchronous

  • REST/HTTP for universality and tooling.
  • gRPC when latency and strict interfaces come first.
  • GraphQL to aggregate and let the client select precisely what it needs.

Asynchronous

  • Kafka or Pulsar for event streaming.
  • RabbitMQ or SQS for application messaging.

Asynchronous exchanges absorb load peaks and open the door to resilience patterns. They do make reasoning harder: you have to accept eventual consistency.

Distributed transactions: compose rather than block

  • Saga. Orchestration or choreography, with compensating transactions to guarantee eventual consistency.
  • CQRS and Event Sourcing. Separate writes from reads, keep the history of events, rebuild state when needed. Keep this for domains with strong audit or read-performance requirements — the implementation cost is real.

Infrastructure: from container to service mesh

The Docker / Kubernetes / GitOps trio has become the default path: packaging, orchestration, declarative operations. A service mesh such as Istio or Linkerd brings mTLS, fine-grained routing, retries and circuit breakers, and network observability — guard rails placed outside the application code.

Our recommendation: switch mTLS on from the start, then harden progressively namespace by namespace according to risk. Doing it the other way round costs far more.

Observability: traces, metrics and logs, one and the same fight

OpenTelemetry has established itself as the common language of instrumentation: distributed traces, metrics and logs share semantic conventions. Without that instrumentation, neither a low recovery time nor frequent deployment is credible: you are flying blind.

Migrating without stopping everything

You do not throw away a system that is carrying production. You go around it, then you empty it. The Strangler Fig pattern remains the reference: introduce a gateway, redirect some routes to new services, and let the monolith empty out function by function.

Two guard rails prevent regrets:

  • Anti-Corruption Layer. Place a translation layer between the legacy model and the language of the target domain, so the legacy does not contaminate the new architecture.
  • Branch by Abstraction. Hide the old implementation behind an abstraction to switch over gradually, without freezing development for months.

Measuring what matters

  • Contracts: share of contract tests passing, breakages caught before the merge.
  • Flow: deployment frequency, lead time to production, time to restore service.
  • Platform: 95th percentile latency per inter-service call, errors per endpoint, quota rejections at the gateway.
  • Consumption: cost per request, saturation of brokers and clusters.

Without a versioned specification, there is no contract. Without a contract, there is no trust. Without trust, microservices slow you down instead of speeding you up.

As a compass

Microservices give you neither agility nor performance: they offer the option of reaching them, if the API contract is solid, if the data is under control, if observability is systematic and if the platform delivers on its promises.

Architecture is a debt like any other: you can invest in it or suffer it. The organisations that come out ahead standardise their APIs, tool the lifecycle, and are not afraid to not split what does not need splitting today.

Sources

  • CNCF Annual Survey — cloud-native adoption and operational challenges
  • OpenAPI 3.x specification and design-first guidance
  • Martin Fowler, Strangler Fig Application
  • Azure Architecture Center — Saga, CQRS and Anti-Corruption Layer patterns
  • Istio documentation (mTLS) and OpenTelemetry
  • Architecture
  • API
  • Microservices

Further reading

Working on something similar?

Our teams can support you on scoping, architecture and delivery.