Code is excellent context

AI tooling is undoubtedly changing the way software gets built. I’ve watched the hype build for a couple of years now, and I’ll be honest - knowing how heavily subsidised the cost of these tools is right now, I was still on the fence about how much lasting value they’d actually deliver. That changed when I started using them seriously for modernisation and migration work. In this post I want to share why I think that’s a particularly good fit, and a couple of prompting habits that have helped me get more out of these tools.
What’s the problem we’re actually trying to solve?
Picture this: old mainframe code, executing on expensive proprietary hardware. Or business logic baked into a low-code platform that, by design, can only run inside that vendor’s own environment. Or worse - a “citizen developer” starts building something, it grows beyond what they can manage, and IT inherits a sprawling application with no formal handover. You now have hundreds of thousands of (effectively) lines of code, sparse or non-existent documentation, and whatever knowledge exists about how the thing works is distributed across a handful of people - assuming those people are still at the company at all.
The business case for getting off these platforms is usually obvious. The cost of proprietary licences, the vendor lock-in, the inability to hire people who know the technology - it all adds up quickly. What’s not obvious is how you actually do it without spending years and a small fortune on the migration itself.
OK, but how does AI help with this?
The short answer: LLM-based tools are very good at understanding language, and code is language. More than that - it’s highly structured, logical, and repetitive language. That makes it, in some ways, even easier for an LLM to work with than a piece of free-form prose. The patterns repeat, the rules are explicit, and the intent - once you can read it - tends to be consistent.
What this means in practice is that you can take an existing codebase, feed it to one of these tools, and get generated code in a completely different language or framework that conforms largely to the same set of rules and logic. You’re not asking the model to invent business logic from nothing - you’re asking it to translate something that already exists. The source code is the specification.
Code is language - and it turns out, it’s excellent context for an LLM.
Even if you had a team of engineers who understood the source technology deeply, working through a large codebase manually takes many months. An AI tool can move through the same material in a fraction of that time, freeing your engineers to focus on validation, edge cases, and the higher-order decisions that genuinely require human judgment.
The caveats - because there are always caveats
Before you run off and feed your codebases to an LLM, I want to acknowledge the limits here. The results when working against an existing codebase can be quite accurate, but not perfect. You’ll still need subject matter expertise in the business domain to validate that the new code is actually doing what it should. The good news is that validating something is almost always lower effort than defining it from scratch - instead of redesigning, you’re reviewing and testing.
For larger codebases, there’s another practical limit to keep in mind: LLMs have a finite context window, and you can’t reasonably expect to one-shot the rewrite of a large application. You’ll need to decompose the source application into smaller, more manageable chunks and tackle it piece by piece. How you slice it matters a lot - ideally you find seams in the existing application that map to functional boundaries.
If you can, follow something like a strangler fig pattern to run the old and new implementations side by side as you migrate. This lets you shift traffic incrementally and catch discrepancies before they become production incidents.
The prompting tips I promised
OK, a couple of things I’ve found really useful when working with these tools - both for migration work and more generally.
First - ask it to ask you questions before it starts. I’ve found that explicitly prompting the tool to surface any questions or assumptions before diving into a solution is a really useful step. It will almost always find something to ask about, even when the context looks complete. And occasionally it surfaces a gap that would have caused real problems further down the line. It’s a low-effort habit that pays off more than you’d expect.
Second - use the tool to help you build a better prompt, before you build the solution. When I’m working on something complex or unfamiliar, I’ll often tell the tool what I’m trying to achieve and then ask it to help me write a more effective prompt for the actual task. We’ll go back and forth a few times, refining the prompt, filling in missing constraints, making the scope explicit - before I ever run the thing against the real problem. A well-constructed prompt going into a complex migration task is worth a lot more than jumping in with something vague and iterating on broken output.
Where this leaves me
AI pricing is still very much in flux, and where it eventually stabilises is still unclear. But I don’t think these tools are going away - there’s too much real value to be gained, and I’ve seen enough of that value firsthand now to feel confident about it. Migration and modernisation work is one of the clearest examples I’ve come across: the problem is concrete, the source material already exists, and the cost of not solving it keep getting worse.
And the core insight that shifted my thinking applies well beyond migration - if you’re sitting on a codebase and wondering what context to give an AI tool, the codebase itself is often the best place to start. Code is language, after all, and it turns out it’s excellent context!