Should You Adopt Microservices? Architecture, Trade-Offs, and Service Fencing

Arpit Bhayani

Arpit Bhayani

Apr 08, 2022 • 7 min read

Play

Should You Adopt Microservices? Architecture, Trade-Offs, and Service Fencing

Microservices have become the default architectural pattern advocated across the tech industry. However, adopting microservices simply because major tech companies use them often leads to unnecessary operational complexity. Microservices are not a silver bullet; they are an architectural solution designed to address specific engineering scalability and team coordination challenges.

To make an informed architectural decision, you must understand what microservices are, the concrete advantages they provide over a monolith, and the practical heuristics used to define service boundaries (“service fencing”).


What Are Microservices?

Microservices are small, autonomous, and harmonic subsystems that collectively power a larger application. Instead of managing a massive, unified codebase, a complex product (such as Amazon, Flipkart, or Twitter) is partitioned into discrete subproblems, with each subproblem assigned to a dedicated service.

graph TD
    Client[API Gateway / Client]
    Client --> Auth[Authentication Service]
    Client --> Search[Search Service]
    Client --> Order[Order Service]
    Client --> Payment[Payment Service]
    Order --> Delivery[Delivery Service]

Each microservice is designed, built, and optimized to execute a single bounded set of responsibilities:

  • Authentication Service: Handles user identity, token verification, and session management.
  • Search Service: Manages indexing, full-text queries, and relevance scoring.
  • Order Service: Coordinates checkout workflows, carts, and state transitions.
  • Payment Service: Interfaces securely with payment gateways and ledger systems.
  • Delivery Service: Tracks fulfillment, logistics, and shipping partners.

From the outside, the application appears as a single unified product. Internally, these autonomous services communicate harmoniously over defined network contracts to fulfill complex workflows.


Why Companies Adopt Microservices

The transition to microservices is rarely driven by technical purity; it is typically driven by the need to maintain organizational velocity as systems and teams grow.

graph LR
    Idea[MVP / Prototype] --> PMF[Product-Market Fit]
    PMF --> TeamGrowth[Team & Feature Growth]
    TeamGrowth --> MonolithBottleneck[Monolith Coordination Chokepoint]
    MonolithBottleneck --> Microservices[Microservices Decomposition]

1. Developer and Deployment Velocity

In early-stage companies, a monolith is optimal: a few engineers quickly prototype, validate Product-Market Fit (PMF), and deploy features rapidly. However, as the product succeeds, the codebase expands, and the engineering organization fractures into multiple distinct teams.

In a massive monolith, coordinating across dozens or hundreds of engineers creates friction:

  • Merge Conflicts: Multiple teams touching shared files and models produce constant integration hurdles.
  • Deployment Chokepoints: If Team A is ready to release a critical update, but Team B introduced a regression or has half-finished code in the shared branch, the entire release pipeline stalls.
  • Bug Contagion: A null-pointer exception or memory leak caused by one non-critical component can bring down the entire monolithic process.

Microservices decouple deployment lifecycles. Each team can test, ship, and iterate on their service independently without blocking or being blocked by other teams.

2. Predictable, Independent Scalability

In a monolithic architecture, horizontal scaling is an all-or-nothing proposition. If your search workload increases tenfold, you must replicate the entire monolith. Because each instance contains the complete application code, internal caches, and dependencies, the memory (RAM) and CPU footprint per instance is unnecessarily massive.

With microservices, resources are allocated proportional to the workload profile:

ServiceTraffic / Resource ProfileExample Scaling Footprint
Search ServiceCPU-intensive, heavy scoring algorithms, high RAM10 high-compute instances
Auth ServiceHit on every incoming API request; low compute, high I/O20 low-latency instances
Payment ServiceLow-frequency, strictly sequential processing3 redundant, secure instances

Independent scaling ensures predictable resource consumption, prevents resource starvation, and dramatically reduces cloud infrastructure costs.

3. Autonomy, Isolation, and Technology Diversity

Microservices grant teams complete technical autonomy within their service boundaries. A team can choose the programming language, runtime, and storage engine best suited to their specific domain:

  • High-throughput, low-latency microservices can be authored in Go or Rust backed by a relational database like MySQL or PostgreSQL.
  • Unstructured or dynamic document workloads can be built in Java or Node.js utilizing MongoDB or DynamoDB.

Services interact exclusively through strict, versioned network contracts (e.g., HTTP/REST, gRPC over HTTP/2, or raw TCP sockets). The internal implementation details—including the database schema and framework choices—are completely encapsulated.

