Modernization · Containerization

Containerizing a 12-year-old Java monolith — without a rewrite

How we used the strangler-fig pattern to move a stateful Spring Boot monolith onto EKS over 9 months, with progressive traffic shifting via Istio and zero "big-bang" risk.

Industry
SaaS · HR Tech
Engagement
T&M
Duration
36 weeks
Team Size
4 engineers
Practices
Modernization · Build/Release
1×
→ 12× / day deploys
68%
Faster build time
45%
Lower compute spend
0
Rollback incidents
// the challenge

12 years of code. Three weekend deploys a year.

The client's flagship HR platform was a single Spring Boot WAR — 2.3M lines, 47 service domains tangled into one deployable, running on a fleet of bare-metal Tomcat servers. Every release required a Saturday-night maintenance window, manual DB migrations, and a rollback runbook that nobody trusted.

The pain wasn't infrastructure cost — it was velocity:

A "rewrite to microservices" RFP had been floated and rejected three times — too expensive, too risky, too long. The client wanted modernization without a rewrite. We agreed.

// our approach

Strangler-fig. One domain at a time.

Instead of decomposing the monolith upfront, we kept it intact, containerized it as-is, and put a service mesh in front. Then we carved off domains one at a time — each new microservice sitting beside the monolith, both routable via Istio.

Phase 01

Containerize As-Is

Multi-stage Dockerfile for the WAR, externalized config via ConfigMaps/Secrets, JVM tuning for K8s.

Weeks 1–6
Phase 02

EKS Foundation

EKS clusters via Terraform, Istio mesh, ArgoCD GitOps, baseline CI in GitHub Actions, observability stack.

Weeks 6–14
Phase 03

Carve First Domain

Identified the "Notifications" bounded context, extracted into a Go service, routed 5% traffic via Istio.

Weeks 14–22
Phase 04

Scale the Pattern

Carved 4 more domains using the same playbook. Monolith shrunk from 47 to 32 domains, deploy slot freed.

Weeks 22–36
// architecture

Mesh-routed strangler topology

Istio sits at the ingress and routes each URL prefix either to the legacy monolith pod or to the new microservice. Header-based routing lets us canary new domains to 1–5% of traffic before promoting.

           Internet ──► ALB ──► Istio Ingress Gateway
                                          
                       ┌──────────────────┼──────────────────┐
                                                            
              /notifications/*                     /* (everything else)
                                                            
            ┌──────▼──────┐                       ┌──────▼──────┐
             notifications                            monolith    
               (Go svc)                             (Spring Boot)
            └──────┬──────┘                       └──────┬──────┘
                                                            
                                                            
                Postgres (shared)                       Postgres (shared)
// technology stack

Tools we shipped with

DockerContainer Runtime
Amazon EKSKubernetes
IstioService Mesh
HelmPackaging
ArgoCDGitOps
GitHub ActionsCI/CD
TerraformIaC
PrometheusMonitoring
OpenTelemetryTracing
LokiLogging
// outcomes

What changed for engineering

MetricBeforeAfterΔ
Deploy frequency1× / quarter12× / day+1080×
Build time (full suite)3.5 hours67 minutes−68%
Lead time for change87 days2.4 days−97%
Onboarding to first commit9 days4 hours−95%
Compute spend (monthly)$98K$54K−45%
Failed-deploy rate22%1.8%−92%
"They convinced us not to rewrite — that turned out to be the most valuable advice we've gotten in a decade. We're shipping more this month than we did all of last year, and the monolith is still running, getting smaller every sprint."
CTO · HR Tech SaaS
// got a monolith to modernize?

Strangler-fig works. We've proven it.

No rewrites, no big-bang cutovers. Just incremental progress your team can ship and trust.

Start a conversation