A Real-World AI Case Study

Modernize Legacy Micro-Service

A Regenerative Story

A live system, rebuilt — without a human needing to understand a line of its code.

Narrated — best with sound on

Jon Leahy

Staff Engineer, Operations UI

Pismo, A Visa Company

July 2026 · v1.0.0

The Go gopher
1 / 42

Case Study

Modernize Legacy Micro-Service

A Regenerative Story

Pismo

A Visa Company

700K
calls a day
0
lines of Groovy a human had to understand
0
customers noticed

What if the fastest way to replace
a system nobody understands

…is to never read its code at all?

Let me tell you that story.

The problem — one unowned legacy service

700K
calls a day
6 mo
security deadline — new owner
~50
endpoints — 3 days to a month each, manually

At that pace, this job
had years in it

What if it took
months, not years?

AI as the developer — inside a structured, verifiable process.
No human reads the old code. Every request proves the match.

This is the story of getting to that goal.

And at the end, a thought to leave with: this would be worth a pilot on another real microservice.

Before we start

A new process was never the goal.
It emerged.

A case study, not a sales pitch — and fair warning: the slides make it look smooth.
It wasn't. A rough pass, like most real development — and not every edge case got pressure-tested.

Everything you'll see happened — in production, on a live system.
The one claim left to prove: that it generalizes.

2018

crm-api was born

Built before standards existed. It served the customer data our largest banking client used to populate their CRM — the backbone of that relationship.

Seven Quiet Years

2018 2020 2022 2024 2025 Usage — heavy, every single day steady or growing — the records don't say; it never stopped Attention — fading, year on year

Our largest banking client used it every single day — steady or growing, it never stopped.
Pismo moved its attention to other things. Nobody noticed the knowledge leaving.

The Team?

GONE

And with the team went the knowledge of what actually mattered.
Retiring it had been proposed — always too much, always backlogged.

It took a change of ownership to bring it back into focus.

The change of ownership

Visa

Pismo had a new owner

And with Visa came a stricter bar for security.

SECURITY ALERT

CVE Vulnerability

Groovy runtime. Patching meant deep Groovy/Java surgery — and nobody on the team wrote either.

Remediation was now mandatory. Migration was the only road open to us.

The new security regime set a deadline.
No Groovy expertise in-house: the way out was a totally new version.
The deadline moved back a couple of times — twelve months passed before the issue closed for good.

Three Challenges

1

Too Much Surface

A sprawling API — the surface described everything, prioritised nothing.

2

Complex Endpoints

The endpoints that mattered were the most complex ones.

3

Language Change

The company's default language had moved to Go — and nobody on the team wrote Groovy or Java.

Meanwhile, on other teams

The patch path

Upgrading the version for security was a major challenge in itself —
even for a well-understood service, actively maintained by its own squad.

And at the end of it: still Groovy. Still legacy.

For an unowned service with incomplete docs? That road wasn't even open.

The Plan: Two Tracks

Track one

Migrate the API

Replace Groovy with modern Go.
Like-for-like. Invisible to callers.

Track two

Move the clients off

Help consumers migrate away —
so the system could one day retire.

Fix the risk now. Retire the system eventually. Both happened.

Track one began as a standard manual rewrite — AI wasn't the plan yet.

The Code We Found

CustomerStatus.groovy
def mapStatus(customer) {
    // TODO: review this later
    return customer.status == 7 ? "ACTIVE" : "INACTIVE"
}

7 — Why seven? What are the other six? Nobody could tell us.

27,000 lines of this — and none as simple as the sample above.
We reverse-engineered by hand — it's where the manual months went. The responses carried 200 properties. That's a lot of archaeology.

The best migration
is deletion

27K
lines of code
80%
was noise

Access logs, tracked over time, showed which endpoints actually mattered.
10 migrated — the other 40 were never ported.

50 → 10 ported → 2–3 live by the end: track two — the client-facing teams —
kept switching endpoints off, even after cutover.

The Paradox

On the org chart

“Minor, at Pismo's scale”

Resourced — but minor next to the big platform services.
Incomplete docs. Day to day, a single developer.

In reality

700K

calls a day

A bank's backbone.
Getting it wrong wasn't an option.

Low on the radar — yet failure would cost real money.
The perfect proving ground is exactly this shape.

How many services in your estate have exactly this shape?

We Tried Everything

More developers — onboarding cost outgrew output ✗ Failed
Manual porting from docs and observed behavior — three months, barely two endpoints ✗ Failed
Writing specs from code — no oracle: nobody knew what "correct" was ✗ Failed
Traditional TDD — same wall: no one could say what to assert ✗ Failed

Months in, one thing was clear: uncertainty doesn't shrink with headcount.

What if I stopped trying to

understand the code?

