What Microservices Actually Mean and Why Most Teams Get It Wrong

by Arif Ikhsanudin, Backend Developer

What teams think they're building versus what they're actually building

Ask five engineers at a "microservices company" to define a microservice and you will get five different answers. "It's a small service." "It's a service with its own database." "It's a service owned by one team." "It's anything that's not a monolith." These aren't wrong exactly, but they're incomplete in ways that explain why most microservices migrations produce distributed monoliths rather than the autonomous, independently deployable systems the pattern promises.

The confusion isn't semantic pedantry. It has real consequences. Teams optimizing for "small" end up with services so fine-grained that a single user request touches eleven of them. Teams optimizing for "separate database" end up with a service that can't do anything useful without synchronously calling three others. The architectural failure mode is predictable and consistent: you get the operational complexity of microservices with none of the autonomy benefits.

The actual definition and what it requires

Microservices — as articulated by Martin Fowler and James Lewis in the original 2014 paper, and as practiced by organizations that make them work — are services organized around business capabilities, owned end-to-end by a single team, independently deployable, and communicating only through well-defined interfaces.

That last part is load-bearing. "Communicating only through well-defined interfaces" means a service's internal data model, database schema, and implementation details are completely hidden from other services. No shared databases. No shared libraries that bleed domain logic across boundaries. No direct SQL joins across service-owned tables.

The organizational corollary is Conway's Law applied deliberately: your service architecture should mirror your team structure. If you have a payments team, there is a payments service (or a small cluster of payments-related services). That team owns the API contract, the deployment schedule, the database schema, the SLAs. No other team has write access to payments' database. No other team deploys to payments' infrastructure without going through the payments team.

When those two conditions are in place — true data isolation and team ownership — you get the actual benefit: the payments team can deploy, scale, and evolve their service without coordinating with anyone else. That's the promise. Most "microservices" architectures violate one or both conditions and then wonder why they don't feel any more autonomous than the monolith did.

The shared database trap

The most common way teams build microservices that aren't: multiple services sharing a single relational database, querying each other's tables directly.

-- Order service querying inventory's table directly
SELECT i.quantity, i.warehouse_location
FROM inventory.items i
JOIN orders.order_items oi ON oi.item_id = i.id
WHERE oi.order_id = ?;

This looks like a performance optimization — one query instead of a service call — and it is. It's also a coupling that makes every microservice benefit impossible. If the inventory team needs to rename warehouse_location to location_code, they now need to coordinate with every service that queries their table. The inventory service can't be deployed independently because its schema changes break other services. You have the operational overhead of multiple services with the coupling overhead of a monolith. This is specifically the distributed monolith anti-pattern.

The correct pattern requires a service call through a defined API when you need another service's data:

// Order service calls inventory service API instead of direct DB access
InventoryItem item = inventoryClient.getItem(itemId);
// inventoryClient is a typed HTTP/gRPC client generated from the inventory service's API spec

Yes, this is slower. Yes, it introduces network failure modes. That's the trade. You're paying operational cost for deployment independence. If you're not willing to pay it, use a monolith with clear module boundaries instead.

Size is the wrong dimension

"Microservice" implies small, and small is not the property that matters. The right metric is cohesion: a service should do one thing well and own all the data and logic for that thing. A billing service that handles invoicing, payment processing, and tax calculation is not "too big" in any meaningful sense if billing, payments, and tax are all aspects of the same bounded context owned by the same team.

Domain-Driven Design's bounded context is the right mental model here. A bounded context is a semantic boundary within which a specific domain model applies consistently. The Ubiquitous Language (the shared vocabulary of the domain) is consistent inside a bounded context. A microservice maps to a bounded context, not to a functional layer or a database table.

If your "User Service" handles authentication, profile management, preferences, notification settings, and social connections — you probably have multiple bounded contexts collapsed into one service. If your "Auth Service" and "Profile Service" are separate but one can't function without synchronously calling the other for every request — you probably split a bounded context across two services.

The minimum viable check

Before calling something a microservice, verify three things:

  1. Does this service own all the persistent state it needs to serve its primary use cases without calling another service?
  2. Can this service be deployed without coordinating with any other team?
  3. Is there exactly one team responsible for this service's SLA, schema migrations, and API contract?

If any answer is no, you don't have a microservice. You have a distributed component of a larger system — which might be fine, but name it accurately so you can reason about it honestly.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

Why Cheap Freelancing Can Damage Your Career

Ever wonder why some freelancers seem stuck forever in low-paying gigs? Cheap freelancing isn’t just about money—it can quietly sabotage your career.

Read more

Mandatory Office Days: A Contractor’s Productivity Nightmare

“We just need you in the office a few days a week.” Sounds harmless—until those days quietly become the least productive ones.

Read more

Monitoring Is Not Optional. It Is How You Know Your App Is Alive.

A service without meaningful monitoring is a service you're flying blind on. You don't know if it's working, degrading, or failing — until a user tells you. That is not an acceptable operational posture.

Read more

Disagreeing With a Technical Decision Without Burning Bridges

Technical disagreements are inevitable in engineering teams. How they are handled determines whether the team's technical quality improves over time or whether decisions get made by whoever argues longest.

Read more