On this page
A Flutter/Firebase product I run engineering for has, at last count, roughly 17 separate Cloud Functions codebases — each independently deployed, each with its own dependencies, each born the same way: a feature needed a backend endpoint, and the path of least resistance was another folder with another index.js. No one decision was wrong. The sum is a backend that exists as an archipelago.
This is a decision post, not a victory lap — the consolidation is planned, not shipped. But the planning surfaced trade-offs that I think generalize to anyone staring at their own functions sprawl.
Why move at all: cold starts
The forcing function is latency. Gen-1 Cloud Functions cold-start, and a backend made of 17 small, separately-warmed functions cold-starts a lot — each function is its own instance pool, so low-traffic endpoints are nearly always cold. Users feel it as the app’s random slowness: one tap fast, the next tap seconds.
Two consolidation shapes fix this:
- A single gen-2 service (one HTTP app — Hono or Express — behind one function) with minimum instances, so there is always a warm instance and one shared pool absorbs all HTTP traffic.
- Cloudflare Workers, where cold start effectively stops being a concept, and which the rest of my stack already leans on — see my infrastructure post.
Either way, the architectural move is the same: HTTP endpoints stop being seventeen deployables and become one app with a router.
What stays behind
Not everything should move. Trigger-based functions stay put. Firestore triggers, Auth triggers, storage triggers — these exist because they sit inside Firebase’s event system, and re-platforming them means rebuilding event delivery for no gain. The consolidation target is the HTTP surface: the request/response endpoints the app calls. Splitting along that line keeps the migration honest — event plumbing stays where events live; request handling goes where requests are served best.
The thing you actually lose
Seventeen codebases have one genuine virtue nobody designed on purpose: per-function deploy control. Ship a fix to one endpoint and you cannot break any other, because you didn’t deploy any other. Blast radius is a folder.
Consolidation trades that away — one app means one deploy touching every route — and pretending otherwise is how consolidations get rolled back. The mitigations are real but must be chosen deliberately: a router structured so each module stays independently testable; deploys gated by a test suite that covers routes you didn’t change; and gradual cutover, endpoint by endpoint, with the old function left serving until its replacement has taken traffic. What you’re really buying back is confidence-per-deploy, and it has to come from tests and staged rollout now that it no longer comes from isolation.
The honest confession: the middleware debt
Here’s the part that makes consolidation more than a performance project. Seventeen codebases means seventeen places where auth, logging, and input validation were each implemented — or, honestly, not implemented. Grown-by-accretion function folders accumulate endpoints that lack consistent auth middleware and structured logging, because every new folder started from zero and shipped under deadline.
A single app has a single middleware stack. Consolidation is the one moment where putting shared authentication, request logging, and validation in front of every endpoint is cheaper than not doing it — the route table becomes a checklist, and an endpoint that shouldn’t be public has nowhere left to hide. That, as much as cold starts, is the real payoff: the migration forces an inventory that the archipelago made impossible.
The decision framework
If you’re weighing the same move, the questions that mattered:
- Where does latency hurt? If it’s user-facing HTTP, consolidate that surface. If your pain is elsewhere, min-instances on the hot functions may be the whole fix.
- What’s trigger-bound? Leave it. Migrate the HTTP surface only.
- What replaces per-function blast radius? Name the mechanism (tests, staged cutover, module boundaries) before the first endpoint moves.
- What debt does the move let you pay? Auth, logging, validation — write the endpoint inventory first; it doubles as the migration checklist.
- One platform or two? If the rest of your infrastructure already lives somewhere (mine leans Cloudflare), gravity matters more than benchmark deltas.
Seventeen folders was never a decision anyone made. The consolidation is the decision — which is exactly why it deserves a plan instead of an eighteenth folder.