Episode 6 — Scaling Reliability Microservices Web3 / 6.1 — Microservice Foundations

6.1.b -- When Microservices Make Sense

Microservices are not a universal upgrade -- they are a trade-off. This section gives you a decision framework so you choose the right architecture for your context, not the trendy one.


Navigation << 6.1.a Monolithic vs Microservices | 6.1.b When Microservices Make Sense | 6.1.c Service Boundaries >>


1. The Microservices Premium

Every microservices architecture pays a tax -- an operational overhead that does not exist in monoliths. Before adopting microservices, you must be certain the benefits outweigh this premium.

1.1 What the Premium Includes

Overhead ItemWhat It Costs You
Service discoveryEach service must find others dynamically (Consul, DNS, K8s services)
Distributed tracingDebugging requires correlation IDs and tools like Jaeger or Datadog
Network reliabilityEvery call can fail; you need retries, timeouts, circuit breakers
Data consistencyNo more ACID across services; you must implement sagas and eventual consistency
Deployment pipelinesN services means N CI/CD pipelines to maintain
Monitoring and alertingN services means N dashboards, N sets of alerts, N log streams
InfrastructureContainer orchestration (Kubernetes), load balancers, service meshes
Testing complexityIntegration tests require all dependent services to be running or mocked
Security surfaceEach service endpoint is an attack vector; mTLS, auth between services
Cognitive loadDevelopers must understand distributed systems concepts
 Monolith Complexity                  Microservices Complexity
 +-------------------+               +-------------------+
 |                   |               | Network failures  |
 | Application logic |               | Data consistency  |
 |                   |               | Service discovery |
 +-------------------+               | Deployment x N    |
                                     | Monitoring x N    |
                                     | Application logic |
                                     +-------------------+

 Total complexity is HIGHER with microservices.
 The question is: do the benefits justify it?

2. Decision Framework

2.1 Team Size Thresholds

Team SizeRecommended ArchitectureReasoning
1-5 engineersMonolithA small team cannot absorb the operational overhead
5-15 engineersModular monolithEnforce boundaries inside a single deployable; extract if needed
15-50 engineersSelective microservicesExtract services where scaling or deployment independence is critical
50+ engineersMicroservicesIndependent teams need independent deployables to avoid coordination overhead

Conway's Law: "Organizations design systems that mirror their communication structures." If you have 3 teams, you will naturally end up with ~3 services. Fight it and you lose.

2.2 Codebase Complexity Signals

Ask yourself these questions:

// Decision function (pseudo-code for your brain)
function shouldUseMicroservices(context) {
  const signals = {
    deploymentFrequency: context.deploysPerWeek,
    teamSize: context.engineers,
    codebaseAge: context.yearsInProduction,
    scalingNeeds: context.trafficPatternIsUneven,
    domainComplexity: context.distinctBusinessDomains,
    teamAutonomy: context.teamsNeedIndependentReleases,
  };

  // Strong signals FOR microservices
  const forMicroservices = [
    signals.teamSize > 15,
    signals.deploymentFrequency > 10, // per week
    signals.scalingNeeds === true,
    signals.domainComplexity > 4,
    signals.teamAutonomy === true,
  ];

  // Strong signals AGAINST microservices
  const againstMicroservices = [
    signals.teamSize < 5,
    signals.codebaseAge < 1,
    signals.deploymentFrequency < 3,
    signals.domainComplexity < 3,
  ];

  const forCount = forMicroservices.filter(Boolean).length;
  const againstCount = againstMicroservices.filter(Boolean).length;

  if (forCount >= 3) return 'MICROSERVICES';
  if (againstCount >= 3) return 'MONOLITH';
  return 'MODULAR_MONOLITH'; // the safe middle ground
}

2.3 The Decision Matrix

Rate each factor from 1 (low) to 5 (high):

FactorScore 1-2 (Monolith)Score 3 (Modular Monolith)Score 4-5 (Microservices)
Team size< 88-20> 20
Deploy frequency neededWeeklyDailyMultiple per day
Domain complexitySingle domain2-3 subdomains4+ distinct domains
Scaling varianceUniform trafficSome hot spotsHighly variable per feature
Uptime requirements99%99.9%99.99% (per service)
Technology diversity needsSingle stackMinor varianceDifferent stacks per service
Regulatory / compliance boundariesNoneSomeStrong isolation required

