One Engineer’s Notes from Moving a Monolith to Microservices
This story comes from one of our engineers who recently led the backend restructuring for a long-time client. They had a Rails monolith that had grown organically over six years. Originally built for speed, it now supports multiple teams, environments, and use cases. It worked, but just barely.
The deployment became tense. A single controller change risked affecting four product areas. CI runs took close to 40 minutes. Onboarding new developers required reading internal documents and hoping not to touch the wrong part of the codebase.
Leadership asked if it was time to move to microservices. But it wasn’t a flip-a-switch decision. The risk of breaking production was real. There was no room for abstract architectural discussions. This had to be deliberate, hands-on, and gradual.
The Problem
The monolith had become unreasonably brittle.
Code ownership was unclear. Changes in one part often triggered bugs in another.
Feature flags were everywhere. Some branches had been toggled off for over a year.
Tests were slow. Unit and integration tests blurred together. Failures were non-deterministic.
Deployment pipelines were monolithic, too. A hotfix for authentication would delay shipping a marketing page redesign.
Worse, engineers no longer trusted the system. They started building logic outside the monolith, in unofficial scripts or bypass APIs. Fragmentation had already begun.
The Task
We didn’t set out to rebuild everything. That’s a trap.
Instead, we asked one question: What is the smallest slice of the system we can extract that makes the whole more stable?
Our goal was to start carving out microservices only where they added clarity or speed. Not for the sake of using Kubernetes or hitting an architecture goal. The north star was simple. Better developer experience, faster iteration.
The Actions We Took
1. Mapped the coupling points.
We made a list of deeply entangled subsystems. Payments. Authentication. Notifications. Reporting. Then we scored each on three things:
How often it changed.
How often it broke things.
How many teams relied on it.
Notifications stood out. It was noisy. Email and SMS logic were deeply embedded across controllers. Engineers kept rewriting similar logic. It became the test case.
2. Created a shadow service.
We built a small notification service in Node.js with a lightweight API. The monolith would still send notifications, but now it also mirrored those calls to the new service. That let us run it quietly in the background, watch logs, and validate correctness.
No traffic was routed through it yet. But we could observe everything in real time.
3. Slowly redirected traffic.
Once we confirmed the service was behaving as expected, we changed one feature flag. A specific notification flow now used the microservice. Then another. This approach gave us rollback safety. If the new path failed, the monolith still had the fallback logic.
Over three weeks, we migrated all notification flows. The old code was deleted. Fewer flags. Fewer surprises.
4. Documented patterns, not rules.
After the first service, the team wanted to extract more. We resisted the urge to create a rigid microservices framework. Instead, we documented what worked:
Always start with a single use case
Mirror traffic before cutting over
Own logs and monitoring from day one
Keep contracts small and stable
This helped avoid overengineering. Every new service had to justify its existence.
The Results
Notification-related bugs dropped significantly
Deployment speed has improved since fewer changes touched shared code
Engineers were more confident making changes to isolated services
The team avoided adding new logic to the monolith out of habit
Most importantly, the process gave the team a mental model. Microservices were no longer just an abstract technical goal. They were a tool for organizing work and reducing stress.
Takeaway
Moving from a monolith to microservices is less about architecture and more about behavior.
If your codebase feels slow and unsafe, don’t assume microservices are the answer. Start by identifying friction. Identify areas where ownership is unclear or where change poses a risk. Then carve those pieces out carefully.
You don’t need to split the whole thing. Just make one part better. Then repeat.