- AWS describes a Kiro-based method for moving Laravel and Symfony apps to Java. It treats the PHP app as the specification and requires a parity test before sign-off.
- The aim is to catch behaviour that a clean compile hides, such as business rules held in model traits and mapping files.
- The AWS sample ported 4 of the 37 models in its Laravel app and built no parity test for it. Our advice: ask any migration vendor for parity evidence.
A port that compiles is not a port that works
It is easy to assume that translation is the hard part of a migration. AWS's new guide to moving PHP applications to Java points elsewhere. The hard part, it suggests, is knowing what the old system really does.
The AWS post warns that a long-lived PHP codebase can hide rules the business depends on. They may sit in Eloquent traits (reusable code blocks in Laravel's database layer) or in XML mapping files. A Java port that mirrors the structure can compile without errors and still act differently.
That is the idea worth taking from the post. AWS's method treats the running PHP application as the specification. Under it, a migration succeeds when behaviour matches, not when the new code looks tidy.
What AWS built
The post describes an engine for Kiro, an AI development environment that pairs an agent with your codebase. The engine is a set of versioned Markdown skill files. One holds the migration method, and one holds a discovery scanner workflow. An eight-phase workflow ties them together, with approval gates where a person reviews the discovery manifest and approves the rulebook.
The same files can also be handed to AWS Transform, the AWS service for large-scale migration. AWS positions Kiro for one application at a time and AWS Transform for many applications in parallel. AWS says a rulebook fix made in one path improves the other.
How the scanner works
The first rule in the method is that no one reads, guesses at or hand-translates a model file until the scanner has run. The scanner, discover-models.php, is a single PHP command-line tool. It reads source code as text tokens and does not execute the application. So it works even when the app's dependencies are not installed.
It runs three strategies. One walks the class inheritance chain to find Eloquent models. One looks for the #[ORM\Entity] attribute that marks Doctrine entities. One reads external *.orm.xml mapping files. The output is a JSON manifest of fields, casts, relationships and visibility rules.
The scanner does not guess when it is unsure. AWS gives one example. If a $fillable list, which says which fields can be set in bulk, is built at runtime, the scanner writes a plain-language flag. A person then resolves each flag.
The referee comes before the code
The sequencing is the unusual part. In Phase 1, before any Java exists, the team builds a parity harness. This is a test rig that sends the same requests to the old PHP app and the new Java app, then compares the responses.
AWS says the rig must pass against working PHP. It must also fail when the PHP is deliberately broken. A rig that stays quiet while faults are planted earns no trust.
Phases 4 to 8 are mostly automated loops. They scaffold the project, translate the data model, translate routes and validation, convert business logic in batches, and then prove parity and cut over. The second principle is "fix the loop, not the code." When the same mistake recurs, you amend the rulebook and regenerate instead of patching by hand.
What the sample shows, and what it does not
The sample works on a read-only portion of an open source Laravel app. It converts four models, Book, Chapter, Page and User. The target is Spring Boot 3.4.1 running on Java 21.
One example shows the manifest at work. The original Page model lists html, markdown and text as hidden fields. The Java version therefore marks them with @JsonIgnore, which keeps them out of JSON output. Book's description stays visible.
That example is about which fields are exposed. It is not a business rule. The post shows no case of a hidden business rule being caught or missed by the scanner.
The post is open about other limits. Its spec says the sample ported 4 of the 37 models in the Laravel app it used. It also says the parity rig was never built for this slice, and calls that "the single biggest gap." So the sample shows the mapping step. It does not show the parity proof that the method puts at the centre.
The post also gives no results from real migrations, such as defects found or time saved. This is a vendor describing its own method.
Questions for your team
Whether or not you use Kiro, the discipline carries over. Ask these before approving a migration.
Does a test rig exist that compares old and new behaviour on the same requests? Has anyone shown it fails when the old system is deliberately broken? Who approves each logged difference between the two systems? Which business rules live in traits, mapping files or runtime expressions that no one has listed?
One more question is our own advice, not something the AWS post says. If a vendor is selling you a migration, ask it to show parity results on a pilot application before you commit a portfolio.
The lesson here is simple. A migration is only as trustworthy as the test that judges it, and that test should be built first.
Produced by the WebPulse Newsroom with AI assistance from the original reporting credited below, and checked against that source by our editorial review. How we use AI.
Original reporting: AWS.





