AI-Driven Development Series

Modernizing crm-api

A BVD Story

Jon Leahy | Staff Software Engineer | Pismo/Visa

Personal exploration - not an official initiative

Case Study: Groovy to Go Migration |
0:00 Back to Series

Case Study

Modernize Legacy App

A BVD Story

100
Endpoints
10
We Needed
0
Specs Written
2018

crm-api

One of the earliest APIs. Built before the company had standards.
Built for our first major clients. And it worked.

The Team?

GONE

And with them went the knowledge of what actually mattered.

Discontinued. But still essential.

700,000

calls per day

Itau had no idea where it was called in their own organisation.
Can't shut it down? Modernize it.

Two Tracks. Running in Parallel.

🔧

Track 1: Migrate the API

Groovy to Go. Like for like. Invisible to clients.

🤝

Track 2: Migrate the Clients

Work with clients to move to newer, supported routes.

Both had to succeed.

Three Challenges

1

Too Much Docs

100 endpoints documented. We only needed 10.

2

Complex Endpoints

The 10 we needed were the most complex.

3

Language Change

Groovy to Go. Different paradigms.

SECURITY ALERT

CVE Vulnerability

Groovy runtime. Cannot be patched.

Migration is now mandatory.

The Code We Found

InvoiceProcessor.groovy
def calculateTax(amount) {
    // TODO: review this later
    return amount * 0.0825
}

8.25% - Why that specific value? Nobody could tell us.

The Reality

100
Endpoints
27K
Lines of Code
90%
Was Noise

Signal lost in noise.

We Tried Everything

More developers Failed
Manual reverse engineering Failed
Writing specs from code Failed
Traditional TDD Failed

Uncertainty doesn't shrink with headcount.

3
Months
4
Developers
2
Endpoints Done

Out of a hundred.

The problem wasn't the code.

The problem was understanding the code.

Hidden business logic. Undocumented edge cases. Magic numbers with no explanation.

The usual story.

More budget. More time. More developers.

Same result.

What if we stopped trying to

understand the code?

AI Spec TDD

🔍
Probe Legacy
📋
Capture Tests
🤖
Generate Code
Verify Match

The running system IS the specification.

Step 1: Get Real Test Data

200
Clients
200
Accounts
Ext
Source

Reverse-engineered from the external system. Real data. Real relationships.

Step 2: Capture Every Behavior

10
Endpoints
200
Data points per endpoint

Each response becomes a golden test case.

Step 3: AI Builds the New System

Go + Hexagonal Architecture + TDD

Ports. Adapters. Clean separation. Proper engineering.

Step 4: The Compare Loop

📡
Call Old
+
📡
Call New
🔍
Compare
🔄
Fix & Repeat

100% hands-off. The system iterated on itself.

Claude Code Did the Work

🔍

Found a difference? Investigated both systems.

🖥️

Got the local system running on its own.

🔧

Identified what needed refactoring. Then refactored it.

🔄

Ran the comparison again. Until it matched.

Not Just One Repo

crm-api

+

ext-gateway

+

deploy-scripts

Like onboarding a senior dev. Brilliant, but cuts corners.

You review. You push back. It doesn't get tired.

AI Automated the Infrastructure Too

🏷️

Tagging

Auto-create tags

⚙️

CI/CD

Trigger pipelines

🚀

ArgoCD

Deploy to prod

📊

Grafana

Query logs, feed back

Plus canary reports — error rates and traffic matching verified before every switch.

But these are all stories for another talk.

Here's what surprised us

The AI replicated
the bugs

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

The Moment of Truth

verify.sh
Testing: /calculate?amount=1000
  Legacy:  { tax: 82.50, total: 1082.50 }
  Modern:  { tax: 82.50, total: 1082.50 }
  EXACT MATCH

Testing: /invoice/lookup/INV-2024-001
  Legacy:  { status: "paid", amount: 5000 }
  Modern:  { status: "paid", amount: 5000 }
  EXACT MATCH
/invoice/lookup EXACT MATCH
/calculate/tax EXACT MATCH
/payment/reconcile EXACT MATCH
/customer/history EXACT MATCH
... 6 more endpoints ALL MATCH

The Comparison

Traditional

3 months
2 endpoints
4 developers

BVD Method

1 week
10 endpoints
1 developer + AI

The Validation Problem

No full user data. Test data wasn't enough.

