# EVE Online's Move to Python 3: A Visual Guide to Talk Python Episode 564

**Guide**: https://talkpython.fm/guides/564/eve-online-departs-for-python-3
**Episode**: [Talk Python to Me #564: EVE Online Departs for Python 3](https://talkpython.fm/episodes/show/564/eve-online-departs-for-python-3)
**Featuring**: Jamie Bannister (Senior Software Engineer at Fenris Creations), Thomas Dähling (Principal Programmer at Fenris Creations), Kristinn Þór Sigurbergsson (director of gameplay engineering on EVE Frontier)
**Published**: October 06, 2026

**TL;DR:** EVE Online has run on Python 2 since it launched in 2003, and its roughly 2.4 million lines of Python are now headed for Python 3.12. The studio proved the jump on its sister game, EVE Frontier, first. Getting the syntax right turned out to be the easy part. The hard work is 6,500 lines of division, 100 gigabytes of pickled objects, a pile of C++ extensions and a game server that has to stay up every day.

Most Python 2 to 3 migration stories were told years ago. EVE Online's is happening right now. The space MMO launched in 2003 and has run on Python 2 the whole time, on a heavily customized build of Stackless Python. That's about 2.4 million lines of Python behind a single universe that every player shares.

This upgrade has been a long time coming. Kristinn Þór Sigurbergsson first came on Talk Python in 2016, and Python 3 came up back then, too. "So it's been on the table for a while," as he put it. Now the studio, Fenris Creations, has made it official with a post called "The Move to Python 3 Begins." Jamie Bannister admits the title was a little cheeky, since EVE Frontier runs on the same engine and is already on Python 3. What's new is that EVE Online itself is on the path, and the first batch of changes has already shipped to the live game.

The destination is Python 3.12, by way of a stop on Stackless Python 3.8, which was already end of life by the time the team got there. Why is this harder than your average upgrade? Michael Kennedy, host of Talk Python, put it this way: "In EVE, a destroyed ship is gone for good. There are no do-overs, and the universe has to stay online the entire time."

This guide follows the migration from the start. It covers why EVE picked Python and Stackless in the first place, why staying on 2.7 stopped being an option, and the phased plan. Then it gets into the three places where valid Python 3 still breaks, what Frontier taught the team, and the advice the engineers doing the work would give anyone sitting on an old codebase of their own.

## What is EVE Online? One universe, full loss and an economy economists study

![Operating in a Zero-Margin Environment. A continuously running, single-shard universe defined by permanent consequences. Panels around a hexagonal hub: Scale, 2.4 million lines of Python running since 2003; Concurrency, ~15,000 characters sharing one un-instanced universe; The Stakes, full loss; The Constraint, zero downtime, online 24/7.](https://blobs.talkpython.fm/guides/564/02.webp?cache_id=8a14a6f5)

What exactly is being migrated here? EVE Online is an MMORPG in the classic sense, and it's been running since 2003. Jamie Bannister has been an engineer on EVE for about 16 years, and he was a big player before that. He describes the setting as a far future where humanity has gone through a wormhole to a new system, and society has collapsed and rebuilt itself in a new form. It's an open-world sandbox: mining, trading, piracy and a lot of combat.

The rule that sets EVE apart is full loss. When a ship is destroyed, it's removed from the game for good. Keep that in mind, because it's why a small change in how numbers get rounded turns into a big deal later in this guide.

EVE is also a single shard. There's one universe, and if any two players travel to the same place, they'll meet. Jamie puts EVE's largest player group at about 15,000 characters, all of whom could, in principle, show up in one place at one time for a huge fight. The game holds two Guinness World Records for the most people taking part in a PvP fight.

How do you coordinate a battle that size without it turning into a cacophony? They built it themselves. Big groups run a chain of command, with scouts feeding information up to a fleet commander (FC) who makes the calls, voice systems like Discord, and in some cases their own IT departments. Kristinn Þór Sigurbergsson points out that the people running these groups are playing the game without really being in it, since they're managing spreadsheets and communications. Some have put that experience on their CV, and it helped them get a job.

The economy works the same way. There's no formal mechanic for banks, but players built banks anyway, along with loan schemes, welfare groups and even communist groups. Economists have studied EVE because the game records every single transaction, which is more precise data than any real-world economy offers. Michael Kennedy's take: zero privacy would be creepy in a human society, "But from an economics perspective, you have perfect data, right?"

Where did the idea come from? Michael compared it to Trade Wars, the turn-based space trading game on 1990s BBSs. Jamie traces EVE's lineage to Elite, one of the early 3D space games with trading at its center, and to classic role-playing games. EVE's character stats include perception, intelligence and charisma, and Jamie says you can tell there were D&D players among the early designers.

> "The distinctive factor is the game is full loss. If you kill someone's ship, that's a ship removed from the game. There's no getting things back. Nothing lasts forever. So the game is built on creation and destruction."
>
> Jamie Bannister

**Resources**

- [EVE Online](https://www.eveonline.com)
- [Trade Wars](https://en.wikipedia.org/wiki/Trade_Wars)
- [Elite (video game)](https://en.wikipedia.org/wiki/Elite_%28video_game%29)
- [Multi-user dungeon](https://en.wikipedia.org/wiki/Multi-user_dungeon)

## Why EVE Online chose Python, and the runtime it built around it

![The 20-Year-Old Bespoke Runtime. CARBON engine architecture in three layers: Python gameplay logic and network stack on top, an embedded custom Python interpreter in the middle, C++ physics (Destiny) and graphics (Trinity) engines at the base. The Ecosystem Isolation: dependencies meant extracting PyPI zip files and patching build paths. No pip, no uv.](https://blobs.talkpython.fm/guides/564/03.webp?cache_id=eb5f27b4)

Development on EVE started around 1999, and the game shipped in 2003, when Python itself was still fairly new. Why bet a game on it? Kristinn Þór Sigurbergsson's answer is about people more than performance. It was the dot-com era, and programmers were scarce and expensive. Python let junior developers and even game designers write straightforward sequential code, which sped up development. Jamie Bannister adds that this mattered even more in Iceland, with its much smaller pool of people to hire from.

The speed-critical parts were never Python. EVE's physics engine, Destiny, and its graphics engine, Trinity, are C++. The gameplay logic, much of the network stack and the clustering stack are Python. Michael Kennedy suspects that makes EVE the single largest deployment of a Python app on Windows. Kristinn remembers it as a niche: libraries would break on Windows and nobody cared, because nobody else was running them there.

Being that early meant building a lot from scratch. Jamie says the original developers had to write their own way to cluster an application across machines, their own logging and their own telemetry, and that those in-house tools now look a lot like what Redis and container systems do. Michael points out that EVE also predates the two milestones that matured Python around 2006: Django, and NumPy with the data science stack that followed.

Then there's packaging. Yikes. EVE has never had a package manager. "We still don't have it, but we're working on it," says Thomas Dähling. Adding a library meant taking the zip file from PyPI, extracting it into the game's environment and seeing what happened. Native extensions were worse. Thomas tells of modules built for Python 2.7.18 that corrupted memory, because the layout of `PyTypeObject` changed in 2.7.6 and EVE's forked interpreter never picked up that change.

Shipping the game is just as custom. For 20 years, the studio has embedded the Python interpreter in its own binary, controlled which paths and environment variables it sees, and patched system-specific paths out of Python's build process. Thomas calls it "more like a 75% solution than a real one."

Internal tools hurt too. Designers use Python tools that share code with the game, and Kristinn says distributing them has been a struggle for years, because people break their setup by changing the Python path or running the wrong version of Python. Jamie says a new hire's first few days often go to setting up their machine, sometimes including disabling the Python that ships with Windows.

> "The programming resources were scarce and heavily sought after. So very expensive. So part of the reason why they chose Python was to get more junior people and even designers to be able to write sequential code in Python in order to speed up development."
>
> Kristinn Þór Sigurbergsson

**Resources**

- [EVE Online on Talk Python, episode 52 (2016)](https://talkpython.fm/episodes/show/52/eve-online-mmo-game-powered-by-python)
- [Embedding Python in another application](https://docs.python.org/3/extending/embedding.html)
- [Python/C API reference](https://docs.python.org/3/c-api/)
- [PyPI](https://pypi.org)
- [CARBON engine on GitHub](https://github.com/carbonengine)

## Stackless Python vs asyncio vs free-threaded Python at EVE

![The Concurrency Evolution Matrix. A table compares Stackless Python (legacy: tasklets and channels, sequential standard Python, core to 20 years of game code), asyncio (event loop and coroutines, explicit async/await colored functions, 'out of the window', requires rewriting 2.4M lines) and free-threaded Python 3.13+ (true parallel threads, needs an audit for implicit GIL locks).](https://blobs.talkpython.fm/guides/564/04.webp?cache_id=baddf7a1)

EVE needed serious concurrency long before Python had a good answer for it. Thousands of players can be in the same solar system, and the server can't block while one of them waits on the database. Back then, the answer was Stackless Python.

Stackless is a fork of the CPython interpreter with lightweight tasklets built in. Thomas Dähling describes it as close to its own Python runtime, with a library that monkey patches the threading and socket modules so they don't block, much like gevent does. On top of that, EVE built an abstraction so developers write plain sequential code without realizing it runs asynchronously. That's what let the game scale the way it did.

It's not all unicorns and rainbows, however. Thomas says the model came with a learning curve, and even experienced developers would forget that a database call means the code yields, so they need a lock. EVE also layered its own changes onto Stackless for things like metrics and memory allocation, which made every interpreter upgrade harder. Meanwhile, the rest of Python moved on without it. Python 3 got asyncio, built on the Twisted model, and even in the Python 2 world gevent became far more popular than Stackless, presumably because you could simply import it into standard CPython.

So why not switch to asyncio? Because it needs every function that can pause marked with `async`, and EVE's 2.4 million lines were written without that in mind. Thomas's verdict: that option is "out of the window for us." Michael Kennedy raised colored functions and TonIO, the multi-threaded async runtime from the creator of Granian. Thomas saw that talk at EuroPython, but EVE's engine already does a lot of the same work under the hood. All of the I/O happens on worker threads in the background, with a scheduler acting as the synchronization point that hands data to the main Python thread, so a new runtime isn't straightforward to integrate.

And free-threaded Python? It's on the radar, and Thomas thinks EVE's resource loading could benefit. The catch is that many places implicitly rely on the GIL for synchronization, and those have to be found first. He keeps an eye on the community tracker of which packages support free-threading, which Michael noted is very data science heavy. "I guess it's once again the data scientists that are leading the way here," Thomas said. Kristinn Þór Sigurbergsson gives data scientists similar credit for pushing Python 3 adoption in the first place.

Michael's own worry about free-threading isn't his code. It's all the libraries written by people who assumed the GIL was there, and how many of them hide a latent race condition that turns into a deadlock once someone adds locks to fix it. As he put it, "I'm optimistic for it and I want it to exist but it scares me."

> "Stackless Python itself was a very great solution at the time, right? I mean, it's basically Golang's goroutines, but in Python, before things like this co-routine model became so popular as it is in recent years."
>
> Thomas Dähling

**Resources**

- [Stackless Python](https://www.stackless.com/)
- [CARBON scheduler on GitHub](https://github.com/carbonengine/scheduler)
- [gevent](https://www.gevent.org/)
- [Free-threaded Python HOWTO](https://docs.python.org/3/howto/free-threading-python.html)
- [Free-threading compatibility tracker](https://py-free-threading.github.io/tracking/)
- [TonIO on Talk Python, episode 561](https://talkpython.fm/episodes/show/561/tonio-a-multi-threaded-async-runtime-for-python)

## Why EVE Online couldn't stay on Python 2.7

![The Gravity of Obsolescence. A spaceship is pulled into a gravity well labeled Technical Debt by three forces: Dead Runtime (Stackless Python repo archived, no Pydantic or FastAPI), Hardware Incompatibility (Python 2.7 knows nothing about Apple Silicon) and Team Drain (a sub-team maintaining a bespoke interpreter).](https://blobs.talkpython.fm/guides/564/05.webp?cache_id=741e2505)

A heavily customized interpreter can work for a very long time. EVE's did, for 20 years. So what finally forced the move?

The first problem is that Stackless Python simply stopped. It never went past Python 3.8, and its GitHub repository is now marked as archived in 2025. That's no knock on the project. As Michael Kennedy put it, not every project is meant to last forever, and Stackless carried EVE a long way. (A fun detail: GitHub shows the Stackless repository as forked from CPython, even though CPython moved to GitHub years later. Thomas Dähling's explanation is "it's git magic," since you can make up any history you like. There are repositories on GitHub, he notes, with commits from Abraham Lincoln.)

Being stuck on it also meant being shut out of the modern ecosystem. Michael listed the kind of libraries a developer would reach for today, such as Pydantic, FastAPI or aioredis, none of which were an option on EVE's Stackless. Thomas calls that a major headache. Every package the team took on needed a close look at whether it would even work in their stack.

For Thomas, the push that made the upgrade happen came from the game engine side: the native Mac client the studio worked on in the early 2020s. Python 2.7 knew nothing about Apple silicon. On top of that, macOS handles Unicode in system calls very differently from Windows. Before the native client, Mac players went through a translation layer similar to Wine.

Could the team have kept patching its own Python 2 forever? In principle, sure. Thomas's point is that the price would be a team doing nothing but that. He saw it as one possible solution, but not the best one for a studio that wants to move quickly and keep adding features.

> "If we don't go up to Python 3, we are stuck with having a small team that is dedicated to nothing else but maintaining the Python interpreter."
>
> Thomas Dähling

**Resources**

- [Stackless Python on GitHub (archived)](https://github.com/stackless-dev/stackless)
- [Sunsetting Python 2](https://www.python.org/doc/sunset-python-2/)
- [What's new in Python 3.12](https://docs.python.org/3/whatsnew/3.12.html)

## The Python 2 to 3 migration roadmap for 2.4 million lines

![The Navigational Trajectory. A five-stop route: Basecamp, Legacy to 2.7 (eliminate 2.4/2.5 idioms); Camp 1, Syntactic Compatibility (the futurize pass); Camp 2, The Runtime Hazards (math execution, C++ extensions); Camp 3, The Data At Rest (100GB of serialized memory); Target Orbit, Python 3.12 ('Mix Mode' heterogeneous cluster deployment).](https://blobs.talkpython.fm/guides/564/06.webp?cache_id=0abc0836)

Jamie Bannister is working on the EVE Online migration itself, building on what the Frontier team proved out. He lays the plan out as a series of phases, and the team is already a couple of them in.

**Legacy to 2.7.** Plenty of EVE's code still used Python 2.4 and 2.5 idioms. The first phase brings all of it up to a Python 2.7 standard, because many of those changes are also forward compatible with Python 3. This phase uses the futurize fixers, and its first batch has already shipped. The details are in [phase one](#phase-one-futurize-has_key-and-998-valid-python-3).

**Two unstable branches.** This is the phase the team is in now. They forked the Python 2 branch into a Python 3 branch, which they call Python 3 unstable. Immediately, many things failed and the tests went red, and the job now is to work through them until that branch is stable. Alongside it sits a 2.7 unstable branch full of shims and adapters, code that does one thing on Python 2 and another on Python 3. Why keep a branch you'll never ship? Because it answers the question you care about most on a live game: can this change go into 2.7 today, or does it break compatibility and have to wait for the Python 3 side? The shims make it slower, so it's a compatibility proof, not something you'd release.

**Data, not just code.** Runtime code isn't the only thing that has to change. Data stored in the database and data sent across the network is serialized Python too, and it has to work on both sides of the jump. See [the pickled objects](#migrating-100-gb-of-pickled-python-objects).

**Mix mode.** Once the code and the data formats are worked out, the plan is to swap pieces of the server cluster to Python 3 one at a time rather than all at once. That's covered in [mix mode](#mix-mode-upgrading-a-200-node-cluster-one-node-at-a-time).

How long will the branch work take? Jamie calls it "our next however many months of work," which is about as honest as an estimate gets for a codebase this size.

> "The downside of that is it's going to be a lot slower. So that's more of a compatibility proof versus a releasable version."
>
> Jamie Bannister

**Resources**

- [The Move to Python 3 Begins (EVE Online)](https://www.eveonline.com/news/view/the-move-to-python-3-begins)
- [Porting Python 2 code to Python 3](https://docs.python.org/3/howto/pyporting.html)
- [The upgrade of Tranquility to Python 3 (Nosy Gamer)](https://nosygamer.blogspot.com/2026/08/the-upgrade-of-tranquility-to-python-3.html)

## Phase one: futurize, has_key and 99.8% valid Python 3

![Phase 1: Syntactic Forward-Compatibility. A Python 2.7 idiom, if my_dict.has_key(ship_id):, is struck through and replaced by the Python 3 compatible if ship_id in my_dict:. Tooling: futurize subset (maintaining 2.7 deployability). Progress: jumped from 94% to 99.8% syntactically valid Python 3 in production. Insight: parsing is not the same as behaving.](https://blobs.talkpython.fm/guides/564/07.webp?cache_id=8d5f1d22)

If the goal is Python 3, why does the first phase target Python 2.7? Because EVE's codebase is old enough that a lot of it was written in Python 2.4 and 2.5 style, and bringing that code up to a 2.7 standard makes much of it forward compatible with Python 3 along the way. Jamie Bannister calls the phase "legacy to 2.7."

The tool doing most of the work is futurize, from the python-future project. Futurize is a set of fixers, and each one takes one Python 2 idiom and rewrites it for Python 3. Some of those fixers produce code that still runs on 2.7, and that subset is the one EVE has applied so far. So the game keeps shipping on Python 2.7 while the code creeps closer to Python 3.

What does that look like in practice? The change Jamie's team ran into most was testing whether a key is in a dictionary. Old Python 2 code called the dictionary's `has_key` method. Python 3 dropped it, and the way that works on both versions is the `in` operator. Michael Kennedy listed a few other Python 2 habits that are flat-out incompatible with Python 3, like the old way of declaring exceptions and some of the string handling.

The numbers are interesting. When the team started, about 94% of EVE's code was syntactically valid Python 3. After this pass, it's 99.8%. And this work isn't sitting on a branch somewhere. The first part of the work was released and deployed to the live game a few weeks before the recording, around the time the announcement post went out.

So does 99.8% mean the migration is nearly finished? No, and Jamie says as much himself. The [next section](#why-valid-python-3-syntax-is-the-easy-part) explains why.

> "94% of our code was technically Python 3 syntactically valid. We've now got up to 99.8%."
>
> Jamie Bannister

**Resources**

- [futurize documentation](https://python-future.org/futurize.html)

## Why valid Python 3 syntax is the easy part

![The Hazard Map: Where Parsing Meets Reality. Syntax is the easy part; state, runtime and ecosystem are the hard parts. Three peaks on a terrain map: the math hazard (6,500 lines of division), the state hazard (100GB of historical data the new runtime cannot read) and the engine hazard (a C++ runtime in a chicken-and-egg loop).](https://blobs.talkpython.fm/guides/564/08.webp?cache_id=fdbaf8d7)

Code that parses as Python 3 doesn't necessarily work on Python 3. Jamie Bannister said as much right after giving the 99.8% figure: "It doesn't mean it's going to do the right thing when you put it in Python." There are three big places in EVE's migration where valid syntax still goes wrong:

- **The math.** Division means something different in Python 3, and EVE has about 6,500 lines that divide. See [integer division](#python-3-integer-division-6500-lines-that-decide-who-wins-a-fight).
- **The data.** Twenty years of state is stored as pickled Python 2 objects, and no syntax fix touches it. See [the pickled objects](#migrating-100-gb-of-pickled-python-objects).
- **The engine.** The Python code can't run without its C++ modules, and the C++ modules make little sense without the Python. See [the C++ extensions](#c-extensions-and-the-all-or-nothing-jump-to-python-3).

## Python 3 integer division: 6,500 lines that decide who wins a fight

Division is the classic example of code that parses fine on both Python versions and behaves differently on each. In Python 2, dividing one integer by another gives you an integer: the result is floored and the fraction is thrown away. In Python 3, the same `/` gives you a float. Here's what that looks like on Python 3, where `//` is the floor division that Python 2's `/` did for integers:

```pycon
>>> 100 / 3
33.333333333333336
>>> 100 // 3
33
```

On Python 2, `100 / 3` is `33`. Same line, different answer.

How much of this is in EVE? It's a game that does a lot with a lot of numbers, and Jamie Bannister says about 6,500 lines feature a division. They won't all break, but some will if the team just switches to Python 3 without touching them. Sorting out which ones is part of the next phase of the work.

Why does a decimal place matter so much? Michael Kennedy pointed out one reason: once integers quietly become floating point numbers, equality checks like `x == y` get unreliable. Jamie pointed out the one players would notice. EVE's combat is damage math, one ship shooting another, and if the numbers come out differently after the upgrade, the winner of a fight can change. As Jamie put it, that's "gonna be important for us to get right."

> "If you're doing a lot of damage calculations, you've got one ship shooting another ship, that can make a lot of difference if suddenly your damage changes and now I should have won this fight and now I've lost it because the maths is working out differently."
>
> Jamie Bannister

**Resources**

- [PEP 238: Changing the division operator](https://peps.python.org/pep-0238/)
- [What's new in Python 3.0](https://docs.python.org/3/whatsnew/3.0.html)

## Migrating 100 GB of pickled Python objects

![Hazard 2: The Pickle Time Capsule. A pipe labeled The Vault: 100 gigabytes of stored memory over 20 years runs from a 2006 node, which serializes an NPC agent interaction using a Python 2 old-style class into a pickle byte stream, to a 2026 Python 3 node that rejects it. The solution: migrate to version-independent formats, Protocol Buffers or FlatBuffers.](https://blobs.talkpython.fm/guides/564/10.webp?cache_id=e82e7623)

The migration isn't only about runtime code, and Jamie Bannister says this other angle is really worth calling out: a lot of EVE's data is serialized Python. Some of it sits in the database, and some of it travels across the network between servers. All of it has to survive the jump.

Here's the simplest version of the problem. Say a Python 2 node serializes an instance of an old-style class and sends it to a node running Python 3. Old-style classes don't exist in Python 3, so the receiving end has no idea what to do with it. Michael Kennedy pointed out how common this pattern is: pickle some objects and put them in Redis. That works fine, right up until the shape of those objects changes from one Python version to the next.

A good example is EVE's agents. These are the game's non-player characters, and Jamie is quick to say they have nothing to do with LLM agents. They have memory. Talk to one, and it remembers you. Come back later, and it might hand you a mission to go fetch something or go kill something. That memory is saved to the database as serialized Python, so it can be loaded back later, and it goes back 20 years. Jamie's estimate is about 100 gigabytes of it.

Why not just write a migration script? Because a SQL script can't serialize or deserialize Python objects. It has no idea what it's looking at.

So what replaces pickle? Michael asked about version-independent formats like msgspec, Pydantic or plain JSON. Jamie's answer is probably protobuf, or FlatBuffers depending on the size, since a big part of EVE's internal and external APIs is already built on protobuf. Michael noted a nice side effect of that choice: a node that only needs to pass the data along might not have to deserialize it at all.

Jamie has been writing a public blog post about exactly this problem, due a week or two after the recording.

> "We have those going back now for 20 years. I think there's about 100 gigabytes of Python memory in the DB, of agent memory in the DB in the form of pickled Python objects."
>
> Jamie Bannister

**Resources**

- [pickle in the Python docs](https://docs.python.org/3/library/pickle.html)
- [Protocol Buffers](https://protobuf.dev)
- [FlatBuffers](https://flatbuffers.dev)
- [Pydantic](https://docs.pydantic.dev)
- [Redis](https://redis.io)

## C++ extensions and the all-or-nothing jump to Python 3

![Hazard 3: The C++ Dependency Loop. Orange arrows circle The Bind. Step 1: Python 3 gameplay code cannot run until the C++ graphics/physics modules are importable. Step 2: but the C++ modules make no sense and cannot be verified without the Python glue tying them together. All or nothing: you cannot put the base interpreter on Python 3 and leave native extensions on Python 2.](https://blobs.talkpython.fm/guides/564/11.webp?cache_id=29f98762)

The Python code is only half of EVE's port. The other half is the C++ modules that the Python code imports, and the two are knotted together.

Kristinn Þór Sigurbergsson describes what amounts to a chicken-and-egg problem. The team could start migrating the Python code, but none of it would really work until the C++ modules were importable from Python 3. And they could start on the C++ modules, but those wouldn't make much sense without the Python glue tying them together.

Thomas Dähling, who led the initial Python 3 transition for CARBON, the engine EVE Online and EVE Frontier share, explains why there's no getting around it. You can't move the interpreter to Python 3 and leave the native extensions on Python 2. Everything has to go at once.

That raised the team's biggest early worry: could they actually pull off a jump that size in one push? Their answer was to make the jump smaller, sort of. Instead of going straight to modern CPython, they went to Stackless Python 3.8 first. That way, only the Python major version changed. The async library underneath stayed the same, which mattered because a lot of the code still has a timing dependency somewhere. From there, they went on to Python 3.12.

The port also turned up some fossils. When the team ported the native extensions, Thomas says, they found code relying on Python 2.3 behavior that should never have survived, kept alive by EVE's customized interpreter.

Thomas gave a talk on this work at EVE Fanfest 2025, and it's linked below.

> "You can't just put the base interpreter on Python 3 and then keep all your native extensions to Python 2, right? You need to go all the way. Otherwise, it just doesn't work."
>
> Thomas Dähling

**Resources**

- [Fanfest 2025: Upgrading CARBON to Python (Nosy Gamer)](https://nosygamer.blogspot.com/2025/05/fanfest-2025-upgrading-carbon-to-python.html)

## EVE Frontier as the Python 3 proving ground

![The Proving Ground: EVE Frontier. The EVE Online main server links by a test branch to a small EVE Frontier testbed. The Fork: Frontier shares the CARBON engine but has a small alpha player base. The Abyss: the first Python 3 release had hourly downtime; unseen deadlocks froze processes. The Climb: TeamCity CI pushed further into the build.](https://blobs.talkpython.fm/guides/564/12.webp?cache_id=477fd8bc)

EVE Frontier is the studio's other game, and it's a fork rather than a sequel. Kristinn Þór Sigurbergsson, who now directs gameplay engineering on Frontier, explains that it started from the EVE Online codebase and shares a lot of the underlying tech. The two have drifted far enough apart that the team has stopped pulling changes across.

The games play differently, too. In EVE Online, you arrive in an established society, with NPC police and infrastructure already in place. In Frontier, you land in an uninhabited zone, and it's up to the players to build everything. It leans toward survival gameplay, with smaller, more claustrophobic environments, which Kristinn admits is hard to pull off in space, because space is pretty vast. Being new also means Frontier can make dramatic gameplay changes. EVE Online can't, because people didn't play the same game for 20 years to have all of it change.

That freedom made Frontier the right place to try Python 3 first. Kristinn says the risk of breaking things was a lot less there. Jamie Bannister puts it from the EVE Online side: EVE's servers need to run every single day, so having Frontier prove the approach out before it had to be online 24/7 was really helpful.

It still wasn't smooth. Not even close. Frontier's first Python 3 release had hourly downtime. After an hour and a half or two, some process somewhere would stop responding, and the team was in the dark about why. Kristinn calls it a very stressful time. The cause? He didn't name it on air, only that it "turned out to be something really really stupid."

Part of the difficulty is that a game this old doesn't come with a robust suite of unit tests. Automated testing wasn't a big thing when much of the code was written, and as Michael Kennedy noted, graphics and physics are hard to test automatically anyway. So what helped? The build system. Kristinn credits TeamCity, and the approach of running the real build and getting further and further into it, fixing what broke along the way.

Kristinn had been talking to Thomas Dähling about a Python 3 upgrade back when he was technical director on EVE Online, and he admits the whole idea felt so daunting he couldn't imagine how they would do it. They had a good plan, and they more or less carried it out on Frontier. The stakes are higher for EVE Online.

> "The first release we did, we had hourly downtime because after like one and a half, two hours, some process somewhere would just stop responding at all and we were like a bit in the dark there. It was like a very stressful time and it turned out to be something really, really stupid."
>
> Kristinn Þór Sigurbergsson

**Resources**

- [EVE Frontier](https://evefrontier.com)
- [Moving into the future: upgrading to Python 3 (EVE Frontier)](https://evefrontier.com/en/news/moving-into-the-future-upgrading-to-python-3)
- [TeamCity](https://www.jetbrains.com/teamcity/)

## Mix mode: upgrading a 200-node cluster one node at a time

![Summit Approach: Heterogeneous Mix Mode. Reject the 'Big Bang' deployment; EVE servers must run 24/7. A grid of about 200 orange legacy nodes running stable Python 2.7, with a few blue upgraded nodes on Python 3.12, wired to a box labeled The Router. Traffic passes between heterogeneous nodes (and environments like Linux vs Windows), spreading risk over time.](https://blobs.talkpython.fm/guides/564/13.webp?cache_id=d681b174)

EVE Online isn't one server process. Jamie Bannister describes it as an application spread across about 200 nodes in the server cluster, each with certain jobs to do, all deployed and configured as one giant cluster with coordination tools the studio built over the years. Add every player's game client on top of that. Today, every one of those pieces runs Python 2.7.

So how do you move all of that to Python 3? The obvious way is one big deployment day where everything switches at once. Jamie has been through big deployments like that, and he calls them very stressful. EVE also can't afford it. The servers need to be running every single day, and taking the game down for hours isn't an option.

The plan instead is what the team calls mix mode. Once the runtime code and the data formats are worked out, they want to be able to replace one piece of the cluster at a time with its Python 3 equivalent. That spreads the risk and the load over a long stretch instead of betting everything on one day.

That takes tooling the team doesn't strictly need for the migration itself. Jamie's point is that it pays off anyway. Being able to deploy a heterogeneous mix of builds opens up other options, like asking whether some nodes should run on Linux and others on Windows. Michael Kennedy admitted that was a consequence he hadn't considered.

To be clear about where this stands: Mix mode is the next stage of the plan, not something running in production today, and it depends on the earlier phases in [the roadmap](#the-python-2-to-3-migration-roadmap-for-24-million-lines) landing first.

> "So rather than doing a big deployment day where we say, okay, this is the moment, we want to incrementally do it, kind of spread that risk and that load at the time."
>
> Jamie Bannister

**Resources**

- [The Move to Python 3 Begins (EVE Online)](https://www.eveonline.com/news/view/the-move-to-python-3-begins)

## Advice for a large Python 3 migration: people, branches and a linter

![The Mega-Project Migration Playbook. 1. Secure the Resources: make the team untouchable; if you pause, the effort is wasted. 2. Integrate Constantly: merge from legacy to modern branches often, in small batches. 3. Ratchet the Linter: developers on the old stack write forward-compatible code. Stop digging the hole deeper.](https://blobs.talkpython.fm/guides/564/14.webp?cache_id=fb999d0e)

What would the people doing this tell someone with an old codebase of their own? Each of the three guests had a different piece of advice.

**Secure the people.** Kristinn Þór Sigurbergsson's advice is to secure the resources and make them untouchable while the migration runs. On a live game, a team that's also doing feature work gets derailed immediately. That's part of why Frontier brought in a contractor, Reckon Digital, which Kristinn says did a really good job migrating most of the Python code. And once you start, he says, you can't stop halfway: "Because once you start, you have to finish it." A paused migration is almost wasted effort.

**Keep new code from digging the hole deeper.** On EVE Online, one group pushes the migration forward while a whole bunch of developers keep adding features to the game every day, against Python 2.7. Jamie Bannister's team wants that new code to be as forward compatible as possible, so the week of the recording they rolled out a new linter. The plan is to progressively ratchet up its strictness.

**Integrate constantly, in small pieces.** Thomas Dähling starts with the age-old advice: have really good test coverage. Then do the migration in a separate branch, but keep merging from the Python 2 side into the Python 3 side, so new pain points show up as soon as possible. On Frontier, changes would sometimes come in that clearly weren't Python 3 compatible, and the team could fix them right away. Waiting costs you, too. When they only merged every other week, they spent a whole day resolving the issues each time. His summary: "do it frequent, do it often, keep it small."

One more thing stuck with Kristinn. When he started looking into the upgrade, he went searching for other teams' war stories and found very few. He's sure plenty of people went through painful Python 3 migrations, so his running theory is that nobody wanted to talk about it afterward. It was a traumatic experience, and they just let it go.

> "The more frequent you do these integrations, like the less painful they are. If you do it once a month, oh my god. Like there was a period where we only did it like every other week, and we would basically spend a whole day just resolving all of the issues."
>
> Thomas Dähling

**Resources**

- [Reckon Digital](https://reckondigital.com/)
- [Porting Python 2 code to Python 3](https://docs.python.org/3/howto/pyporting.html)
- [CARBON engine on GitHub](https://github.com/carbonengine)

## What Python 3 unlocks for EVE: speed, types, dataclasses and uv

![Reaching Light Speed: What Python 3 Unlocks. Four panels: Performance, a 15% to 20% CPU performance improvement across the board, essentially for free; Architecture, native Apple Silicon support and paths to future updates (targeting 3.15 next); Code Integrity, dataclasses, type hints and type checkers on 2.4 million lines; Ecosystem, uv and uvx to install packages into the game environment.](https://blobs.talkpython.fm/guides/564/15.webp?cache_id=36aade19)

After all that work, what does the studio actually get? Some of it showed up right away. Thomas Dähling reports roughly a 15% to 20% improvement across the board for most things, which they basically got for free. The one exception is string handling. Everything is Unicode in Python 3, and that carries a little overhead compared with working on bytes directly.

For Jamie Bannister, a gameplay programmer, the wins are in the language. EVE has sorely lacked a good way to declare types. It's sort of there in Python 2.7 with the right IDE, but it's not consistent. He also wants dataclasses, because the team writes a lot of small classes that just define a packet of data and pass it around. Michael Kennedy's point is that 2.4 million lines is exactly the situation typed Python is perfect for, with checkers like Pyrefly or ty answering "is our code still consistent?" automatically. Jamie agrees. EVE has reasonable test coverage, but not complete coverage, so anything the language can do to help prove correctness is welcome.

Packaging is the slower change. The team is exploring `uv run` and `uvx`, and Thomas wants a developer to be able to install a package straight into the game environment. Native extensions make that non-trivial, since they need the right build environment. The studio has also open-sourced the CARBON engine, and Thomas can see a world where people install its pieces into a regular Python interpreter with pip or uv. Some workflows won't change much. Since around 2011 or 2012, the engine has had an interpreter mode: start it with a special flag, and you get a Python REPL inside the game engine.

Is easy installing all upside? Kristinn Þór Sigurbergsson's director side says no. A bit of hassle around adding dependencies isn't all bad on a large team, where someone can flippantly add a dependency that causes problems. The flip side, he admits, is that if upgrading is hard, you're less likely to upgrade.

And the next upgrade is already on the calendar. Thomas says the team has reworked its build pipelines so future updates are a lot easier, with plans to go to Python 3.15 later this year.

If this got you thinking about your own older code, Talk Python Training has three courses that line up with this conversation: [Python 3, an Illustrated Tour](https://training.talkpython.fm/courses/python-3-illustrated-tour?utm_source=talkpythondeepdive) for what changed in Python 3, [Async Techniques and Examples in Python](https://training.talkpython.fm/courses/python-concurrency-deep-dive?utm_source=talkpythondeepdive) for the concurrency background behind Stackless, asyncio and free-threading, and [Rock Solid Python with Python Typing](https://training.talkpython.fm/courses/python-type-hint-course-with-hands-on-examples?utm_source=talkpythondeepdive) for adding types to a large codebase.

> "So the more we can put in a kind of leverage from the language to help prove correctness and find issues, that's going to lead to a better product and less issues."
>
> Jamie Bannister

**Resources**

- [Python typing documentation](https://docs.python.org/3/library/typing.html)
- [Python dataclasses documentation](https://docs.python.org/3/library/dataclasses.html)
- [Pyrefly](https://pyrefly.org)
- [ty on GitHub](https://github.com/astral-sh/ty)
- [uv documentation](https://docs.astral.sh/uv/)
- [What's new in Python 3.15](https://docs.python.org/3.15/whatsnew/3.15.html)

---

Listen to the full episode: https://talkpython.fm/episodes/show/564/eve-online-departs-for-python-3
Episode show notes as Markdown: https://talkpython.fm/episodes/show/564/eve-online-departs-for-python-3.md
