中国・10年物PHPのEC基幹を4人×2週間でJavaへ——AIを「865行のルール」と「差分比較コマンド」で縛った移行手順 #モダナイゼーション - Qiita
@soliperse

Moving a 10-year-old PHP EC core system to Java in 4 weeks — a migration process that bound AI with "865 rule lines" and "difference comparison commands"

  • AI
  • legacy
  • Java
  • PHP
  • modernization
0
Posted at 2026-11-12

Upon hearing "AI migrated 540,000 lines in two weeks," the engineers on site were first skeptical. This case, which became popular in China, is disclosed in a follow-up article about what happened during the "two weeks" and the specific rules that bound the AI. The process is more valuable to take away than the numbers.

Case scale (only the source-provided numbers)

  • Target: A core EC system written in PHP, approximately 540,000 lines, that has been in operation for 10 years (including product listings, pricing, search, etc.). Supports the US and Canadian sites.
  • Status: Inadequate documentation, no unit, integration, or regression testing.
  • Team: 4 veteran developers proficient in Java. Used 9 Cursor Ultra accounts, with tool costs for two weeks totaling about $1,800.
  • Period: Core migration and main behavior alignment and basic verification took two weeks. Further one and a half weeks were spent on phased releases and monitoring, as well as detailed fixes.
  • Result: Approximately 540,000 lines of PHP were converted to about 220,000 lines of Java. Commits exceeded 300 in the first week (about 45 per day) and over 500 in the second week (over 70 per day).
  • Live results: A/B testing showed a -0.8% difference in orders and -0.6% in sales. The US site was fully switched, and the Canadian site was 80% switched. No rollback, no P0-level issues.

The "two weeks" refers specifically to the time taken for the code's core parts to be migrated. This is one of the reasons why the author's clear delineation of this boundary makes the process trustworthy.

Step 1: Set the goal as "reproducing current behavior" rather than "understanding the design intent"

The turning point was changing how the problem was approached. Instead of trying to understand "why it was designed this way," we set the standard based on "what input is given, what output is expected, and which items must match."

The first week was a "get it running" week. We converted the weakly typed Map data passing in PHP to Java's DTO (typed data class), and performed bulk conversion of the logic for ES search, filtering, caching, and price display using AI. We also set up Redis and Feign (common components for HTTP API calls) at the same time. The second week was a "get it running correctly" week, during which we worked on resolving differences, splitting long functions, adding correspondence comments with the original PHP code, and improving performance.

Step 2: Compare PHP and Java separately by sending the same input to both

Verification in the absence of tests was done by sending the same request to both the old and new systems and comparing the returned content item by item.

However, when comparing directly, the differences in the cache would result in a screen full of discrepancies. So I split the comparison into two steps.
  • Differences appeared only in the first step, but disappeared in the second step without the cache: these are differences due to the cache, so they are not bugs.
  • Differences appeared in both steps: these are differences due to business logic, so they must be fixed.
  • Furthermore, we added rules to ignore differences such as between "1" and 1, or between "59.00" and 59.00, as well as values that change every time like requestId and traceId. As a result, the proportion of differences in the report that actually needed fixing increased from about 10% to over 80%.

    Step 3: If AI makes the same mistake, add one line to the rule.

    Every time a new conversation started, the AI repeated the same mistake. These included things like tricking the reflection to hide type issues, simplifying the logic on its own, and writing "the rest is similar so it's omitted."

    The countermeasures were tedious, and we added one line to the rule every time a recurring mistake occurred. Eventually, the rule file reached 865 lines, including rules such as "implement exactly as the original code without simplification," "summarize external API calls using Feign," "use a common utility for serialization," and "log catch errors at the error level." As a result, the percentage of AI-generated code that could be used directly increased from 50-60% to over 90%.

    Step 4: Consolidate repetitive tasks into one command.

    We converted tasks that had been done by hand three or more times into Cursor's skills (which consolidate standard operations into one command).

    • /api_diff: Creating requests for new and old versions, classifying differences, and generating reports.
    • /log_investigate: Extracting traceId from one line of log, identifying the relevant code, analyzing the cause, and keeping it as an investigation memo.
    • php_sync_check: Daily import of the latest PHP code during migration, and checking whether it is in sync with the Java side.

    The last command is often overlooked but is important. This is because modifications to the old system continue even during migration.

    Actual pitfalls encountered.

    • The item name for error messages was "msg" in PHP and "message" in Java. The AI noticed the difference but corrected it in the opposite direction, causing the error message to no longer appear on the screen. This was discovered during a 5% phased release and took 10 minutes to fix.
    • The number of items was fewer in PHP. Both the AI and humans judged the search conditions to be correct, but in reality, some items were missing during the response assembly phase. From this, we created the rule to "follow not only the entry point but also the line where the value is finally assigned."
    • The AI ran the build without confirmation, and thus erased the state of investigation. After that, we made sure that any operation affecting the environment would be confirmed by a person.

    Judgments that should be made by people.

    Depending on the level of risk, we have divided the degree to which we entrust AI into three levels. The core parts are mainly handled by people with AI as an assistant, important parts are implemented by AI but strictly reviewed by people, and peripheral parts allow for broader discretion by AI. We first asked AI to plan (files to change, steps, risks, impact scope) for each layer, and then executed it after human approval. Releases started with verification comparing results in parallel with production, and then proceeded to small-scale phased releases, split switches, and full switches. We have prepared rollback capabilities at the unit level.

    The author lists four premises that can be emulated: "four experienced people", "investment in tools", "modules with relatively clear interfaces", and "cooperation from the business side", and emphasizes not using the number "two weeks" alone for management decisions.

    Even in sites that are still using old Java or PHP, they start by preparing two things: a comparison command and a rule file.

    Source

    0
    0
    0

    Register as a new user and use Qiita more conveniently

    1. You get articles that match your needs
    2. You can efficiently read back useful information
    3. You can use dark theme
    What you can do with signing up
    Sign upLogin