From Monolith to Microservices: A Migration Roadmap for Media Companies

The monolith that is slowing everything down

There is a familiar pattern in how media companies arrive at the situation they find themselves in. The system was built quickly and pragmatically when the platform was small - and that was the right decision. A well-structured monolith is genuinely easier to manage than a prematurely distributed system. But then the platform grew. The team grew. The feature count grew. And the monolith that was once an advantage has become a drag on everything.

The symptoms are well known: a small change to the payment logic requires a full regression of the entire system. The product team wants to ship faster, but every release requires coordination across the whole engineering team. Under peak traffic, the search service collapses and takes the video player down with it. Newly hired engineers take months to become productive in a codebase that nobody fully understands any more.

Migration to microservice architecture is the answer to these symptoms - but it is not a simple answer. Done incorrectly, a microservices migration is one of the most expensive and highest-risk projects an engineering team can undertake. Done correctly, it is one of the most transformative.

This article is a practical guide to how that migration is done in a way that delivers value along the way, limits risk, and leaves the organisation with a system that is genuinely easier to maintain and evolve.

Shapp helps media companies and streaming platforms with API integrations and system architecture. The principles we describe below are those we apply in real migration projects.

When to migrate - and when not to

The most important decision in a microservices migration is whether to do it at all.

The signals that say it is time

Releases take disproportionately long: if it takes days to deploy a change that took hours to build, the coordination cost inside the monolith is a genuine problem. Scaling constraints: if you must scale the entire system to relieve pressure on a specific component - transcoding logic or a search index, for example - that is a signal those components should be separate.

Domain conflicts within the team: when five teams work in the same codebase and their pull requests continuously conflict, the codebase no longer reflects the organisational structure. This is a Conway's Law problem, and microservices offer the opportunity to correct it. Isolation requirements: if security, compliance, or SLA requirements vary dramatically between different parts of the system, those requirements are difficult to accommodate inside a single deployable unit.

The signals that say you should wait

The system is still small: if you have a team of under ten engineers and the system is manageable, a microservices migration is probably premature. Distributed systems carry overhead that a small team cannot justify.

You do not understand the domain well enough: if the system is new and the domain model is still changing rapidly, premature separation is a risk. Incorrectly drawn service boundaries are more expensive to correct in a distributed system than in a monolith. Technical debt is the primary problem: if code quality is poor, microservices will not fix it. You risk distributing poor code rather than refactoring it.

The strangler fig pattern

Strangler fig - named after a species of tree that gradually envelops and replaces its host - is the migration pattern consistently recommended by those who have done this more than once.

The principle

Rather than rewriting the entire system at once (a big bang migration, the highest-risk strategy), you begin by identifying a well-defined, relatively isolated function within the monolith. You build a new, standalone microservice that implements that function. You gradually route traffic to the new service, with the ability to fall back to the monolith if problems arise. Once the new service is stable, you remove the old code from the monolith.

Then you repeat the process for the next function.

Why it works

Strangler fig delivers value at every step. Each new microservice is an improved, tested, isolated system. There is always a safe fallback. The team learns to work in distributed systems gradually, rather than being thrown into full complexity on day one.

It requires a facade layer - typically an API gateway or reverse proxy - that can route requests to either the monolith or the new service based on configuration. This is a component worth investing in early, as it lives on throughout the entire migration process.

Common pitfalls with strangler fig

Starting with the wrong service: do not choose the most complex or business-critical part of the system as the first microservice. Start with something whose boundaries are clear and whose team understands the domain well.

Failing to complete: the most common failure mode is stopping halfway. An organisation with a half-migrated architecture - some functionality in microservices, most still in the monolith - has the complexity of both without the advantages of either.

Service boundaries and domain-driven design

The hardest technical decision in a microservices migration is where to draw the boundaries. Incorrectly drawn boundaries create distributed monoliths: systems that are nominally separate but whose tight dependencies mean they must in practice be deployed and scaled together.

Bounded contexts as the design unit

Domain-Driven Design (DDD) and the concept of bounded contexts provide a useful framework. A bounded context is a domain area with clear internal coherence and well-defined boundaries with the outside world. Within the context, terminology, data models, and business rules are consistent. The boundaries mark where a different team with a different domain model begins.

For a media company, bounded contexts might include: content catalogue (titles, metadata, availability), user management (accounts, subscriptions, authentication), playback (sessions, DRM, quality selection), recommendations (personalisation, rankings), and billing (payments, invoices, SLA reporting).

How to test whether a boundary is correct

A well-placed service boundary should mean the service can be deployed, scaled, and tested independently of all other services. If a service consistently needs to make synchronous API calls to another service to complete an operation, the boundary is probably misplaced - they should perhaps be one service, or the communication should be restructured around events.