4. Fault Tolerance and Graceful Degradation

In a monolith, an unhandled exception or thread pool exhaustion in a non-essential feature (e.g., product reviews) can crash the runtime process, taking down checkout and payments.

In a microservices architecture, outages are isolated:

  • If the Search Service experiences a catastrophic failure, users may temporarily lose search functionality, but they can still view their existing orders, complete a pending payment, or browse their account settings.
  • The blast radius of any individual failure is localized, allowing client applications to degrade gracefully rather than presenting users with a total system outage.

5. Seamless Architectural Upgrades

Software stacks become obsolete over time. Modernizing a monolith often requires an expensive, high-risk, multi-year rewrite.

With microservices, incremental modernization is straightforward:

  • A legacy service written in Java can be rewritten in Go or Rust without impacting upstream or downstream consumers, provided the existing API contract is preserved.
  • A team can migrate its internal datastore from a relational model to a key-value store transparently, without requiring coordinating changes across the rest of the company.

How to Fence a Microservice

The most challenging aspect of microservices architecture is fencing—determining where one service ends and another begins. How small is “micro”?

graph TD
    TooBig[Too Big: Distributed Monolith] --- Optimal[Optimal: Bounded by Features & Conway's Law] --- TooSmall[Too Small: Nano-services / Chatty Network]

The Pitfalls of Sizing Extremes

  1. Too Big: If a service encompasses multiple distinct domains, it inherits all the problems of a monolith: cross-team merge conflicts, shared deployment bottlenecks, and scaling inefficiencies.
  2. Too Small (Nano-services): Breaking services down by individual API endpoints or functions creates extreme operational overhead. It leads to excessively chatty network communication, high cross-network latency, complex distributed transactions, and tangled cross-team dependencies.

Fencing by Feature Boundaries and Conway’s Law

A reliable starting point for fencing is to follow Conway’s Law, which states that system designs inevitably mirror the communication structures of the organizations that design them.

Align service boundaries with organizational feature ownership:

  • If you are architecting a video streaming platform (e.g., Netflix or Prime Video), structure services around cross-functional team domains: Live Streaming, Authentication, Payments, Notifications, and Catalog Search.
  • Each service is owned end-to-end by a single team with clear accountability.

Progressive Decomposition

Service fencing is dynamic. As the business grows, coarse-grained services can be further decomposed:

  • A generic Payment Service might initially handle the entire transactional lifecycle.
  • At scale, it can be cleanly partitioned into a Pre-Payment Flow Service (handling cart validation, discounts, and currency conversion) and a Post-Payment Flow Service (handling settlement, invoicing, refunds, and reconciliation).

Internal Subcomponents Within a Service Fence

A fenced microservice does not have to be a single binary or monolithic process. A single microservice domain often comprises several coordinated subcomponents:

graph LR
    subgraph Notification Microservice
        API[Notification Ingestion API]
        Queue[(Message Queue)]
        Scheduler[Task Scheduler]
        Workers[Notification Dispatch Workers]
        API --> Queue
        Queue --> Workers
        Scheduler --> Queue
    end

In this example, the entire Notification domain functions as one cohesive microservice to the rest of the organization, even though it internally orchestrates an ingestion API, background schedulers, and asynchronous workers.


Summary Checklist: Should You Adopt Microservices?

Before transitioning from a monolith to microservices, evaluate whether your organization meets the following criteria:

  1. Team Scale: Do you have multiple independent engineering teams whose velocity is actively constrained by coordination bottlenecks, merge conflicts, and shared deployment queues in a single monolith?
  2. Disproportionate Scaling Profiles: Do specific sub-domains of your application experience exponentially higher throughput or resource demands than the rest of the system?
  3. Domain Maturity: Do you understand your business domain well enough to draw stable boundaries, or is the product still pivoting rapidly?
  4. Operational Readiness: Does your team have the operational maturity (CI/CD pipelines, automated testing, observability, distributed tracing) required to manage multiple distributed services?

If the answer to these questions is yes, fencing your application into small, autonomous, and harmonic microservices will unlock higher developer velocity, predictable scaling, and robust fault isolation.

Arpit Bhayani

Principal Engineer II at Razorpay - building Agent Studio, Ex-staff engg at GCP Memorystore & Dataproc, Creator of DiceDB, ex-Amazon Fast Data, ex-Director of Engg. SRE and Data Engineering at Unacademy. I spark engineering curiosity through my no-fluff engineering videos on YouTube and my courses