Synapsea
AI · Modernization

Using AI to speed up an application migration without losing control of quality

Where language models save time in a PHP or Symfony migration, where they go wrong, and which guardrails (tests, static analysis, human review, confidentiality) make their use acceptable on production code.

By the Synapsea team · · 4 min read

Coding assistants and AI agents can now read a module, propose its rewrite and generate tests. On an application migration, where much of the work is repetitive, that is real help. It is also a new source of errors if you let them decide. Here is how we use them: as an accelerator under control, never as the pilot.

1. What a language model does well

  • Read and summarize unfamiliar code. Explain what an 800-line controller does, spot buried business rules, propose a dependency map. The gain on the inventory phase is clear.
  • Propose local refactorings. Extract a method, replace a deprecated call, convert annotations to attributes, adapt a signature. On an isolated class, the proposals are often right.
  • Write characterization tests. From an observed behavior (a request and its response, a function and its outputs), write the test that pins it down. Tedious for a human, fast for a model.
  • Produce documentation. Describe a migrated batch, explain a choice, write a deployment guide.

2. What it does badly, or dangerously

  • Make things up. A method that does not exist in this framework version, a plausible but wrong configuration option, an imaginary Composer package. The error reads fluently, so it is hard to catch by eye.
  • Change behavior while "improving". A model that "cleans up" an odd condition sometimes removes a deliberate business rule. Characterization tests exist precisely to catch that.
  • Reason about the whole system. Beyond a few files, consistency degrades: inconsistent names, duplication, broken interface contracts.
  • Assess risk. A model does not know that a monthly export is loaded as-is by a partner; it will happily reformat it.

3. The guardrails

An AI proposal enters the code only if it clears the same bars as a human proposal — plus a few more.

| Guardrail | Role | | --- | --- | | Characterization tests first | Pin down current behavior; any proposal that breaks them is rejected, even if it "looks better" | | Deterministic tools first | Rector for mechanical rewrites, PHPStan for type errors: what a rule can do is not handed to a model | | Systematic human review | Every proposed diff is read by an engineer who knows the context; no automatic merge | | Small batches | One proposal = a scope readable in one sitting; no "migrate the whole module" | | Static analysis afterwards | PHPStan rerun after each batch, at a level at least equal to before | | Decision log | What the AI proposed, what was accepted, modified or rejected, and why |

The idea fits in one sentence: the AI proposes, the tests and the engineer decide.

4. Confidentiality of code and data

A business application's code and, even more, its production data are sensitive assets. Rules we apply and write into the scoping:

  • No production data is sent to an AI service. Tests and examples are built on synthetic or anonymized data.
  • The choice of provider and model hosting is made with the client: an external service with contractual no-training and zero-retention commitments, or a model hosted in the client's infrastructure when the context requires it.
  • Written agreement before any source code is transmitted, with a precise scope (which repositories, which files excluded: secrets, infrastructure configuration).
  • Traceability: what was sent, when, to which service, for which batch.

A client who refuses any external service is not a blocker: deterministic tooling (Rector, PHPStan, PHPUnit) already delivers most of the gain, and local models cover the rest.

5. A typical run

  1. Assisted inventory: the model summarizes each module; the engineer validates and corrects the map.
  2. Characterization tests: generated from observed behaviors, reviewed, executed; they become the safety net.
  3. Mechanical rewrite by Rector; PHPStan analysis.
  4. Assisted rewrite of the non-mechanical parts, file by file, every diff reviewed.
  5. Tests + static analysis; the batch ships to production; log updated.
  6. Next batch.

Takeaways

  • AI speeds up inventory, local refactorings, tests and documentation.
  • It makes things up, changes behavior and does not weigh risk: without a test safety net and review, it costs more than it saves.
  • Deterministic tools go first; the model handles what is left.
  • Confidentiality is settled by contract and architecture, before the first request.

This is the approach described in our PHP & Symfony modernization offer: automation under supervision, never a promised automatic migration. For the framework step, see Symfony 6.4 to 7.4 migration.

Sources: Rector documentation (getrector.com), PHPStan (phpstan.org), PHPUnit (phpunit.de); Symfony blog post "Preparing for Symfony 7.4 and Symfony 8.0" on deprecation detection (symfony.com/blog).

A question about this topic in your own context?

A free 30-minute call, written answer within 2 business days.

Let's talk about your project