Not the AI — me. Behavior, not comprehension,
became the source of truth.

AI Spec TDD

Probe Legacy
Capture Tests
</>
Generate Code
Verify Match
Fix & Loop

The running system IS the specification.

The AI references both systems — old and new — works out each difference,
runs the tests, fixes it, and loops. All the code: AI. Every merge: human-gated.

The new system: Go, hexagonal architecture, test-driven from the first line.

A fair question

Why not use AI
from day one?

Because we couldn't — AI at Pismo was patchy until early 2026.

Experimental access. Inconsistent tooling. Consistent AI arrived in January
and this project's development stopped within a couple of months.

Enough time to finish the port. No time to design a process.

AI has been my personal passion for the last two to three years — which is how
I knew this was the only sensible route. The process took shape in hours outside work.

Capturing Behavior

probe-legacy.sh
$ curl legacy-api/customer/38412
{
  "status": "ACTIVE",
  "document": "00123456000189"
}

# → Golden test case captured

Each response becomes a golden test case — quirks and all.
Status codes, zero-padded documents, magic values: captured, not explained.

No Test Data? Reverse-Engineer It

The endpoint tests needed real customers to run the queries against.

probe-customers.sh
$ ./probe --surname "aab"   # 3-letter search minimum
  → 0 matches
$ ./probe --surname "smi"
  → 12 customers found
$ ./probe --surname "zzz"
  → search space exhausted

90 customers recovered — via the test platform's own API

The customer table was protected — unqueryable. Without it: hand-populating hundreds of customer test cases. So an AI-written script walked every surname from aaa to zzz instead.
Test platform, sanctioned — and yes, that's an enumeration vector. We flagged that too.

90
customers recovered
180
tests run — across our few endpoints

Not everything —
but enough

90 recovered customers, 180 tests run across our few endpoints.
The golden test corpus was live — the spec nobody ever wrote.

Here's what surprised me

The AI replicated
the bugs

Like-for-like means bugs too. We weren't fixing the system.
We were replicating it exactly.

They weren't even known bugs. Only we knew they were wrong.
To our customers, they were the contract.

The Moment of Truth

verify.sh
Testing: /customer/38412
  Legacy:  { status: "ACTIVE", document: "00123456000189" }
  Modern:  { status: "ACTIVE", document: "00123456000189" }
  ✓ EXACT MATCH

Testing: /customer/search?surname=smi
  Legacy:  12 results, same order
  Modern:  12 results, same order
  ✓ EXACT MATCH
2
endpoints in 3 months — manual
hours
to repeat the solution per endpoint — once the harness ran

I couldn't
believe the tempo

Everything still needed landed within the two-month AI phase.

Verify in Production — Safely

Customers always got the legacy answer. Always. Read-only query traffic — nothing to double-fire.

request answers the customer mirrored copy differences → failing tests Customer Shadow Proxy routes + compares Legacy · Groovy still serving every customer New · Go answering in the shadows Diff Log → TDD backlog

Ran for months, day and night — an autonomous feedback loop.

It caught things
nobody knew existed

Edge cases no hand-written test would ever cover.

The cutover gate: zero diffs — sustained.

The Fix Loop

Started locally — landed in production: the new version ran as a shadow process, zero effect on prod.

1 Run the tests — pick the most common difference. Investigate. Fix.
2 AI reads both codebases — original Groovy, new Go — and runs both locally to trace the source.
3 Failing test first — then the code that satisfies it.
4 TDD keeps it safe — the full suite re-runs as it develops; nothing already built breaks.
5 The fix lands as a pull request — and the new system grows a little more complete.
6 Canary vs stable — error rates and traffic matching verified before every switch.

Part ideal, part early-days — the rough version worked; this smooth is well within reach.

Unattended: capture, diffing, test generation. Human-gated: every PR merge, every canary promotion.

I used AI to script the cycle — then gave AI the scripts

Bare infrastructure — so AI reverse-engineered the pipeline too.

🏷️

Tagging

auto-create release tags

⚙️

Codefresh

trigger CI/CD pipelines

📦

Auto-deploy

straight to ext, scripted

🚀

ArgoCD

promote deployments

📊

Grafana

query logs, feed back

A huge time compression: every turn of the loop went from
hours to minutes — the AI running the loop with scripts it wrote itself.
Looks easy here. It wasn't — getting these scripts reliable was its own rough, iterative slog.

What It Took

3 mo
Investigation
4 mo
Manual port — stalled
2 mo
AI — finished
3 mo
Shadow verify & governance

12 months in a fintech, red tape included

👤 1 engineer full-time — all of the AI work, solo 👥 3 teammates, a few weeks each — on the manual phase 🛡️ security, architecture & governance reviewers