Data ownership and eventual consistency

One of the most underestimated challenges in a microservices migration is what happens to the data.

Database-per-service as a principle

Each microservice should own its data and never share a database directly with another service. This is a hard rule that is easy to state and difficult to follow in practice, because shared databases are the most common way services are coupled together in a monolith.

Splitting a shared database requires a deliberate approach. Typical steps: identify which tables belong to which domain. Establish clear ownership rules. Move tables to service-specific schemas or separate database instances. Replace direct JOIN queries with API calls or event-driven data synchronisation.

Eventual consistency and what it means in practice

In a distributed environment, strong consistency - all data is always exactly up to date across all services - is neither achievable nor necessary for most use cases. Eventual consistency - data converges toward a correct state, but with some delay - is the normal condition.

This means you need to design for it: UI that can display stale data with a clear indication, backend logic that handles a state that may be transiently inconsistent, and compensating mechanisms (sagas, idempotent retry flows) for transactions that span multiple services.

Event sourcing and domain events are powerful tools for managing this. Rather than synchronously updating data in service B when service A makes a change, service A publishes an event ("subscriptionStatus updated") that service B listens to and handles at its own pace.

Observability in a distributed system

A monolith is relatively straightforward to debug: a stack trace tells you exactly what happened. In a distributed system, a single user request may span ten services, and a failure in one of them may manifest as a symptom in another.

The three pillars: logs, metrics, and traces

Distributed tracing (exemplified by standards such as OpenTelemetry) is a necessity, not a luxury. Every request must carry a unique trace ID that propagates through all service calls, so you can follow the complete flow - from frontend request to every downstream service - in a unified tracing system.

Structured logs (JSON format with standardised fields) make it possible to correlate logs across services based on trace ID, user ID, or session ID. Per-service metrics aggregation should include latency percentiles (p50, p95, p99), error rates, and queue depth for asynchronous flows.

Alerting and on-call strategy

In a monolith, a single team typically owns the entire system's on-call responsibility. In a microservice architecture, service ownership is distributed, and on-call responsibility should match it. Each service must have defined SLOs (Service Level Objectives) with automated alerts, and an owning team that is accountable for incident response.

This requires organisational and cultural change, not just technology. Migrating to microservices without adjusting team structure and on-call processes is half-finished work.

Migration roadmap: a phased approach

A realistic migration roadmap for a media company looks roughly like this:

Phase 1: Foundations (1-2 months)

Set up observability infrastructure (tracing, centralised logging, metrics). Establish API gateway as the traffic routing point. Define bounded contexts with domain experts and product teams. Select pilot service based on isolation level and team knowledge.

Phase 2: Pilot service (2-3 months)

Build and deploy the first microservice using the strangler fig pattern. Validate infrastructure, deployment pipeline, and on-call process against a real service. Iterate on standards for service-to-service communication, error handling, and API contracts.

Phase 3: Selective extraction (6-18 months depending on system complexity)

Extract additional services in priority order based on business value and isolation level. Build up a shared platform layer (service mesh, shared auth, standardised logging). Continue retiring monolith code as functionality is extracted.

Phase 4: Decommissioning and stabilisation

Eliminate remaining monolith code. Optimise inter-service communication. Formalise service ownership and lifecycle processes.

Summary: migration as strategy, not project

A microservices migration is not a project with an end date - it is a direction and an architectural strategy. The best migrations we have seen treat each extracted service as a delivery with business value, not as a milestone toward a distant goal.

It requires patience, discipline, and an organisation that is willing to invest in infrastructure and platform development before all the benefits are visible. But for a streaming platform or media company with ambitions to scale - technologically and commercially - it is an investment that pays off.

Shapp helps media companies with API architecture and integrations and streaming infrastructure. If you find yourself in a situation where the monolith is starting to slow you down - or if you want to plan a migration proactively - get in touch with us.

Frequently asked questions

How do you know when it is the right time to migrate from monolith to microservices?

Signs that the time has come include release cycles that have become significantly longer, the inability to scale specific parts of the system independently, a single bug that consistently affects overall system stability, and a team whose ability to work in parallel is constrained by tightly coupled code.

What is the strangler fig pattern and why is it recommended?

Strangler fig is a migration strategy in which new functionality is built as standalone microservices while the old monolith is gradually phased out. It is recommended because it enables incremental migration without requiring a big bang rewrite, and because it allows value to be delivered along the way rather than a long period without output.

How do you handle data ownership when splitting a database?

Each microservice should own its own data and expose it through well-defined APIs. When splitting a shared database, techniques such as database replication, domain events, and event sourcing are used to ensure consistency without tight coupling between services.

Keep reading