There is a quiet shift happening in the way we think about where code runs. For the last decade, the answer to almost every infrastructure question has been "put it in the cloud." Need to scale? Cloud. Need reliability? Cloud. Need to stop worrying about hardware? Cloud. And for most of that decade, the cloud was the right answer.
But something changed around 2024. Latency budgets tightened. Data sovereignty regulations multiplied. Cloud bills became a board-level concern. And a new generation of tools made it genuinely practical to run meaningful workloads closer to users, on hardware you actually control.
This is not a post about abandoning the cloud. It is about understanding when the edge is the better tool for the job, and how to think clearly about the trade-offs.
The Pendulum Swings
If you have been in this industry long enough, you have seen this pattern before. Mainframes gave way to client-server. Client-server gave way to web apps. Web apps moved to the cloud. And now parts of the cloud are migrating to the edge. It is the same fundamental tension between centralization and distribution, playing out on a new stage.
The future of computing is not centralized or distributed. It is a spectrum, and the best architectures know where to place each workload along that spectrum.
Werner Vogels, CTO of Amazon Web Services
What makes this iteration different is the tooling. Five years ago, running code at the edge meant wrestling with Cloudflare Workers' early limitations or cobbling together your own CDN-like infrastructure. Today, the ecosystem is mature enough that a solo developer can deploy a globally distributed application in an afternoon.
What Changed in the Last Two Years
Three things converged to make edge computing practical for everyday applications:
- Runtime standardization. WinterCG (now WinterTC) brought consistency to edge runtimes. Code that runs on Cloudflare Workers also runs on Deno Deploy and Vercel Edge Functions with minimal changes. The "write once, run anywhere" promise is finally real for edge workloads.
- Edge-native databases. Solutions like Turso (libSQL), PlanetScale, and Neon now offer read replicas that sit inside edge PoPs. The database is no longer the bottleneck that makes edge deployments pointless.
- Cost models shifted. Cloud egress charges became untenable for media-heavy applications. Running your own edge nodes with providers like Fly.io or Hetzner can cut bandwidth costs by 80% or more.
Practical Edge Patterns I Use Every Day
After a year of running production workloads at the edge, a few patterns have proven themselves reliable. These are not theoretical -- they are running in production right now, serving real users.
Pattern 1: Edge-Side Rendering with Stale-While-Revalidate
The most impactful change was moving server-side rendering to the edge. Pages render at the PoP closest to the user, pulling data from a nearby read replica. The first render uses cached data while a background revalidation ensures freshness for the next request.
Code Walkthrough: An Edge-First API Router
Let me show you a real example. Here is the core of an edge-first API router I have been using across several projects. It handles routing, middleware, and response caching in under 100 lines.
import { Context, Middleware, RouteHandler } from './types'; interface Route { method: string; pattern: URLPattern; handler: RouteHandler; middleware: Middleware[]; } export class EdgeRouter { private routes: Route[] = []; private globalMiddleware: Middleware[] = []; // Register a route with optional per-route middleware register( method: string, path: string, handler: RouteHandler, ...middleware: Middleware[] ) { this.routes.push({ method, pattern: new URLPattern({ pathname: path }), handler, middleware }); }
The key insight here is using the URLPattern API, which is natively available in edge runtimes. No need for a routing library. The pattern matching is fast and the API is standardized across all major platforms.
Now here is the request handler that ties everything together:
async handle(request: Request): Promise<Response> { const url = new URL(request.url); // Find matching route const match = this.routes.find(r => r.method === request.method && r.pattern.test(url) ); if (!match) { return new Response('Not Found', { status: 404 }); } // Build the middleware chain const chain = [ ...this.globalMiddleware, ...match.middleware ]; // Execute middleware, then handler const ctx: Context = { request, params: match.pattern.exec(url)?.pathname.groups ?? {}, state: new Map(), env: this.env }; for (const mw of chain) { const result = await mw(ctx); if (result) return result; // Short-circuit } return match.handler(ctx); } }
Notice the highlighted lines where we compose the middleware chain. The global middleware runs first, followed by route-specific middleware. Any middleware can short-circuit the chain by returning a Response directly -- perfect for auth checks, rate limiting, or cache hits.
Using the Router
Here is how you would wire up a simple API with caching middleware:
import { EdgeRouter } from './router'; import { withCache, withAuth, withCors } from './middleware'; import { getPosts, getPost, createPost } from './handlers'; const router = new EdgeRouter(); // Global middleware runs on every request router.use(withCors({ origin: '*' })); // Public routes with edge caching router.register('GET', '/api/posts', getPosts, withCache({ ttl: 60, staleWhileRevalidate: 300 }) ); router.register('GET', '/api/posts/:slug', getPost, withCache({ ttl: 120 }) ); // Protected route: auth middleware runs before handler router.register('POST', '/api/posts', createPost, withAuth({ roles: ['admin', 'editor'] }) );
Clean, readable, and the caching happens transparently at the edge. The withCache middleware checks the edge cache first and only calls the handler on a miss. The staleWhileRevalidate option means users always get a fast response, even when the cache is refreshing in the background.
The Trade-offs Nobody Talks About
Edge computing is not a silver bullet. After a year of running production workloads this way, I have hit every sharp edge (pun intended). Here are the honest trade-offs:
Debugging distributed systems is like trying to find a black cat in a dark room. Debugging edge systems is like trying to find a black cat in thirty dark rooms simultaneously.
A colleague, after our first edge deployment outage
Observability is harder. When your code runs in 30 locations simultaneously, traditional logging falls apart. You need structured logging from day one, a centralized aggregation pipeline, and distributed tracing. I use a combination of Grafana Cloud and custom OpenTelemetry instrumentation. It works, but it took weeks to get right.
Cold starts still matter. Edge runtimes have gotten faster, but cold starts on rarely-hit PoPs can add 50-200ms to the first request. For APIs with consistent traffic, this is a non-issue. For long-tail routes that only get hit from specific regions, you need to think about warming strategies.
State management is genuinely hard. Anything beyond read-heavy workloads requires careful thought about consistency. I have settled on a pattern where writes go to a central origin database, and reads come from edge replicas with a configurable staleness budget.
What I Actually Run at the Edge
Not everything belongs at the edge. Here is my current split:
At the edge: API routing, authentication, static asset serving, server-side rendering, rate limiting, A/B testing, geolocation-based content, and read-heavy API endpoints. These all benefit from being close to users and handle eventual consistency gracefully.
At the origin: Database writes, payment processing, complex business logic with strict consistency requirements, long-running background jobs, and anything that needs more than 128MB of memory. These need the reliability and power of a traditional server.
The boundary between edge and origin is not fixed. I evaluate it for each feature based on three questions: Does this need strong consistency? Does this need more than a few seconds of compute? Does latency meaningfully impact the user experience?
Looking Ahead
The edge is not going to replace the cloud any more than the cloud replaced on-premise. But it is becoming an essential layer in the stack for applications that care about performance, cost, and user experience.
What excites me most is the developer experience trajectory. Two years ago, deploying to the edge felt like a hack. Today, it feels natural. The tools are getting out of the way, and that is when real adoption happens.
If you are building a new application in 2026 and you have not considered where each piece of your stack should physically run, you are leaving performance and money on the table. The pendulum has swung, and the teams that recognized it early are building faster, cheaper, and closer to their users than ever before.
The best architecture is the one that puts each workload exactly where it belongs. Sometimes that is a hyperscaler region. Sometimes it is a 4GB VPS in Frankfurt. The craft is knowing which is which.
If you want to discuss this further, reach out. I am always happy to talk shop about infrastructure decisions. And if you are just getting started with edge computing, the router pattern above is a genuinely good place to begin. Start small. Deploy a single endpoint. Measure the difference. You will be surprised.
Until next time -- ship small, ship often, and keep your latency budgets honest.