The first chapter is a vocabulary lesson, and it matters more than it looks. Many arguments about microservices are really arguments about different definitions: one person means "small", another means "runs in Kubernetes", a third means "has its own repo". Newman pins the term down to a few properties and then spends the rest of the chapter on the ideas that actually decide whether a split helps: information hiding, cohesion, coupling and domain boundaries.

It is also the chapter where he defends the monolith. That defence has aged very well. In 2026, with modular monoliths back in fashion, it reads less like a caveat and more like the main point.

Chapter 1 in the book.

Key ideas

Independent deployability is the definition that matters

Newman defines microservices as independently deployable services modelled around a business domain, communicating over networks and hiding their own data. Of all the properties, he singles out independent deployability as the one to take away if you take nothing else: you should routinely be able to change one service and release it to production without releasing anything else.

That single requirement drives most of the others. To deploy alone, a service needs a stable, explicit contract, so consumers do not break when its internals change. To keep that contract stable, the service must hide its implementation, and the database is the biggest piece of implementation there is. Size, language and hosting platform are deliberately left out. Newman calls size one of the least interesting questions and suggests two better ones: how many services can you actually operate, and where do the boundaries go so that changes stay local?

Slice by business capability, not by technical layer

The classic three-tier system (UI team, backend team, database team) is optimised for grouping people by skill, which Conway's law then mirrors in the architecture. The cost shows up when a simple feature, such as letting a user pick a favourite category, needs coordinated changes and releases across all three layers.

Newman's alternative is to group code by business functionality, so one service owns a thin vertical slice: some UI, some logic, its own storage. Technical cohesion is traded for business cohesion. He makes a point worth stressing: server-side decomposition while the UI stays one big deployable often wastes much of the benefit, because every feature still queues behind a single frontend release.

There is more than one kind of monolith

Newman uses "monolith" to mean a unit of deployment, not a judgement about code quality. He distinguishes:

Kind What it is His view
Single-process monolith All code deployed as one process (possibly many instances) The common case, and a valid choice
Modular monolith One deployable built from well-separated modules Often an excellent choice; Shopify is his example
Distributed monolith Several services that must be deployed together The worst of both worlds
Third-party black box Vendor software or SaaS you cannot change Can still be decomposed around its edges

The problem monoliths tend to create is what he calls delivery contention: many people changing the same code, waiting on the same release, unsure who owns what. A monolith does not guarantee contention and microservices do not cure it, but service boundaries give you clearer lines to draw ownership around. Meanwhile, monoliths keep real advantages: simpler deployment, monitoring, testing and code reuse. Treating "monolith" as a synonym for "legacy" is, in his words, a problem.

Four kinds of coupling

Cohesion is code that changes together staying together; coupling is how much changing one thing forces changes elsewhere. Newman offers his own practical classification for distributed systems:

Coupling Example in a course platform How to reduce it
Implementation Reporting reads the Enrollment service's tables directly Hide the schema behind an API or a purpose-built read dataset
Temporal Checkout calls Enrollment, which synchronously calls Billing Caching, or asynchronous messages
Deployment Every service ships together on a fixed release train Release on demand, smaller deployables
Domain Certificates need to know a learner completed a course Unavoidable, but share only what the receiver needs

The fix for implementation coupling is information hiding, Parnas's idea that a module boundary should hide what is likely to change. Newman adds outside-in design: shape a service's interface by asking its consumers what they need, as you would design a user interface, instead of exposing whatever the data model happens to be.

For domain coupling, his trick is to send a purpose-built message rather than a whole entity. The warehouse does not need prices or card details, only what to pick and where to ship it. In my own domain, the certificate service does not need the whole enrollment record:

type Enrollment = {
  id: string;
  learnerId: string;
  courseId: string;
  pricePaidCents: number;
  couponCode: string | null;
  paymentMethodId: string;
  progress: Record<string, number>;
  completedAt: Date | null;
};

type CertificateRequested = {
  enrollmentId: string;
  learnerName: string;
  courseTitle: string;
  completedOn: string;
};

