Don't Start With the Hardest Part
Every migration guide tells you to identify bounded contexts first. That's right, but also misleading — in a working monolith, every domain is coupled to every other through the database. Start with the most isolated, lowest-risk service: in our case, the notification service.
The 5× Win Wasn't From Splitting
The throughput gain came from moving CPU-heavy processing (image analysis, report generation) to dedicated workers with proper queue depth management. The monolith was doing everything synchronously in the request path.
Event-Driven Beats REST for Internal Services
We started with REST between services. It made dependencies explicit but created tight coupling and cascade failures. Switching to event-driven communication (with explicit event contracts) decoupled teams as much as services.
The Hidden Cost: Distributed Debugging
Distributed tracing (we used correlation IDs + structured JSON logs) wasn't optional — it was the difference between 2 minutes to diagnose a bug vs 2 hours.
What I'd Do Differently
Run the monolith and microservices in parallel for 2 weeks minimum. Our 3-day overlap was too short to catch edge cases that only appear in production traffic.