To be 100% confident, we needed production traffic.

The Shadow Proxy

📨
Request
🔀
Shadow Proxy
Legacy (serves)
Modern (proves)
📋
Log Diffs

The old system served. The new system proved it could.

⚠️ Unexpected Problem

PII in the diff logs.

Shadow compare with production data means real customer data
in comparison logs. That needed careful handling.

What We'd Do Differently

The Strangler Pattern.

Get the new system running on production first.

Migrate route by route. API by API.

Gradually strangling the old system until nothing's left.

The Shadow Proxy Pattern

When test data isn't enough:

Run both systems on production.
Compare everything. Log the differences.

Build confidence from reality, not assumptions.

Groovy runtime Gone
CVE vulnerability Eliminated
New codebase Modern Go
Test coverage Built-in from day one

We never read a single line of Groovy.

Not one.

The behavior was the specification.

The tests were the proof.

The code was just the output.

MODERNIZATION COMPLETE

All Behaviors Preserved

98.5%
Traffic Migrated
0
Specs Written
High
Fidelity

We didn't understand the logic.

We captured it.

The running system was the specification all along.

But let me be honest.

The path wasn't as smooth as this story makes it sound.

AI wasn't consistently available until early 2026.

Before that? Patchy. Experimental.
Not something you could build a migration plan around.

The Requirement

Replace the live API. Like for like.

No differences. No visible switch.

Users should never know it happened.

Complete continuity.

An invisible switch.

The old system stopped. The new one started.

Nobody noticed.

AI Generated the Swagger Too

Old System

No docs

New System

Complete Swagger

Generated from captured behavior. Weeks of work, done in hours.

The Experiment

Same REST server. Same spec. Same tests. Same prompt.

GPT-3.5

Early 2023

Claude 2

Mid 2023

GPT-4

Late 2024

Claude 4

2026

Four models. Four rewrites. One report.

Early Models: Alarming

security-audit.sh — GPT-3.5 output

CRITICAL SQL injection in /api/users — unsanitised input

CRITICAL Hardcoded DB password in config.go

HIGH No input validation on POST /api/orders

HIGH Missing rate limiting on all endpoints

HIGH Error messages expose stack traces

5 critical/high issues found

Tests: 12/12 passed ✓

Tests passed. Security failed.

The Evolution

GPT-3.5
5 issues
Claude 2
3 issues
GPT-4
1 minor
Claude 4
0 issues

Don't Patch. Regenerate.

The Old Way

Audit → Find → Fix → Test → Repeat

Weeks of security remediation

The BVD Way

Regenerate with latest model

Same spec. Same tests. Zero issues.

Dependencies Decay Too

go-chi v5.0.1 ⚠ Deprecated
jwt-go v3.2.0 CVE-2024-xxxx
pgx v4.18.1 ⚠ EOL

Regenerate → latest dependencies. No upgrade treadmill.

It Mimics Life

We don't live forever. We have children.

They carry our genes. Our knowledge. Our specifications.

But they're born fresh. Better adapted.

The spec is the DNA. The code is the organism.

Each generation, born fresh. Carrying everything that mattered.

It Gets Better Over Time

Models improve week by week.

Prompts improve week by week.

If the product is disposable...

Regenerate it.

Better code. Better architecture. Same spec. Same tests.

90% of code will be AI-generated

90% of that is slop.

Throwaway projects. Demos. Prototypes nobody maintains.

The difference? We have specs. Tests. A process.

Our regenerated code is production-grade, validated, and reproducible.

The Workflow Exists

End to end. Tested. Documented. Proven once.

Scale to every legacy repo. Start with the critical ones.

Re-clone and regenerate as models improve.

The map is drawn. We just need to walk it.

crm-api. Like planet Earth in the Hitchhiker's Guide...

Mostly Harmless.

A minor microservice. But the process proved the approach.
And this is how we'll keep modernizing.

The Code Was the Fast Part

📋

Weeks of validation

🏗️

Solutions Architecture sign-off

🔒

Security Engineering review

📝

Documenting every process

⚙️

Full Pismo microservice infrastructure

The governance was the real work.

What's Still Missing

The process validates that BVD works.

⚠️

No formal way to store the test data and contracts long-term.

🔍

Contract X-ray might be a solution — area of investigation.

The approach works. The tooling around it still needs maturing.

Thank you.

Case Study: Modernizing crm-api

1 / 27