Scoring:

  • Total 7-14: Start with a monolith.
  • Total 15-21: Build a modular monolith; prepare to extract.
  • Total 22-35: Microservices are justified.

3. When Microservices ADD Unnecessary Complexity

3.1 Small Teams (< 5 Engineers)

Developer 1: writes User Service, Order Service, deploys both,
             monitors both, debugs distributed transactions,
             maintains two CI/CD pipelines, handles service
             discovery...

Developer 1: (also) builds features, fixes bugs, talks to
             customers, and sleeps occasionally.

Result: Burnout. Slower development. More bugs.

A small team wearing microservices is like a solo hiker carrying a tent designed for 20 people.

3.2 Simple Domains

If your application has a single, well-understood domain (a blog, a portfolio, a simple CRUD app), microservices offer no benefit. There is nothing to decompose.

3.3 Early-Stage Startups

The first priority of a startup is finding product-market fit. That means:

  • Rapid iteration
  • Frequent pivots
  • Throwing away code regularly

Microservices slow all of this down. You cannot pivot quickly when you need to update 5 services, 5 databases, and 5 deployment pipelines for a single feature change.

3.4 When You Do Not Have DevOps Maturity

Microservices require:

  • Automated CI/CD
  • Container orchestration (or equivalent)
  • Centralised logging and monitoring
  • Service discovery
  • Automated testing infrastructure

If you do not have these, microservices will be a nightmare.


4. Warning Signs You NEED Microservices

SignalWhat It Looks Like
Deployment queueTeams wait days to deploy because they share a release train
Merge conflict hellMultiple teams constantly conflict in the same files
Scaling wasteYou scale 10x for one hot endpoint, wasting resources on cold modules
Blast radius fearA bug in payments crashes the entire system
Onboarding takes weeksNew engineers cannot understand the full codebase
Different scaling profilesChat needs WebSockets; billing needs batch processing; search needs Elasticsearch
Compliance boundariesPCI-DSS requires payment data isolation; HIPAA requires health data isolation
Release bottleneckOne team's delay blocks all other teams

4.1 Real Scenario: The Deploy Queue

Timeline of a monolith deploy at a 30-person company:

Monday:    Team A merges feature. Wants to deploy.
           Team B says "wait, we're not done testing."
Tuesday:   Team B finishes. Team C says "we have a hotfix, let us go first."
Wednesday: All three teams merge. Deploy breaks -- Team A's code conflicts
           with Team C's hotfix.
Thursday:  Rollback. Debug. Fix.
Friday:    Finally deploy. 4 days for a feature that was ready Monday.

With microservices:
Monday:    Team A deploys their service. Done. 15 minutes.
           Team B and C are unaffected.

5. Warning Signs You DO NOT Need Microservices

SignalWhat It Looks Like
"Everyone is doing it"Following trends without evaluating fit
Resume-driven developmentChoosing tech to pad resumes, not to solve problems
"We might need to scale"Premature optimisation -- you are solving a problem you do not have
Team of 3Operational overhead will crush velocity
No CI/CDYou are adding distributed complexity to a system that cannot even auto-deploy a monolith
Single domainNothing to decompose
Tight deadlinesMicroservices slow down initial development

5.1 The YAGNI Principle

You Ain't Gonna Need It.

// What people imagine when starting with microservices:
//
//   "In 2 years we'll have millions of users and 50 engineers!"
//
// What actually happens:
//
//   Month 1: Spent setting up Kubernetes instead of building features.
//   Month 3: Lost a customer because the competitor shipped faster.
//   Month 6: Pivoted. Now 3 of your 5 services are irrelevant.
//   Month 9: Back to a monolith. Lesson learned.

6. The Evolutionary Approach

The safest strategy is start simple, evolve when needed:

Stage 1: Monolith
  - Ship fast
  - Learn the domain
  - Find product-market fit

Stage 2: Modular Monolith
  - Enforce module boundaries
  - Separate concerns with clear interfaces
  - Prepare data models for eventual splitting

Stage 3: Selective Extraction
  - Extract the module that has divergent scaling needs
  - Extract the module that has the highest deployment frequency
  - Keep everything else in the monolith

