A Million Downloads Isn’t Scale: What “Scalable” Actually Means on the Client
I keep running into the same word used to mean nothing.
Someone introduces themselves as a developer who builds scalable mobile apps — and the evidence is that an app they worked on has over a million downloads. Somewhere else there’s an article titled along the lines of How BLoC Helped Me Build a Scalable Flutter Application, and someone asked a question that cuts straight to the bone: “How does BLoC help someone scale? Wouldn’t that be the same for any other state management, or am I missing something?”
You’re not missing anything. That instinct is correct, and it’s the whole reason this article exists.
“Scalable” has become the mobile engineering equivalent of “synergy.” It signals that something is good without committing to what. And the confusion isn’t anyone’s fault really — the word was borrowed from a place where it had a precise meaning, dragged onto the client, and quietly stripped of it.
Where the word comes from
On the backend, scale has a definition you can argue about with numbers. It’s the system’s ability to absorb a large increase in concurrent load without falling over, slowing to a crawl, or costing an absurd amount of money. Ten thousand requests a second become a hundred thousand; can your service take it, and at what price? That’s a real, measurable property of an architecture. Horizontal scaling, connection pools, sharding, caching layers — these exist because more users hitting the same server is a genuine engineering problem.
Now carry that definition over to a mobile app and watch it dissolve.
The client is single-tenant, and that changes everything
A backend serves everyone from shared infrastructure. A mobile app does not. Every user runs their own private copy, on their own device, against their own CPU and memory. There is no shared resource on the client that gets more contended as your user base grows. The app binary running for your ten-thousandth user is doing exactly the same work as it did for your first.
This is why “I build scalable apps because we hit a million downloads” falls apart the moment you poke it.
Downloads measure distribution. They’re a marketing and product outcome, not an engineering one. They’re also cumulative — that million includes everyone who installed once, opened the app, hated it, and uninstalled. It isn’t even monthly actives, let alone concurrent load. You can rack up a million downloads on a codebase held together with setState and prayer, and you can have five hundred downloads on something architected immaculately. The number tells you the marketing worked. It tells you nothing about what the engineer built.
And here’s the part that gets skipped: if anything strained when that app hit a million users, it was the backend — the API, the database, the queues. The client’s only relationship to that load is whether it behaves like a good citizen or a bad one. More on that later.
So the first correction is simple. On the client, scale is never about how many people use the app. That axis isn’t yours.
Tools don’t grant scale. Discipline does.
Which brings us to BLoC, and to the broader genre of “X helped me build a scalable app.”
Get Samuel Abada’s stories in your inbox
Join Medium for free to get updates from this writer.
The commenter’s question deserves a straight answer, so here it is: yes, the same is true of any disciplined state management. BLoC didn’t make that app scalable. What BLoC gave the author was a structure — enforced separation of business logic from UI, a predictable unidirectional flow, testable transitions, the ability to scope rebuilds so changing one thing doesn’t repaint the world. Those properties genuinely help an app stay maintainable and performant as it grows.
But none of them are unique to BLoC. Riverpod gives you the same separation and granular rebuilds. Signals gives you fine-grained reactivity that’s arguably better at scoping updates. Even plain ChangeNotifier with disciplined boundaries gets you most of the way. The property the author is praising — predictable, testable, scoped state — is a property of discipline, and BLoC was just the vehicle that enforced it for them.
Here’s the proof that the tool isn’t the property: a bad BLoC implementation scales worse than good Signals. One god-bloc holding the entire app’s state, emitting on every change and rebuilding half the tree, is less maintainable and less performant than a cleanly scoped Signals setup. Same library, opposite outcome. If the tool granted the property, that couldn’t happen.
So when you read “X helped me build a scalable app,” translate it: “X gave me a structure that stayed maintainable as my codebase grew, and I had the discipline to use it well.” That’s a true and useful sentence. “X scales” is not.
So what does scale mean on the client?
It does mean something — it just splits into three axes that have nothing to do with user count and everything to do with what is growing. The honest question to ask anyone who says “scalable” is: scaling along which axis, and what’s the bottleneck?
Data and usage scale — the app under a heavy single user. This is the only axis that touches performance, and it’s per-device. A list that’s smooth at 50 items and locks the UI thread at 50,000. An infinite image feed that climbs toward an out-of-memory crash because nothing’s being disposed and the cache is decoding full-resolution bitmaps. A local database that queries instantly on a fresh install and stalls once a power user has years of data in it. Scaling this axis is the familiar craft: ListView.builder instead of building everything, sized image caches (memCacheWidth/memCacheHeight rather than decoding 4000px images into 200px slots), heavy work pushed to isolates, controllers disposed, rebuilds scoped. Note that none of this depends on your download count. One user with a lot of data triggers all of it.
Codebase scale — the app under a growing feature set. When the project is three screens, everything in lib/ is fine. At eighty screens and a dozen feature teams, that same flat structure collapses into merge conflicts and fear of touching anything. Scaling here means clear layer boundaries, feature-first organization, breaking things into isolated packages so coupling stays low and compile times stay sane, and making the codebase changeable — because the real test of a large app isn't whether it runs, it's whether you can add a feature next quarter without it taking three times as long as it should.
Process scale — the app under a growing team. This is the axis people forget, and it’s often the one that actually bites. Can fifteen engineers ship into the same repo without constant breakage? That’s CI/CD running tests and lints on every PR, a real testing pyramid (fast unit tests for logic, widget tests for components, a thin layer of E2E with something like Patrol or Maestro for critical flows), and a shared design system so twenty developers building twenty features don’t produce twenty subtly different buttons. Manual testing and hand-deployed builds don’t scale to a team; that’s not a metaphor, it’s just true.
The scale that isn’t yours
There’s one last thing worth naming, because it’s where the original confusion lives. There is a user-count scaling problem in any app with a backend — it’s just not on the client. Your job, client-side, is to not make it worse. Paginate instead of fetching everything. Back off and retry instead of hammering a struggling endpoint. Cache so you don’t re-request what you already have. Tolerate being offline. None of this makes your app scale — it makes your app a well-behaved citizen against the thing that actually has to.
That distinction is the entire point. The client doesn’t scale to more users. It stays fast under more data, stays maintainable under more features, and stays shippable under more developers.
The litmus test
Next time you’re about to call something scalable — your app, your architecture, your favorite library — stop and finish the sentence:
Scalable along the ____ axis, because the bottleneck would otherwise be ____.
If you can fill both blanks, you mean something. If you can’t, you probably mean “well-built” — and that’s a fine thing to mean, so just say that instead.
Precision isn’t pedantry here. The reason that commenter’s question was so good is that they refused to let a vague word slide, and in doing so they understood the topic better than the article they were questioning. Be that commenter.











