PythonMastery
In the wild · Dropbox

How Dropbox moved its desktop app to Python 3

Dropbox's desktop client, over a million lines of Python, moved from Python 2 to 3 without a big-bang switch. Why they did it, how they ran both side by side, and the rollout rule worth copying.

The problem

The Dropbox desktop app runs on Windows, macOS and Linux, and it is written in Python: "over 1 million Python LOCs", in their words. Staying on Python 2 was costing them in three ways:

What they did

They didn't switch everything on one day. The work started in 2015, and the app was rebuilt so it could carry both interpreters at once: a bootstrap library let them ship "both Python 2 and Python 3 'packages,' complete with bytecode and extensions, side by side."

With both in the same app, Python 3 became a switch they could turn on for some users and off again. They widened it in stages: first for Dropbox employees, then the Beta population, then the Stable channel.

The rule that kept it safe

One policy did most of the work: "all bugs identified as migration-related be fully investigated and corrected before expanding the number of exposed users." A problem found at 1% of users was fixed at 1%, not discovered at 100%.

The part any team can copy

You don't need a desktop app to use a staged rollout. The core of it is a stable way to decide who gets the new path, so the same user always lands on the same side:

python
import hashlib

def in_rollout(user_id: str, percent: int) -> bool:
    bucket = int(hashlib.sha256(user_id.encode()).hexdigest(), 16) % 100
    return bucket < percent

users = ["ada", "linus", "grace", "hedy", "margaret", "alan"]
print([u for u in users if in_rollout(u, 50)])
print(in_rollout("ada", 50) == in_rollout("ada", 50))   # same answer every time
output
['hedy', 'alan']
True

Only two of six at 50% is normal for a small sample; over thousands of users it evens out. Raising percent only ever adds users: anyone already in stays in, so widening the rollout never flips someone back and forth between old and new code.

the tipTake this away

Migrate in stages, behind a switch you can turn back, and fix every regression before widening the rollout. Learn it properly: Type Hints: Industrial-Grade Python, Async/Await: Cooperative Concurrency, Testing with pytest.

Sources

every claim above comes from these