Most software companies have had AI coding tools in their engineers’ hands for a while now, and if you’re on the board or you own a portfolio of them, you’ve probably seen the results on the things that are easy to see. New services get built faster. Test coverage goes up. The backlog that nobody could get ahead of starts to move.
What you may have also noticed is that none of that seems to happen on the one application that actually matters, the product the company sells. It’s usually fifteen or twenty years of Java or .NET, somewhere between a few hundred thousand lines and a few million, sitting on a database that everything else depends on.
The same tools and the same engineers that are flying everywhere else slow to a crawl there, and when they don’t slow down, that tends to be worse, because the tools will happily keep producing code against a system they don’t understand.
Sooner or later that comes up in a board meeting or a portfolio review, and someone asks, carefully, whether the problem might be the team.
We’re vFunction’s forward-deployed engineers, which means we spend our weeks inside applications exactly like this one, working alongside exactly these teams. So we’d like to answer that question before it gets asked, because we’ve answered it many times already. It isn’t the team. Here’s what the team is actually up against, and what it takes to get through it.
First, it isn’t the team
Every application like this was built by people who were making trade-offs so they could ship. Somebody chose a shared table over a service boundary because the boundary would have taken another month. Somebody wrote one authorization module and let everything call it. Somebody read straight from the database because the release was Friday.
None of those were mistakes at the time. They were the right calls for a company that needed revenue before it needed architecture, and most of us have made every one of them ourselves. What nobody budgets for is that trade-offs compound, and twenty years later they aren’t decisions anymore. They’re the shape of the system.
We say some version of this to every team we start with, and we mean it: we’re not here to tell you your software is bad. There’s always a trade-off when you’re building, unless you have infinite time and money, and nobody has that.
What the team is up against
There are five things we run into on almost every engagement. Any one of them is enough to stall an AI-assisted modernization on its own, and the critical application at most companies has all five at once.
1. The team maintaining it isn’t the team that built it
The engineers who built the thing moved on years ago. The people on it now have usually been there a year, maybe three, on a system that has been running for twenty, and what they know well is the handful of areas they’ve personally had to touch.
So they work carefully, and even so, every once in a while a change in one place breaks something in another place that nobody knew was connected. That isn’t a competence problem. It’s what happens when you change a system that no one alive has a complete map of, and the blast radius is simply unknown until something goes off.
AI coding tools inherit exactly the same blindness. They read the code in front of them very well, and they have no way of seeing the consequences three modules over.
2. The code doesn’t match the picture
A team that has lived with an application for a long time carries a picture of it in their heads: these are the domains, this is how they talk to each other, this is where the boundaries are. It’s a reasonable picture, and it’s usually the one the original design documents describe.
The first time they see what the code actually does, the reaction is often that the analysis must be wrong. It usually isn’t. What they’re looking at is the distance between how the system was meant to be organized and how twenty years of fixes and features actually organized it.
That distance is where rewrites get scoped wrong, because the plan was drawn against the picture rather than the code, and the picture is always tidier.
3. The database is where rewrites stall
If you ask a team whose modernization stopped partway through where exactly it stopped, the answer is almost always the database. Hundreds of tables, and nobody certain which ones are still used. Business logic living in stored procedures. Comparisons that only work because the original database happened to ignore case.
On one engagement the application had five hundred-odd tables, and the running system turned out to touch a few dozen of them. The first thing our engineer did with that finding was sit down with the team and work out, table by table, why the rest hadn’t shown up, before anyone was tempted to drop anything.
The hard part of the database was never the migration. It’s knowing what the application actually needs from it, and that knowledge isn’t written down anywhere in the code.
4. AI alone gives you back what you already have
When a team points an AI pipeline at a monolith and asks for a modern version, what comes back, in our experience, is a very faithful reflection of the implementation that already exists. The same coupling, the same shared state, the same tangles, now in newer syntax.
That’s not a failure of the tools. They are extremely good at rote conversion, and rote conversion of a tangled system produces a tangled system. The output can only be as good as the target architecture it was asked to build toward, and nothing in the codebase says what that target should be.
5. Hundreds of prompts before the first real one
Watch an engineer try to get a coding agent to do serious work on a large application and you’ll notice that most of the session isn’t work at all. It’s context. Explain the problem. Check that the agent‘s understanding matches reality. Correct it. Explain the edge case it missed. Then, and only then, let it run.
And when that domain is done, the next one starts the whole conversation over. What the agent is doing, one expensive exchange at a time, is reconstructing an understanding of the application that nobody on the team can simply hand it, because nobody has ever had it written down.
Get the PE portfolio one-pager
Includes the case study of a PE-owned software company moving a 10,000-class .NET product to AWS.
What actually gets a team through
None of the five is fixed by a better prompt or a smarter engineer, and all five are fixed by the same two things. The first is evidence of how the application actually runs. The second is someone experienced sitting with the team while they use it.
The evidence comes from watching the application in production: what runs, what never does, what depends on what, and which tables each part actually touches. That’s the map the team never inherited. Put it in front of the company’s architects and they can set a target architecture from it before a line of code moves, which is the step every stalled rewrite skipped.
From there, each domain gets a specification and a set of integration tests describing what the code has to keep doing. Kiro, Claude Code, AWS Transform, GitHub Copilot, or whatever tools the team already uses take it from there, one bounded domain at a time, with every change checked against a test built from real behavior.
The second thing is us. Half of what vFunction delivers is software, and the other half is an engineer who has done this before, working inside the company’s environment and codebase rather than from a slide deck. We interpret the analysis with the team, including the parts that don’t match their picture. We tune the specs against the real code. We direct the AI tools, and early on we often make the changes ourselves.
The way we put it to every new team is simple: either we’ll do it, or you’ll do it with us, but you’re not doing it alone. It ends when the team runs the process without us. Some get there fast and check in now and then; some want us hands-on for longer. Either way the capability stays in the company, which is the whole point.
One more thing, because it’s the strongest predictor we’ve seen of whether an engagement lands, and it has nothing to do with how sophisticated the team is. It’s whether someone with authority removes blockers within a day. The license, the repository access, the environment, the person who owns the front end. The teams that get through this are the ones where the work is treated as if it matters, and where an outside engineer gets the same access as one of their own.
Where vFunction fits
vFunction is a modernization platform for large existing Java and .NET applications. It captures how an application actually runs in production and helps your architects define a target architecture before modernization begins; it generates a specification and integration tests for each domain from the observed runtime flows; and Kiro, Claude Code, and other AI coding tools refactor the application one domain at a time under your team’s review, with AWS Transform handling the .NET and Java upgrades in the same workflow.
vFunction’s engineers run the process with your team until your team can run it alone. It’s built for applications past roughly 500,000 lines of code or 1,000 classes.
Keep learning: How engagements work · Full-Stack Windows Modernization
Tell us about the application → Let’s Talk