Python is Dropbox’s most widely used language — backend services and the desktop client both. Dynamic typing is part of why Python is good at the early and middle stages of a project, and for a long time that was simply the deal you took.
The key to static type checking, as the mypy team puts it, is scale: the larger your project, the more you want it, and eventually the more you need it. Dropbox crossed that line.
IMPACT
The questions nobody could answer
At millions of lines of Python, dynamic typing made code needlessly hard to understand, and that started to seriously impact productivity. Once a project is tens of thousands of lines with several engineers on it, understanding code becomes the thing that determines whether anyone stays productive.
The symptoms were mundane and constant. Can this function return None? What is this items argument supposed to be — a list, or will a tuple or a set do? Is the id attribute an int, a str, or some custom type? These are trivial questions that were expensive to answer, and successful Python companies eventually face the decision they lead to: should we rewrite everything in a statically typed language?
CAUSE
Documentation that nobody checks is not documentation
In a perfect world, a docstring answers all of it. Experience overwhelmingly says otherwise.
THE FIX
Annotate the code; then make the checker fast enough that people keep doing it
Dropbox first experimented with mypy during Hack Week 2014, and set up a 3-person team on it in late 2015. Along the way they helped standardize the syntax itself: PEP 484, co-written with Guido van Rossum and Łukasz Langa, shipped with Python 3.5 in 2015. Because type-checked Python needed no special syntax, existing Python tools and workflows kept working — which made adoption far easier.
Then adoption ran into performance. Incremental checking came first: if a module’s dependencies are unchanged, reuse cached data and only re-check what moved. Remote caching followed, downloading a recent cache snapshot for the whole codebase when the local one looked stale. Neither was enough, because of cyclic imports — Dropbox had sets of hundreds of modules indirectly importing each other, including an infamous one called “the tangle,” and touching any file in a cycle meant reprocessing the whole cycle. Many incremental runs still took about a minute; a clean build took over 15 minutes, and was getting worse every week as engineers added code and annotations.
AFTERMATH
Four million lines, and the mistakes that got them there
The annotated line count in Dropbox’s biggest Python repository grew to almost 4 million lines in about three years. No single strategy did it. Strictness requirements ratcheted up until annotations were required in new files and most existing ones. Weekly coverage reports told teams what was highest-value to annotate. Editor integrations for PyCharm, Vim, and VS Code cut the friction of the annotating loop itself, and mypy performance work — daemon plus mypyc — made the p75 run 44x faster.
Not everything worked. PyAnnotate, a tool that collected types at runtime during tests and inserted annotations from them, did not see much adoption: collecting types was slow and the output needed a lot of manual polish. Starting with only a few files in the mypy build was a mistake too — anything imported from outside the build came back as Any and was not checked at all, so merging two isolated islands of typed code later would reveal that their types disagreed.
Gradual typing does not fail on the annotations — it fails when the checker gets slow enough that engineers route around it, which is why Dropbox spent as much effort on a daemon and a compiler as on the types themselves.
A plain-language, AI-drafted and human-edited retelling of the article published on dropbox.tech, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.