I built a code modernization factory

I built a code modernization factory to guide a team migrate about sixty services to a new shared runtime.

It is a git repository containing migration skills, agent rules, documentation and configuration. Developers clone it, check out one service, open the coding agent they already use and work through the migration step by step.

The idea came from working on the first few services myself.

The services were similar, but not identical. Some had local business validators, some did not. Some exposed a single resource, others had several. Model code was generated in separate repositories. A few services followed a different build or generator path entirely.

That was enough variation to make a single migration script impractical.

At the same time, most of the actual work was repetitive.

The useful abstraction turned out to be the migration procedure itself.

meta-repo (skills, docs, config)
├─ services/<app> runtime
├─ models/<app> model
└─ generators/<app> OpenAPI

The workspace does not contain product code. The repositories under services, models and generators are local clones and are ignored by the meta-repo.

The unit of work is also deliberately small: one service, its model repository and its generator worktree.

I do not want a coding agent looking at sixty repositories and deciding what should happen next.

Migration skills

The migration is divided into explicit steps.

There are separate skills for assessing a service, changing the shared runtime dependencies, migrating validation APIs, moving business validators, adopting shared runtime, handling legacy code, regenerating models and integrating those models back into the service.

Later structural changes are separate steps as well.

Each skill contains the complete procedure for one step.

This is important because a prompt such as:

Migrate this service.

is too broad.

The agent now has to decide what “migrate” means, which changes belong together, what order they should happen in and when it should stop.

I would rather make those decisions once and put them in the procedure.

The developer chooses the step.

Interactive or non-interactive

The same skill can be used in two ways.

In interactive mode, the agent first finds the files involved in the step. The developer reviews that set and decides what should be included in the current batch.

In non-interactive mode, the agent runs the checklist for that step directly.

The procedure is the same in both cases. The difference is how much control the developer wants before the changes are made.

Tests stay locked

The coding agent cannot change tests without developer approval.

This is a behavior-preserving migration. Existing tests are therefore part of the contract.

If the production code can be changed while those tests still hold, the agent can continue through the current step.

If the only way forward is to modify, disable or silence a test, it stops.

I prefer having this boundary to trying to predict every possible exception in advance.

The factory part

A developer starts with one service and runs it through a known sequence of migration steps.

The repositories may look different internally, but the process stays mostly the same.

When we encounter a service that exposes a case the current procedure does not handle well, I update the relevant skill or documentation. That improvement is then available for the next service.

A typical service takes about a week, although there is a big difference between a small service and a large multi-resource one.

The team does not need me to sit with them through every migration. They have the workspace, the procedures and the boundaries, and they can use their own coding agents to do the work.

That is what I mean by a code modernization factory.