Your monolith isn’t "bad"—it’s just outgrowing its skin.
One day, you wake up to:
- 45-minute test suites because "everything depends on everything."
- Deployments feel like Jenga—move one piece, and the whole tower wobbles.
- Two teams waiting on the same migration, cursing each other in Slack.
But before you jump to microservices, let’s ask: Should you split? And if so, how?
When to Split: The 5 Catalysts
1. Bounded Contexts Are Choking
Sign: Your User
model handles:
- Authentication (Devise)
- Profile management (Avatars, preferences)
- Billing (Subscriptions, invoices)
- Analytics (Track logins, activity)
Solution: Split into:
- Identity Service (Auth, roles)
- Billing Service (Subscriptions, payments)
- User Profiles Service (Bio, avatars)
Pattern: Follow Domain-Driven Design (DDB) boundaries.
2. Scaling Constraints Hit
Sign:
- One database table (e.g.,
events
) eats 80% of your CPU. - Teams argue over schema changes.
Solution: Extract the hot table into a service with its own DB.
Example:
Before: Monolith → PostgreSQL `events` table
After: Monolith → Events Service (with dedicated PostgreSQL)
3. Team Silos Form
Sign:
- Team A owns "Orders," but Team B keeps breaking them via "Inventory" changes.
- Merge requests require 5+ reviewers from different teams.
Solution: Extract services along team boundaries (Conway’s Law).
4. Third-Party Dependencies Multiply
Sign:
- Your monolith directly calls:
- Stripe (payments)
- Twilio (SMS)
- SendGrid (email)
- Every API change forces app-wide updates.
Solution: Extract a "Partner Gateway" service to consolidate external calls.
5. Performance Isolation Needed
Sign:
- A background report job locks the DB, killing checkout performance.
- You can’t scale the hot path independently.
Solution: Move batch jobs to a separate service.
How to Split: 3 Incremental Patterns
1. Strangler Fig Pattern
Idea: Gradually replace monolith pieces with services.
Steps:
-
Proxy requests: Route
/api/v2/orders
to new Orders Service. - Sync data: Use event streaming (Kafka) or DB triggers.
-
Kill the old path: Sunset
/api/v1/orders
once v2 works.
Tools:
-
nginx
for routing -
delegate
gem for lazy extraction
2. Branch by Abstraction
Idea: Build the new service inside the monolith first.
Example:
# Old way
class Order < ApplicationRecord
def process_payment
Stripe::Charge.create(...) # Direct call
end
end
# New way
class Order < ApplicationRecord
def process_payment
PaymentService.new.process(order: self) # Wrapped call
end
end
# Later, move PaymentService to its own app
3. Event-Driven Decoupling
Idea: Use events to phase out direct calls.
Before:
# Monolith
class OrderController
def create
@order = Order.create!(...)
InventoryService.update!(@order) # Direct call → tight coupling
end
end
After:
# Monolith
class OrderController
def create
@order = Order.create!(...)
EventStore.publish(OrderCreated.new(@order)) # Loose coupling
end
end
# Inventory Service (separate app)
class OrderCreatedHandler
def call(event)
Inventory.update!(event.order_id)
end
end
Tools:
Rails Event Store
-
Karafka
(Kafka)
When NOT to Split
🚫 "Because microservices are cool": Complexity ≠ maturity.
🚫 If you lack DevOps muscle: No container orchestration? Think twice.
🚫 For trivial domains: A 10-model SaaS app isn’t Shopify.
Rule of Thumb:
"Extract when the pain of splitting is less than the pain of staying."
The Extraction Playbook
-
Start with observability:
- Trace cross-service calls with
OpenTelemetry
. - Log context boundaries (
tagged_logger
).
- Trace cross-service calls with
-
Extract the easiest service first:
- Low-risk, high-independence (e.g., "Email Service").
-
Keep the monolith as the "orchestrator":
- Let it handle auth, routing, and UI.
-
Test aggressively:
- Contract tests (
Pact
) for service boundaries. - Chaos engineering (
Gremlin
) for resilience.
- Contract tests (
"But we’re not Netflix!"
You don’t need to be. Even extracting one service (e.g., Payments) can:
- Speed up deploys for other teams.
- Isolate failures (no more checkout crashes because email broke).
- Unlock tech flexibility (rewrite the service in Go if needed).
Have you survived a monolith split? Share the war story below.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.