debt: i know it's bad and i still open it every day
Show Notes
Every morning when I walk my dogs, I spin the exact same PokéStop without looking at the screen. I have a PhD in computer science, I've taught courses in cloud-native systems, and I've been playing a mobile game since the summer of 2016 that I could take apart in front of my own students as a case study in almost everything I tell them not to do.
This episode is a design review of Pokémon Go, and it's really an episode about technical debt, legacy systems, and what happens when a team fixes the symptom of a failure instead of its cause. I walk through three real architectural problems: a decade-old crowdsourced dataset from Niantic's earlier game Ingress, reskinned and sold as a new build; a gym system rework that relocated its core failure instead of resolving it; and a flagship AR feature almost nobody who plays the game actually leaves on. Then I turn it around, because the one part of this app that is not lazy at all is the retention engineering — and that mismatch, elegant behavioral design bolted onto neglected core architecture, is the actual argument. Good engineering and successful engineering are correlated. They are not the same thing, and confusing them is how teams get blindsided by which of their own systems actually matter.
If you work in software architecture, care about technical debt, or have ever shipped something you know is held together with old assumptions, this one's for you.
Read full transcript
Every morning when I walk my dogs, I spin the exact same PokéStop. The game's already open — I've been catching Pokémon along the way — and I spin that particular stop because it's the one on my route.
I have a PhD in computer science. I've taught courses in cloud-native systems. I've spent entire episodes of this show telling you what good architecture looks like. And I've been playing a mobile game since the summer of 2016 that I could take apart in front of my own students as a case study in almost everything I tell them not to do. I'm not talking about Pokémon Go being simple — simple is fine, simple is often correct. I'm talking about a game that reused a decade-old dataset instead of building its own, "fixed" a broken core system by relocating the failure instead of resolving it, shipped a headline feature that nobody actually uses anymore, and has been, more or less continuously, one of the most successful mobile apps ever made. That's not a contradiction I'm going to resolve by the end of this — it's the actual subject.
Welcome back to Chaotic Commits. I'm Joanne Skiles. And today I'm going to walk you through Pokémon Go the way I'd walk through a production system in a design review, because underneath the Pikachu and the incense and the Community Day events, it actually is one. It has a data layer, a state machine, a matchmaking system, a notification pipeline — and it has technical debt that's been sitting untouched, by design, since before some of you started your current jobs.
Three things I want to show you: where the app's foundation actually came from, what happened when a broken core system got "fixed," and what happened to the feature that put the whole thing on the map. Then, at the end, the part that actually keeps me up at night a little, which isn't any of that — it's what all three of those things together are optimized for, and how well that part works on me specifically. Someone who can explain the mechanism out loud and still can't stop.
Before Pokémon Go existed, Niantic shipped a different game called Ingress. Same studio, same core loop, wildly smaller audience. Players walked around the real world capturing "portals," almost always tied to public art, historical markers, murals — little landmarks nobody else had bothered to catalog. Ingress ran for years before Pokémon Go was a public idea, and the entire time it was quietly doing something much more valuable than entertaining its small, devoted player base: it was building a location database. Every portal a player nominated, every landmark somebody walked to and tagged, went into a dataset that took years of unpaid, crowdsourced human labor to assemble.
When Pokémon Go launched in 2016, it didn't build a new world. It reskinned Ingress's world. Portals became gyms and PokéStops — same coordinates, same underlying database, new sprites drawn on top. The thing that felt, at launch, like a miracle of location-aware engineering — gyms and stops appearing at exactly the right real-world landmarks in cities all over the planet on day one — wasn't miraculous at all. It was inherited. Somebody else's multi-year, crowdsourced data collection project, wearing a new coat of paint.
I want to be careful here, because I don't think reuse is the sin. Reuse is usually the smart move. If you've got a location database that took years to build and it's already reasonably accurate, rebuilding it from scratch for a new product would be insane — I'd have made the same call, and most of you would have too.
The part that's actually the problem isn't architectural, it's rhetorical. Pokémon Go got covered, at launch, as a leap forward in augmented reality and location gaming — not as "a mature, crowdsourced geographic dataset gets a new front end," which is the more accurate and much less exciting sentence. The engineering story that got told wasn't the engineering story that happened, and that matters, because I've watched this happen inside companies a lot. A team ships a new product on top of an old, unaudited data layer nobody's touched in years, and the pitch deck says "built from the ground up," because "built from the ground up" is a better slide than "we found a foundation somebody poured a decade ago, we're pretty sure it's still sound, and we didn't check because we didn't have the budget to."
You are not shipping a new building just because you put a new storefront on it. You're shipping whatever the foundation can bear, plus a little paint. Know which one you actually built before you go tell the story of which one you built.
The original gym system — 2016 launch version — was almost universally hated, and it was hated for a very specific structural reason. Gyms ran on a mechanic called prestige. To take a gym, you fought your way in. To keep it, you or your teammates had to keep training it, indefinitely, against anyone who showed up. There was no cap, no decay, no natural end state. Whoever had the most time to spend standing in a parking lot tapping a screen won — and I'm not saying I did that, but I might have done that. Not the most skilled player, not the best team composition — the most available one.
That's not a balance bug. That's a system whose outcome is fully determined by an input, available hours, that has nothing to do with the thing it's supposedly measuring, which is who's actually good at the game. I see this exact failure mode in production systems constantly, usually dressed up as a leaderboard, a queue, or an on-call rotation. Whoever can absorb the most unbounded load wins, and we call that a meritocracy because it's got a scoreboard attached.
In 2017, Niantic did a real rework. Prestige got replaced with a motivation system. Gyms decay over time unless someone's actively defending them. Berries feed motivation back up. It's a genuinely different mechanic, and the community, mostly, called it a fix, and it changed something real. But watch what it didn't change. A gym still only functions if there's a reliable flow of nearby players interacting with it. In a dense city, the new system smooths out the old failure nicely. In a rural area — or honestly, a lot of suburban areas on a rainy Tuesday — gyms just sit there. Nobody defends them because nobody's around to defend them. Nobody attacks them because there's nobody to fight over. The gym doesn't get contested, it gets forgotten, indefinitely, because the entire system's function still depends on a volume of nearby player traffic that a large fraction of the actual map doesn't have.
The perfect example is my own neighborhood: there's a gym near me, and if I put a Pokémon in it, it's stuck there for potentially a month or more. So the original failure was "one person can dominate forever by outlasting everyone else." The rework didn't remove that dependency on player density. It just changed the shape the failure takes when density is low — from "one person camps it" to "nothing happens here, ever." That's not the same bug fixed. That's the same underlying assumption, unexamined, wearing a different symptom.
I've sat in more retro meetings than I can count where a team declares a system fixed because the specific failure mode from the postmortem stopped reproducing. Nobody asks the harder question, which is whether the fix addressed the actual dependency the original bug was exposing, or just moved the point where that dependency breaks. A retry queue that quietly grows forever isn't a fix for an unreliable downstream service. It's a more patient version of the same problem, with a delay bolted on so it fails somewhere you're not looking at the dashboard for. The refactor didn't fix the failure. It relocated it to wherever nobody was watching closely enough to call it a regression.
Now let's talk about augmented reality. AR was the entire pitch in the summer of 2016, if you remember. It's why there's news footage of grown adults standing in fountains, why there's a genuine cultural memory of a Pikachu appearing to sit on somebody's actual kitchen floor through their actual phone camera. That feature is the reason a huge percentage of people who've never played the game since know what it is at all. It was the launch keynote demo, and it worked exactly as a launch keynote demo is supposed to work — it got the room's attention.
Play the game today, or ask literally anyone who's played it more than a handful of times, and their AR mode is off. It's a toggle in settings, defaulted on, and in my sample of every experienced player I know, they switch it off within the first week and never switch it back on. The reasons are boring and completely legitimate: it drains the battery fast, it makes the actual mechanic — aiming and catching — meaningfully harder, because now you're also trying to hold a phone steady and track a creature superimposed over a moving real-world camera feed instead of a stable static background, and it adds friction to something you're doing forty times in a play session. The feature that sold the app is a feature nobody who actually plays the app chooses to use.
It didn't get removed, though. It's still there. It gets a new coat of visual polish periodically. It still gets a mention in marketing when there's a new device or a new AR framework to show off. It is, essentially permanently, a demo feature living inside a production app, maintained because it earned its keep once, at launch, and nobody's willing to be the one who deletes the thing that put the company on the map, even though its actual, ongoing, daily usage is close to zero.
Think of it like the concept car sitting on the rotating platform at the auto show. It's the reason people walked over to your booth in the first place. It has doors that open like wings and a dashboard made of something that isn't going into production. Nobody's driving it home. Nobody's supposed to. Its entire job was to get you to walk closer and look at the sedan parked right behind it, the one you're actually going to buy. The problem isn't that AR mode exists — concept cars earning their keep at the show is a completely legitimate reason to build one. The problem is treating the concept car like it's still the product, in a roadmap conversation, three years after everyone in the building quietly stopped driving it.
I'd bet almost every engineer listening to this has a version of this exact feature sitting in their own codebase right now. The thing that got the loudest applause in the launch demo, that nobody's used in production in a year, that's still technically supported because deprecating it feels like admitting the applause didn't mean what everyone wanted it to mean.
So here's where I have to be fair to Niantic, and where the argument gets a little less comfortable, because up to this point it's been easy to sit here and feel superior about somebody else's technical debt. Everything I just walked through — the reused foundation, the relocated failure, the concept car nobody drives — that's the architecture. The architecture is provably lazy in places, unfixed in places, propped up on a decade-old dataset in places, and none of that is the reason the app still has tens of millions of monthly active users a decade later.
The reason is a completely different system, one that got nowhere near the same level of neglect. Raid passes that decay if you don't use them. A catch streak — and a PokéStop spin streak — that resets to zero the moment you miss a single day, engineered specifically so the cost of skipping feels larger than the five minutes it would take to not skip. Community Day: one Saturday a month, appointment-based, deliberately scarce, built to make you feel like you missed something real if you weren't there. Spawn rates for the genuinely rare Pokémon, tuned on a variable schedule, not a fixed one — the exact same reinforcement structure behind a slot machine, because a reward you can't predict the timing of is measurably more compelling than a reward you can set your watch to. None of that is an accident, and none of it is lazy. That system got the careful, iterated, data-driven engineering attention that the gym rework and the AR feature never got, because it's the one system actually tied to the metric the company is optimized around: whether you open the app tomorrow.
We keep grading systems on one axis. Is it well built? Is the architecture clean? Is the debt paid down? Pokémon Go fails that test in at least three specific, documentable places, and I just spent the last few minutes walking you through them — and the test I just gave it isn't the test that determined whether the app won. Good engineering and successful engineering are correlated often enough that we've started treating them as the same word. They're not. They're two different axes. You can max out either one mostly independent of the other, and the uncomfortable version of that sentence is that the axis this app maxed out is the one that predicts revenue, and the axis it left half-built is the one we'd actually praise in a code review.
And here's the specific reason I don't get to sit above this one — this isn't just a story about somebody else's app. I know all of this. I could diagram the retention loop on a whiteboard before you finished asking me to. I teach a version of this exact argument — that a system engineered around a behavioral metric will outperform a system engineered around technical elegance — to graduate students. And I still spun that PokéStop this morning without looking at the screen. Seeing the mechanism clearly and having that change your behavior are two separate events, and the first one does not reliably cause the second one. I'm not the exception to that. I'm just a version of it with a little better vocabulary for describing what's happening to me while it happens.
So here's what I actually want you to take from this, and it's not "go delete Pokémon Go off your phone," because I'm not going to, so I'd be a hypocrite asking you to.
It's this: the next time your team ships something that wins — a feature that takes off, a product that finds an audience, a system that everybody's suddenly excited about — go back and actually separate the two questions before you let the win answer both of them at once. Is this well engineered? And, completely independently: is this why it's winning? Those questions have different answers more often than our instincts want to admit, and a team that only ever asks the first one is going to keep getting surprised by which of their systems actually matter to the business. A team that only ever asks the second one is going to keep accumulating exactly the kind of debt I spent this whole episode walking through: the reused foundation nobody re-audited, the fix that only relocated the failure, the concept car still parked in the roadmap. Both mistakes are real. They're just not the same mistake, and they don't get caught by the same review.
Pokémon Go isn't a cautionary tale where the bad engineering eventually caught up with it. It's been almost a decade. It hasn't. It might not. Sometimes the thing that wins really is the thing that's lazily built in three places and brilliantly engineered in exactly the one place that mattered for the metric somebody was actually optimizing. Learning to tell which place is which, in your own systems, before the postmortem tells you, is most of the job.
That's this episode of Chaotic Commits. I'm Joanne Skiles, who can explain exactly why this app is badly built, and opened it twice while I was writing that sentence. New episodes wherever you get podcasts. No highlight reel, just the commit history. I'll see you in the next one.