A green build is not a finished port
A Java class that compiles is not evidence that a migration worked. It can wear the right annotations, satisfy the build, and still charge the wrong price, skip a permission, or drop a field the old application always returned. That illustrates the miss. It is not a failure AWS demonstrated in a customer system. It is the class of miss that makes a language move expensive.
AWS’s Machine Learning blog published a walkthrough, “Build a repeatable PHP-to-Java migration engine with Kiro, ” aimed at that risk. Kiro is an AI IDE: an agent paired with the codebase for multi-step work, with a developer still approving the gates. The method puts a rulebook in the repository, files that a person and the agent can both read, and it treats the original PHP application as the specification. The Java port is not done until a parity harness agrees (the same requests, replayed against the old app and the new one) or until every mismatch is written down as an approved change.
The public listing frames the alternative as a one-time leap of faith, and the goal as a measurable, repeatable process. No customer results, cost, duration, or parity rate is attached to that framing. The walkthrough’s own sample, by its account, never built the harness it treats as essential. Hold that fact. The method is still worth understanding. An AI agent that rewrites code without a judge is a fast way to move bugs into a language your team knows less well.
What you are actually buying
For a leadership team, the product is not “PHP becomes Java.” It is a way to govern AI automation on a system you already run.
Put the methodology in versioned files, so the next application inherits a correction instead of a chat transcript. Discover the model before anyone translates it. Define done as agreement with the old system, not as a successful compile. When generated code is wrong, change the rule and regenerate. Run one application with a human at the gates. Do not fund a portfolio of AI agents until the referee has failed a fault you planted on purpose.
That is a narrower promise than a weekend in which a portfolio migrates itself. It is also the one a C-suite can govern. The authors, Sarat Guttikonda, Phani Parcha, and Pallavi Jha, say the same shape applies wherever the old system is the only complete specification. What the walkthrough actually specifies is discovery for common PHP frameworks and a path to Spring. Treat other stacks as an extension you still have to write, not as a feature you already own.
There is a counterweight. This is a lot of process for a small, one-off service. A careful human port, backed by tests that characterize today’s behavior, may still be the cheaper path. The rulebook earns its keep when several similar applications can inherit it. One shared, versioned rulebook, not a slogan about a shared brain, is the mechanism. Improve it once, and the next project is supposed to pick up the fix.
Where mature PHP hides the rules
Laravel and Eloquent, Symfony and Doctrine, and the long tail of plain PHP that grew up in production do not keep business rules in one obvious file. They leak into traits, type conversions, hidden fields, relationship methods, XML mapping files, and expressions that only resolve when the app is running.
A line-by-line structural translation misses those. The authors’ case for Java is stronger type safety, broader library support, and scaling behavior that is easier to reason about. Treat that as their argument for the move, not as a measured outcome. The risk that matters here runs the other way: you pay for a rewrite and ship a system that is type-safe and wrong.
The decisions that make it governable
The engine the walkthrough describes has three parts. A shared methodology lives in versioned Markdown skill files. It names php-to-java-migration and php-model-discovery, plus a workspace guidance file, core-workflow.md, so the knowledge sits in the repo rather than in one person’s chat history. A mechanical scanner reads the PHP before anyone translates it. A parity-first workflow then tries to keep people on the ambiguous decisions and let the later work run as a checked loop.
The public listing calls the workflow eight-phase. The writeup also numbers a preparatory Phase 0 through Phase 8, and it is not consistent about whether people own the first three phases or the first four. Use this split, which matches the walkthrough’s own diagram more cleanly than its prose. People own discovery through the rulebook, and after that the harness is the referee.
- Assess and discover. Run the scanner, assemble an inventory, and record a cutover choice. A person reviews every flag before a model is translated.
- Build the judge. The parity harness comes before any Java. It has to pass on the working PHP application, and it has to fail when someone deliberately breaks behavior. A referee that cannot see a fault you planted is not a referee.
- Write the rulebook and the gap list. Known mismatches go on paper, not into an agent’s improvisation.
- Stress-test the rulebook on representative files, and have a person approve it before it is applied application-wide.
- Then let the loop run. Scaffold a compiling Spring Boot project, translate the data model from the manifest, turn the request path into controllers, filters, and annotated request objects, convert business logic in batches, and finish with a parity report plus a cutover checklist. On the interactive path, a person still signs a phase before the next one starts.
The walkthrough names a cutover choice at the start and a checklist at the end. It does not describe a data move, a dual run, a traffic shift, or a rollback. Do not assume a particular cutover product is in the loop.
The scanner, discover-models.php, is described as a single PHP file with no dependencies. It does not execute the application. It reads source text, so it can run even when libraries are not installed. Three strategies are actually described: follow an Eloquent model back to the framework’s base class, look for a Doctrine entity marker, and read external ORM XML mapping files. The output is one JSON manifest (fields, type conversions, relationships, and visibility rules) plus a flag wherever the source is ambiguous. A list of mass-assignable fields built from an expression that only resolves at runtime becomes a plain-language review item, not a guessed annotation.
The walkthrough’s hardest rule, paraphrased, is not to read, guess, or hand-translate a model until that scanner has run. An agent that invents a plausible class where the scanner shrugged is not being helpful. It is hiding a question inside a pull request.
The walkthrough also says the engine auto-detects Laravel, Symfony, CodeIgniter, and plain PHP. Laravel and Symfony are what the three strategies cover. CodeIgniter and plain PHP are names on a detection list. Adding a framework, in this writeup, means a detection entry plus a written guide. That is not the same as a scanner that can already emit a manifest.
When generated code is wrong, the instruction is to fix the loop, not the file. Amend the rulebook and regenerate. A local patch feels faster on Tuesday and teaches the next application nothing. Agentic does not have to mean unreviewed. In this design, the review is front-loaded on purpose.
Before a second application, four things should be true:
- A person has reviewed the discovery manifest, including every item the scanner refused to guess.
- The parity harness has failed a behavior break planted on purpose in the old application.
- The last correction went into the rulebook and was regenerated, not patched into one Java file.
- No portfolio run is funded until those three are true.
Two ways to run it, and a limit on what is confirmed
The walkthrough offers two execution paths that are supposed to read the same skill files. Kiro is the interactive path: one application, a person reading the generated Java and running tests, a better fit when the framework is new to the rulebook. It presents AWS Transform as the agentic path, with applications running as parallel jobs and review at checkpoints the skill files define.
That second claim is not established by the product’s own documentation. AWS Transform is a broader modernization workbench. Its agents can be reached from Kiro, among other surfaces, and the product advertises custom, organization-specific transformations. That makes a method like this a plausible thing to attempt there. It does not confirm that these skill files execute unchanged as parallel PHP-to-Java jobs. Published Transform results for virtual machines, mainframe, and Windows modernization do not measure this engine. Do not borrow them. And “continuous learning” in the product description is not the same mechanism as editing a Markdown rulebook and regenerating.
The demo that leaves the referee out
The bounded walkthrough is a read-only slice called laravel-eloquent-sample, drawn from an open-source Laravel application the post does not name. It says the README pins the upstream commits used for comparison. Those hashes are not in the post body.
The models it says were ported are Book, Chapter, Page, and User. The target stack it describes is Spring Boot 3.4.1 on Java 21, with Maven and an in-memory H2 database. The sample spec, as the walkthrough describes it, is candid that this is 4 of 37 models, and that the parity harness was never built for the slice. It calls that the single biggest gap, and treats writing the shortfall down as part of the design. That is more honest than a silent demo. It is still a hole. The listing promises verification by a harness. The sample, by its own account, does not demonstrate the principle the method treats as essential. A documented gap is not a measured parity rate.
What the slice is described as showing is traceability, and those mappings are the authors’ methodology, not an independent benchmark. One example is enough. On the Page model, the fields html, markdown, and text are marked so Java’s JSON layer skips them, because the manifest recorded them as hidden. Book’s description is deliberately still visible. A reviewer can point at the manifest and see why a field vanished, instead of trusting an agent’s taste.
The repository the walkthrough points to is https://github.com/aws-samples/sample-php-to-java-migration. The engine itself is not described as provisioning AWS resources. This is a method you run in a workspace, not a managed migration service.
Questions worth asking before you fund it
- Does a compiling Java port mean the migration worked?
No. A structurally similar port can build cleanly and still behave differently. Done means the parity harness agrees with the PHP application, or each mismatch is logged as an approved change.
- Did the published sample prove parity?
No. Its own spec, as the walkthrough describes it, says 4 of 37 models were ported, and that the parity harness was never built for that slice. The gap is documented. It is not a measured parity rate.
- Is AWS Transform this migration engine?
Not established. Transform is a broader agentic modernization workbench, and Kiro is one documented way to reach its agents. The walkthrough says Transform can run the same skill files at portfolio scale. Public product docs support custom transformations and IDE access. They do not confirm this PHP pipeline as a shipped workload, and results from other Transform modernizations do not measure it.
- When generated code is wrong, should the team patch it?
The method says no. Amend the rulebook and regenerate, so the next project inherits the correction. A local patch fixes one file and teaches the loop nothing.
- Are there published savings, timelines, or defect rates for this engine?
Not in the walkthrough. There is a phase design, a scanner, a sample stack, and an admitted missing harness. Broader AWS Transform figures for other modernization work do not measure this PHP-to-Java path.
- Can the same shape leave PHP and Java behind?
The authors claim it can, wherever the legacy system is the only complete specification. What is actually specified is discovery for common PHP frameworks and a Spring target. Treat other languages as an extension you still have to write.
Fund the referee before you fund the rewrite. If a demo cannot show a harness that fails when the old system is deliberately broken, you do not have a migration engine. You have a code generator with good manners.