export function toCertificateRequest(
  enrollment: Enrollment,
  learnerName: string,
  courseTitle: string,
): CertificateRequested {
  if (!enrollment.completedAt) {
    throw new Error(`Enrollment ${enrollment.id} is not complete`);
  }
  return {
    enrollmentId: enrollment.id,
    learnerName,
    courseTitle,
    completedOn: enrollment.completedAt.toISOString().slice(0, 10),
  };
}

Whether that message travels as a direct call or as an event the certificate service subscribes to is a separate decision. With an event, the dependency flips: the enrollment side no longer needs to know the certificate service exists.

Just enough domain-driven design

Newman borrows two concepts from DDD. An aggregate is a real domain concept with a life cycle (an order, an invoice, an enrollment), best treated as a state machine whose transitions live together with its state. Crucially, when another part of the system asks an aggregate to change state, the aggregate may refuse. A bounded context is a larger organisational boundary that contains one or more aggregates and hides internal concerns from the rest of the business.

type EnrollmentState = "pending_payment" | "active" | "completed" | "refunded";

const allowedTransitions: Record<EnrollmentState, EnrollmentState[]> = {
  pending_payment: ["active"],
  active: ["completed", "refunded"],
  completed: [],
  refunded: [],
};

export class EnrollmentAggregate {
  constructor(
    readonly id: string,
    private state: EnrollmentState,
  ) {}

  get current(): EnrollmentState {
    return this.state;
  }

  transition(next: EnrollmentState): void {
    if (!allowedTransitions[this.state].includes(next)) {
      throw new Error(`Cannot move enrollment ${this.id} from ${this.state} to ${next}`);
    }
    this.state = next;
  }
}

His guidance on mapping: start with services the size of a whole bounded context, because fewer services are easier to operate while you learn, and split further along aggregate boundaries later. Even then, you can keep presenting a coarse API to consumers, since the internal split is an implementation detail.

Reading it in 2026

  • The modular monolith has gone mainstream. Newman already called it an excellent choice for many organisations in 2019. Since then, teams have become much more willing to say so publicly. The most quoted example is Prime Video's 2023 write-up about consolidating one audio and video monitoring service into a single process, which reported infrastructure cost savings of over 90 percent. Read it carefully: it was one service inside a larger system, not a company abandoning microservices.
  • Module boundaries can be enforced in a monolith. In Java, Spring Modulith can verify that modules only use each other's public API and that dependencies between modules have no cycles. In a TypeScript monorepo, Nx's module boundary lint rule or dependency-cruiser do a similar job. This makes the "modular monolith first" path much more practical than it was.
  • The companion book has a new edition. Building Microservices came out in a second edition in August 2021 and covers communication styles, deployment and testing in far more depth. I found no newer edition of Monolith to Microservices itself.
  • Temporal coupling and tracing. Asynchronous messaging is a default option now rather than an exotic one, and OpenTelemetry, which graduated in the CNCF in May 2026 with traces, metrics and logs all generally available, is the standard way to see a request across services.
  • DDD is still the boundary tool. Bounded contexts and aggregates remain the vocabulary most teams use; the techniques for finding them (covered in the next part) have matured, but the definitions here are unchanged.

Putting it to work

  1. Write down which of your services can be released alone today. Any group that always ships together is a distributed monolith; treat it as one unit until you fix it.
  2. For each service, list who reads its database directly. Every entry is implementation coupling to remove.
  3. Design new endpoints and events by asking the consuming team what they need, and send purpose-built payloads instead of whole entities.
  4. Model key domain objects as aggregates with explicit transitions, and reject illegal ones in code.
  5. If you are on a monolith, make it modular first: one folder or package per bounded context, and a lint rule that forbids imports across their internals.
  6. Before splitting anything, ask how many services your team can actually deploy, monitor and support today.

Check yourself

  1. What is the one property Newman says to take away if nothing else? Independent deployability: routinely changing and releasing one service without deploying anything else.

  2. Why does a three-tier architecture make simple features expensive? Business functionality is spread across all layers, so one feature needs coordinated changes and releases from several teams.

  3. What is a distributed monolith? Multiple services that must be deployed together, carrying the costs of distribution without the benefits of independence.

  4. Give an example of implementation coupling and its fix. One service reading another's tables directly; hide the schema behind an API or a dedicated, stable read dataset.

  5. How should you size your first services? Around whole bounded contexts, splitting along aggregate boundaries later once you can handle more services.

Further reading