Migrating to Microservices: Worst Practices
by James Burns
All migrations from monoliths to microservices start with the same great intentions:
- “Any team will be able to deploy whenever they want!”
- “No more crazy database migration dances!”
- “Once we’re done, we’ll finally be able to refactor our data model to address years of tech debt!”
The reality is that most migrations bog down quickly. Poorly understood goals and tradeoffs end up with a distributed monolith, the worst of all worlds where multiple tightly coupled code bases are deployed often. This worst practices guide will tell you how you too can end up with a distributed monolith at the end of a multi-year long slog.
Start by Moving Data Not Defining Interfaces
Everyone knows that data is what really matters and that the current data model reflects reality, so plan your migration to start by moving as much data as possible to new “service owners” who will serve the current database schema as CRUD services forever. Since not all columns in tables will have clear owners, make sure services with read dependencies keep their own local copy relying on multiple unreliable systems for distributed cache invalidation.
Embed Assumptions about Data Models Everywhere
Create shared libraries that require lockstep updates which are the source of truth for the relationships between shared database ids from one CRUD service to queries for related data on other CRUD services. Make sure that what previously would have been (relatively) simple JOINs now require getting correct and coherent data from at least 3 services.
Design for all Services to Always Be Up
If one of the CRUD services for the new distributed JOIN returns a 500, fail the entire request and return an error page to the customer. Make sure that clients never retry. You may want to sprinkle in some metrics but definitely don’t do distributed tracing, it will distract you from your migration timeline.
The Database is Always Up
There might be 50 times more databases, but they will always be fully available and never have to have downtime for upgrades or have bad migrations. If a database for a tertiary dependency impossibly has an error, then take down all business-critical parts of the website — it’s not safe to keep serving customers.
Require at least Two Company-Wide Migrations a Quarter
Product and engineering teams always have the bandwidth to take on additional non-feature work. Don’t make them search for it, always have a couple of company-wide monolith-migration- related activities a quarter: each at least a sprint long. Every migration should create at least two new direct dependencies and several indirect ones.
If you follow this guide, you’ll successfully maintain tight coupling across your entire engineering organization so you have to coordinate all data model and business logic changes across an ever-growing number of teams. Every deploy will be an opportunity to create a customer-facing outage that will take hours to root cause in the inevitable screaming match that will consume your on-call conference line.
Stayed tuned for our next post: How to Fix Your Agile Velocity Issues by Moving to SOAP.