"We're just upgrading what's already running to a newer version, right?"
When clients outsource language or framework version upgrades, discussions sometimes begin based on this perception. When an estimate is delivered, clients often exclaim, "Does it really cost that much?" leaving the engineering team searching for words to explain. Why isn't moving already working software while keeping it functional cheaper than building from scratch?
Numbers that can be used to explain this were released in late August.
Numbers revealed by a migration 16 years in the making
On August 25, 2026, CCP (Fenris Creations), the operator of EVE Online, announced that it had begun migrating from Python 2.7 to Python 3. The game has run on Stackless Python since its launch in 2003, with its last major update being Stackless Python 2.7 in 2010. This marks its first major upgrade in 16 years.
The scope covers 2.4 million lines of Python code. Portions were written prior to Python 2.7, dating back to the Python 2.3 and 2.5 eras, featuring syntax that Python 3 rejects altogether.
So far, this is just as expected. What is interesting are the initial scan results.
- 95.9% of files already compiled successfully on both versions
- There were around 3,300 "blocking lines" where Python 3 rejected syntax
- On the other hand, there were approximately 20,000 locations where behavior diverged between Python 2 and 3, requiring manual review
Finding only 3,300 lines of syntax errors across 2.4 million lines feels like "fewer than expected." CCP itself noted that it was a pleasant surprise. The migration work proceeds in distinct phases: one leveraging Python community tooling (such as futurize), and another tackling circumstances unique to EVE.
The difference between 3,300 and 20,000 is the difference in estimates
These two numbers possess fundamentally different characteristics.
Machines can find the 3,300 lines. Because they fail syntax validation, execution halts before the code even runs. The remediation strategy is straightforward, and most can be mechanically replaced. Estimating person-hours based solely on this yields a small figure.
For the 20,000 locations, even if machines find them, they cannot judge correctness. The syntax passes. The code executes. However, the results differ.
Typical differences between Python 2 and 3 include integer division results, string versus byte handling, dictionary ordering, comparison rules, and exception syntax. Among these, the most dangerous are those that affect monetary calculations and quantities.
When whether division rounds down changes, the program produces no error. The entire process runs to completion with an invoice amount off by just 1 yen. Tests pass, the UI displays normally, and only at the end of the month does accounting reach out with an alert.

Do you have a way to verify that it "works as is"?
When reviewing a migration estimate, what you should check is not the remediation effort, but how to verify that the remediation is correct.
The ideal scenario is having sufficient automated tests covering the system prior to migration. In that case, you can run the exact same tests after migration to compare results. However, this condition can rarely be expected in business systems that have operated for over a decade.
When test suites are lacking, the available approaches generally fall into one of the following:
- Record production inputs and outputs, then feed identical inputs into both the old and new systems to compare results. This verifies only the pathways actually in use, while unused pathways remain unverified.
- Write tests upfront restricted strictly to critical processes. Since covering everything is impossible, focus on areas with severe consequences if wrong, such as financial calculations, inventory, and permissions.
- Run old and new systems in parallel for a set period and reconcile their outputs. Although labor-intensive, this provides the strongest verification.
Costs vary significantly depending on which option you choose. When comparing estimates, make sure to align the assumptions regarding which verification method the figures are based on before comparing. Estimates that do not state their verification method will always look the cheapest.
Additionally, the reasons why testing effort estimates miss the mark are discussed in Why Testing Effort Estimates Miss Two Multiplications. In migration projects, this oversight compounds directly.
Things the ordering party must decide
Decide whether to freeze feature additions during the project. Adding features in parallel with a migration causes the diff to verify to keep moving constantly. If you cannot freeze additions, the plan must account for this, extending the schedule. Deciding "not to freeze" is fine, but starting without deciding at all is the most expensive path.
Determine the exact boundaries of the migration scope. You will almost always discover features that technically work but are used by no one. Only the client side can decide to exclude those from the migration target. Simply identifying features that can be dropped will eliminate a sizable percentage of those 20,000 locations.
Clarify the deadline for completion. Is there a hard deadline due to end-of-support, or is the goal simply to modernize because it is old? If the former, the process must work backward from the end-of-support date. If the latter, there is room to proceed incrementally without disrupting day-to-day operations.
Common pitfalls
Do not fall into the trap of "rebuilding at the same time as migrating." The idea that "since we are touching the code anyway, we might as well modernize the UI" always surfaces. However, doing both simultaneously makes it impossible to isolate whether bugs stem from the migration or the UI redesign. Deferring the UI rebuild until after the migration ultimately leads to a faster completion.
Completing a migration does not necessarily reduce maintenance costs right away. Immediately after running on a new version, defects in unexpected places will emerge. Postpone any plans to slim down maintenance contracts at the exact moment the migration finishes.
What to do next
For the system under consideration for migration, check which version it currently runs on and whether support for that version has ended. If it has ended, you are looking at a project with a fixed deadline.
Next, ask your development team how many automated tests can currently be run. If the answer is "hardly any," the largest item that should be included in your migration estimate is not code remediation, but verification methods.
At GleamHub, our development, AI, and automation consulting covers defining legacy system migration scopes, designing verification methods, and formulating phased migration plans. Because the approach varies based on the target system's scale and current testing status, please reach out via our contact page to discuss your needs.
Sources
- The Move to Python 3 Begins! — EVE Online
- EVE Online: The Move to Python 3 Begins! — Simon Willison’s Weblog
- Massive Online Game "EVE Online" Announces Migration of Core 2.4 Million Lines of Python 2.7 Code to Python 3 — Publickey
- Built on ‘a codebase older than some of you,’ EVE Online is undergoing a major code migration — Massively Overpowered









