How a B2B SaaS Company Strangled Its Rails Monolith Into Microservices Without a Big Bang Rewrite

A 120-engineer SaaS team used the strangler fig pattern to extract five domains from a 9-year-old Rails monolith, cutting deploy time from 47 minutes to 6 and reducing p99 latency by 62%.

Architecture · intermediate · 12 min read · 2387 words · By Sandip Das · Published

Composite scenario: built from common industry patterns; the figures are illustrative.

What this covers

  1. The Situation
  2. The Problem
  3. Constraints
  4. Options Considered
  5. The Solution
  6. Implementation
  7. Results
  8. What Went Wrong Along the Way
  9. Lessons Learned
  10. Apply It Yourself

Key numbers

MetricBeforeAfter
End-to-end deploy time47 minutes6 minutes (monolith), 3.5 min avg (services)
p99 API latency at peak1,840 ms690 ms
Full-platform incidents per month (avg)3.70.4
Shared RDS peak CPU94%41%
Net additional monthly AWS spendbaseline+$13,200/month

Key components

  • Strangler Router (Nginx) — Routes requests to either the legacy monolith or an extracted service based on a version-controlled route map, enabling incremental traffic shifting without client changes.
  • Shadow Mode Comparison Harness — Mirrors live production traffic to the new service, compares responses to the monolith, and logs diffs without affecting users — the primary safety gate before any traffic shift.
  • Amazon MSK (Kafka) Event Bus — Decouples the monolith from extracted services during the dual-write transition period and becomes the permanent async communication backbone after cutover.
  • Packwerk Boundary Enforcement — Enforces package-level access rules inside the Rails monolith to make cross-domain dependencies visible and measurable before extraction begins.
  • Nightly Reconciliation Script — Compares rows in the legacy database against the new service's database nightly, providing evidence of data consistency before committing to cutover.
  • Terraform Microservice Module — Provides a repeatable, opinionated template for provisioning each new service with its own RDS instance, Kafka topics, autoscaling, and observability wiring.

The Situation

This is a composite scenario assembled from patterns observed across multiple B2B SaaS migrations; all figures are illustrative and internally consistent, not drawn from any single organisation.

The company in question is a 120-engineer B2B SaaS business selling workflow-automation tooling to mid-market enterprises. The product had been in production for nine years. The core application was a Ruby on Rails 5.2 monolith running on AWS, backed by a single RDS Postgres 13 instance (3 TB, 1,400 tables), and deployed as a set of Puma processes behind an Application Load Balancer. The engineering organisation had grown from 8 to 120 engineers over that period, but the codebase had not been meaningfully restructured. By the time this migration began, the repo contained 740,000 lines of Ruby, 2,100 ActiveRecord models, and no enforced domain boundaries.

The platform team owned infrastructure: EKS 1.28 clusters in two AWS regions, Terraform for provisioning, ArgoCD for GitOps delivery, and Datadog for observability. Feature teams owned product domains but had no independent deploy pipelines — every change, regardless of size, went through the monolith's single CI/CD pipeline.

The rest of this case study — the trade-offs, the scaling numbers and the build prompt — is available to LearnXops members. Create an account or see membership.

Topics: architecture, microservices, strangler-fig, rails, kubernetes, migration

Related case studies

Browse all case studies