Stage 4: Full Microservices (if warranted)
  - Only reached by large teams with complex domains
  - Many successful companies never reach this stage

6.1 Code Example: Preparing for Extraction

Build your monolith so modules are extractable:

// BAD: Tight coupling -- Order module directly accesses User table
app.post('/orders', async (req, res) => {
  const user = await pool.query('SELECT * FROM users WHERE id = $1', [req.body.userId]);
  // ... creates order
});

// GOOD: Loose coupling -- Order module calls User module through an interface
// This interface can later become an HTTP call without changing the Order module.

class UserClient {
  constructor(pool) {
    this.pool = pool;
  }

  async getUser(id) {
    const result = await this.pool.query('SELECT * FROM users WHERE id = $1', [id]);
    return result.rows[0] || null;
  }
}

// When you extract User Service, you swap the implementation:
class UserClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }

  async getUser(id) {
    const res = await axios.get(`${this.baseUrl}/users/${id}`);
    return res.data;
  }
}

// The Order module's code does NOT change -- it still calls userClient.getUser(id).

7. Case Studies

7.1 Segment (Went Back to Monolith)

  • Built microservices early with a small team.
  • Overhead of maintaining 100+ services with 10 engineers was unsustainable.
  • Consolidated back to a monolith. Development velocity tripled.
  • Lesson: Microservices at the wrong scale actively harm you.

7.2 Shopify (Modular Monolith)

  • One of the largest Rails applications in the world.
  • Chose a modular monolith over microservices.
  • Enforced strict module boundaries (no cross-module database access).
  • Achieved independent team velocity without the distributed systems tax.
  • Lesson: The modular monolith is a viable long-term architecture.

7.3 Uber (Scaled to Microservices)

  • Started as a monolith.
  • As the company scaled to thousands of engineers and dozens of products, the monolith became a bottleneck.
  • Migrated to ~2,000 microservices.
  • Built extensive internal tooling (service mesh, deployment platform, tracing).
  • Lesson: At extreme scale with extreme resources, microservices are necessary.

8. The Cost-Benefit Checklist

Before committing to microservices, ensure you can answer "yes" to at least 4 of these:

[ ] We have 15+ engineers (or will within 6 months).
[ ] We deploy more than once per day (or need to).
[ ] Different parts of the system have different scaling needs.
[ ] Different teams need to deploy independently.
[ ] We have (or can build) CI/CD, monitoring, and orchestration infrastructure.
[ ] Our domain has 4+ clearly distinct business capabilities.
[ ] We have experienced distributed systems engineers on the team.
[ ] We have already hit concrete pain points with the monolith.

If you checked fewer than 4: stay with a monolith or modular monolith.


9. Key Takeaways

  1. Microservices are a trade-off, not an upgrade. They solve organisational and scaling problems at the cost of distributed systems complexity.
  2. The microservices premium is real. Service discovery, distributed tracing, data consistency, N pipelines -- this overhead does not vanish.
  3. Team size is the strongest signal. Small teams should not use microservices. Period.
  4. Start with a monolith. Even if you plan to use microservices eventually, starting with a monolith lets you learn the domain first.
  5. The modular monolith is underrated. It gives you boundary enforcement and team independence without the distributed systems tax.
  6. "We might need to scale" is not a reason. Solve problems you have, not problems you imagine.
  7. Going back is okay. Segment, SoundCloud, and others consolidated services. It is not a failure -- it is pragmatism.
  8. Prepare for extraction, do not extract prematurely. Build clean interfaces between modules so you can split when the time comes.

10. Explain-It Challenge

  1. Your engineering manager reads a blog post about microservices and says "let's do it." Your team has 4 engineers, the product launched 3 months ago, and you have 500 users. Write the Slack message you would send explaining why this is premature.

  2. Explain the "microservices premium" to a non-technical product manager. Use an analogy (e.g., restaurant kitchen, construction crew, etc.) to make it tangible.

  3. A company has 25 engineers, deploys twice per week (wants daily), has 3 distinct business domains, and recently had an outage because a bug in the reporting module took down the checkout flow. Using the decision matrix from Section 2.3, score each factor and recommend an architecture.


Navigation << 6.1.a Monolithic vs Microservices | 6.1.b When Microservices Make Sense | 6.1.c Service Boundaries >>