Back to Deployment & Containers
Q1Question 1 of 2

What makes a microservice truly independently deployable

Key Concepts
  • Independent deployability means you can build, test, and release a service to production on its own schedule without coordinating with any other service.
  • Requirement 1: own codebase and CI/CD pipeline so a commit triggers only that service's build and deploy.
  • Requirement 2: private database that no other service touches, so schema changes never force lockstep deploys.
  • Requirement 3: backward-compatible API and event contracts so old callers keep working during a rollout.
  • Requirement 4: no shared-library lockstep where a version bump forces every service to redeploy together.
  • Independent deployability, not line count or service size, is the real test of microservices vs a distributed monolith.
  • The test: 'Can I deploy this service alone, right now, without a meeting?'

Definition

Independent deployability means own pipeline, private database, backward-compatible contracts, and no shared-library lockstep; if services must ship together, you have a distributed monolith, not microservices.
  • Own codebase and pipeline – a commit to the Payment Service triggers only the Payment Service's build, tests, and deploy.
  • Private database – the Payment Service owns its tables; no other service connects to them.
  • Backward-compatible contracts – old callers keep working during a rollout; use additive changes and expand-and-contract.
  • No shared-library lockstep – shared libraries are versioned so services upgrade on their own schedule.
  • The real test – independent deployability, not size, determines whether you have microservices or a distributed monolith.

At National Bank of India, the Payment department and the Account department are truly independent only if the Payment department can renovate its counter on Monday without closing the Account department too. That means each has its own staff and procedures (codebase and pipeline), its own private ledger (database), a promise that old forms still work while new ones roll out (backward-compatible contracts), and no rule that says 'if head office changes the stamp, every department must change on the same day' (no shared-library lockstep).

Understanding the Concepts

What does independently deployable mean?

A microservice is independently deployable when you can build, test, and release it to production on its own schedule without coordinating a simultaneous release of any other service.

TEXT
Independent deployment test:
Question: Can I deploy this service alone, right now, without a meeting?
Answer YES -> microservice
Answer NO  -> distributed monolith

Why does own codebase and pipeline matter?

Each service lives in its own repository with its own CI/CD pipeline. A commit to the Payment Service triggers only the Payment Service's build, tests, and deploy.

YAML
# .github/workflows/payment-service.yml
name: Payment Service CI/CD
on:
  push:
    branches: [main]
    paths: ['payment-service/**']  # only triggers for this service

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: cd payment-service && mvn clean package
      - name: Test
        run: cd payment-service && mvn verify
      - name: Deploy
        run: kubectl apply -f payment-service/k8s/
TEXT
Expected behavior:
Commit to payment-service/ -> only payment-service pipeline runs
Account Service is NOT rebuilt, NOT redeployed, NOT affected.

Why does private database matter?

The Payment Service owns its tables and no other service connects to them. The moment two services share a schema, a change to a column forces both to redeploy in lockstep.

JAVA
package com.bank.payment.model;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import java.math.BigDecimal;
import java.time.Instant;

@Entity
public class Payment {

    @Id
    private String paymentId;
    private String fromAccountId;  // ID reference only, NOT a JPA relation
    private String toAccountId;    // ID reference only, NOT a JPA relation
    private BigDecimal amount;
    private String status;
    private Instant createdAt;
    @Version
    private Long version;

    // Payment Service owns this table exclusively.
    // Account Service NEVER queries this table.
    // If Account Service needs payment data, it calls Payment Service API
    // or consumes payment-events from Kafka.
}
TEXT
WRONG: Account Service queries Payment Service's database directly.
  -> Schema change in payments table breaks Account Service.
  -> Both must deploy together.

CORRECT: Account Service calls Payment Service API or consumes events.
  -> Schema changes are internal. Only Payment Service redeploys.

How do backward-compatible contracts work?

During a rolling deploy, old and new versions run at the same time. A new version must not break old callers.

JAVA
package com.bank.payment.dto;

import java.math.BigDecimal;

// Version 1: original request
public record TransferRequestV1(
    String fromAccountId,
    String toAccountId,
    BigDecimal amount
) {}

// Version 2: added optional reference field
// Old clients omit it -> still works (backward compatible)
public record TransferRequestV2(
    String fromAccountId,
    String toAccountId,
    BigDecimal amount,
    String reference  // optional, defaults to null
) {}
TEXT
Expand-and-contract pattern for breaking changes:

Phase 1 (EXPAND): Support both old and new.
  POST /api/v1/transfers -> old format accepted
  POST /api/v2/transfers -> new format accepted

Phase 2 (MIGRATE): Move all callers to v2.