Seven of those months were the price of not yet having the method.
Run it again: 6–8 months — no stalled port — ~1 engineer at the core,
plus a few teammate-weeks and the same review gates.
The harness isn't hardened yet — a rerun is real engineering, not a button press.

Faster — and More Reliable

Legacy query
3,000 ms
Go query
200 ms

15× faster

The 3,000 ms was a fixed async fan-out window — downstreams that missed it were dropped,
and the old system returned incomplete results.

Testing felt it too: on a mismatch, the test re-ran — to rule out an incomplete legacy response.

The new system doesn't wait on a timer. Not just faster — more reliable.

MODERNIZATION COMPLETE
100%
traffic migrated
2.7×
traffic after launch
0
specs written by hand

The customer never noticed the switch.
Then traffic grew 2.7× — and the new system didn't blink.

Bonus: the old system's docs were incomplete. The new one shipped complete Swagger docs from day one — generated.

Full disclosure

It wasn't this smooth.

Months of graft. Trial and error. Dead ends. Sweat and tears.

Shadow diffs meant real customer data — every logged difference went through PII redaction.
And done again? We'd reach for the strangler pattern from day one.

A rough pass, like most real development —
but it revealed a process. A change of direction.

And the methodology meant confidence at every step —
I always knew, provably, where I stood.

Once I pivoted, no human read another line of Groovy.

The AI read all of it.

That's the trade.

I didn't understand the logic.

I captured it.

The behavior was the specification.

The tests were the proof.

The code was just the output.

Step back

Nothing here
is new

TDD. Golden master testing. Canary releases.
Shadow traffic. Code review. Iterative feedback.

Gold-standard software engineering — every practice
decades old and battle-tested.

Classic Discipline, AI Execution

TDD The AI wrote the failing test first — then the fix.
Golden master Live responses became the immutable reference corpus.
Feedback loops Grafana + diff logs closed the loop — autonomously, overnight.
Code review Every AI change landed as a human-reviewed pull request.
Canary release Error rates and traffic matching gated every switch.

AI didn't replace the discipline.
It executed it.

Faster. Tirelessly. Overnight.
The discipline is what made the AI safe.

Gold-standard practice turns AI from
a code generator into a developer.

Why Did It Work So Well?

Fixed Endpoints

A closed, limited interaction surface — easily testable.

Aggregator Shape

Fanned out to other microservices, collated one complex JSON.

Simple Core Logic

The complexity was in the interface — and interfaces can be captured.

Pick a system whose behavior lives at its edges.

And that's how Pismo works: microservice-based, complexity in the interactions —
EventBridge, queues, contracts — not inside each service. The playbook should generalize — the pilot proves it.

The Playbook

1 Pick your proving ground — small, undocumented, unloved. Real traffic, real stakes.
2 Probe the running system — it is the spec. Reverse-engineer test data if needed (sanctioned, in test).
3 Capture golden tests from real responses — quirks included.
4 Let AI generate — test-first, like-for-like, bug-for-bug. The same.
5 Shadow-verify in production — until diffs hit zero and stay there.
6 Cut over — legacy kept warm for rollback. Nobody should notice.

Scope: read-path services with mirrorable traffic — write paths are a different game.
Not yet official process. Proven once — where failure costs money.
The migration is fact. That it generalizes is the one claim left to prove — a pilot turns that into fact too.

The takeaway

Worth a pilot —
on a real microservice.

1
real microservice
6–8
months
~1
engineer

Same bar: 100% shadow match before cutover.

Scope includes hardening the harness into shared tooling — a permanent home
for the golden tests and contracts, so it stops being one person's tooling.

Honestly? It was a lot of work — I'm not sure I'd rush to do it again myself.
But for the company, this is the right direction.

AI is more than a code assistant.

Alone, it gives you variable results.
In a verification harness, it gives you verifiable results.

The system was rebuilt. The live system, replaced.
Security resolved — without incident.
The system is gone. The playbook remains.

As with all fintech development, this was truly a team process — many thanks:
to the team who built the core and took the endpoint slices,
the reviewers across security, architecture and governance,
and the client-facing teams whose migrations kept shrinking the surface.
And to my manager — and his — for the room to push boundaries:
staying close to the cutting edge really does work in software development.

Jon Leahy

Staff Engineer, Operations UI · Pismo, A Visa Company

One closing thought

Every system we ship today
is tomorrow's legacy.

This one was minor — and it's already gone. The key systems are still out there.

The difference now: we have a way to let them retire gracefully.

So the only question left —
which of yours goes first?

Jon Leahy

Staff Engineer, Operations UI

Pismo, A Visa Company · July 2026 · v1.0.0

Jon Leahy · Staff Engineer, Operations UI Pismo, A Visa Company · July 2026 · v1.0.0