Phase 3 (CONTRACT): Remove v1.
  Now safe to remove old format.

What is shared-library lockstep?

A shared library of DTOs or utilities is convenient until a version bump forces every service to upgrade and redeploy on the same day.

JAVA
package com.bank.antipattern;

// WRONG: Shared library with DTO used by 5 services.
// Version bump 1.0 -> 2.0 forces all 5 to redeploy together.
public class SharedTransferDto {
    // private String fromAccountId;
    // Adding a field here forces recompile + redeploy of ALL consumers.
}

// CORRECT: Each service has its own DTO.
// Payment Service has PaymentTransferDto.
// Account Service has AccountTransferDto.
// Small duplication, but no lockstep.
TEXT
Shared library lockstep symptoms:
- Version bump in shared-lib requires all services to upgrade
- Cannot deploy one service without upgrading shared-lib first
- One library change triggers 5+ pipeline rebuilds

Fix: keep shared libraries tiny, version them,
prefer duplication of a small DTO over coupling.

How do you verify independent deployability?

The test is simple: has this service ever gone to production alone? If the honest answer is never, the boundary is fake.

TEXT
Verification checklist:
[x] Own repository or clearly separated module
[x] Own CI/CD pipeline triggered only by its own changes
[x] Private database with no shared tables
[x] API changes are additive (no breaking changes)
[x] Kafka schema evolution is additive (with registry)
[x] No shared-library version bump forces coordinated deploy
[x] Has deployed to production independently at least once

What is a distributed monolith?

A distributed monolith is a system that looks like microservices but requires coordinated deploys. It has all the operational pain of distributed systems with none of the independence benefit.

JAVA
package com.bank.antipattern;

// WRONG: Two "services" that always deploy together.
public class DistributedMonolith {
    // Transfer Service calls Limit-Check Service synchronously.
    // They share a database.
    // They always deploy at the same time.
    // Every feature touches both.
    // This is a monolith with network hops.
}

// CORRECT: Merge them back into one service.
public class ProperMonolith {
    // Limit checking is a method inside Transfer Service.
    // One database, one deploy, one transaction.
    // Split only when a separate team needs independent ownership.
}

What should a senior engineer know?

Size is a distraction. A tiny service that must always ship with another is a distributed monolith. A larger service that releases on its own is a proper microservice. The real test is always: 'Can I deploy this alone, right now, without a meeting?'

Visual Overview

DIAGRAM
  Four requirements for independent deployability:

  ┌───────────────────────────────────────────────┐
  │              INDEPENDENT DEPLOY                │
  │                                                │
  │  1. OWN PIPELINE                               │
  │  ┌──────────────────────────────────────┐     │
  │  │ payment-service repo -> CI/CD -> K8s │     │
  │  └──────────────────────────────────────┘     │
  │                                                │
  │  2. PRIVATE DATABASE                           │
  │  ┌──────────────────────────────────────┐     │
  │  │ payment_db: only payment-service     │     │
  │  │ account_db: only account-service     │     │
  │  └──────────────────────────────────────┘     │
  │                                                │
  │  3. BACKWARD-COMPATIBLE CONTRACTS              │
  │  ┌──────────────────────────────────────┐     │
  │  │ Add fields, keep old endpoints       │     │
  │  │ Expand -> Migrate -> Contract        │     │
  │  └──────────────────────────────────────┘     │
  │                                                │
  │  4. NO SHARED-LIBRARY LOCKSTEP                 │
  │  ┌──────────────────────────────────────┐     │
  │  │ Version libs, prefer duplication     │     │
  │  │ Services upgrade on their schedule   │     │
  │  └──────────────────────────────────────┘     │
  └───────────────────────────────────────────────┘

Key Takeaway

Independent deployability means you can build, test, and release one service to production on its own schedule, without a meeting and without coordinating with any other service. Four things make it real: own codebase and pipeline, private database, backward-compatible contracts, and no shared-library lockstep.

At National Bank of India, the Payment Service has its own repository, its own CI/CD pipeline, and its own database. When Pankaj changes the payment settlement logic, only the Payment Service pipeline runs. The Account Service is not rebuilt, not redeployed, not affected. API changes are additive: new fields are optional, old endpoints stay alive until all callers migrate, and Kafka schemas evolve additively through a registry.

The judgment I always emphasize is that size is a distraction. A tiny service that must always ship alongside another is a distributed monolith that pays the network cost of microservices while keeping all the coupling of a monolith. A larger service that releases on its own schedule is a proper microservice.

Remember one sentence: the real test of microservices is not how small the service is, but whether you can deploy it alone, right now, without coordinating with another team.

---

Share
Use ← → arrow keys to navigate