Steven's Knowledge

Best Practices

Production edge functions - limits, cold starts, observability, secrets, dev workflow, cost, pitfalls

Best Practices

Edge functions are deceptively simple to deploy and surprisingly easy to mis-deploy. These habits keep them fast, cheap, and reliable.

Stay Within the Right Niche

The single biggest mistake: using edge for things it's bad at. Edge functions are for request-shape transformation, not heavy compute or stateful logic.

Use the edge for:

  • Routing, auth, header rewrites
  • Personalization injection on cached HTML
  • A/B testing, canaries, geo
  • API aggregation (BFF pattern)
  • Streaming (LLM, SSE)

Don't use the edge for:

  • Heavy DB queries against a single-region database (latency)
  • Long-running tasks (CPU limits)
  • Anything needing local filesystem
  • Complex stateful workflows

When you find yourself rewriting Node.js code in an edge function, you've probably picked the wrong tool.

Know the Limits

Edge runtimes are tightly constrained. Plan for them:

ConstraintTypical limit (Cloudflare Workers)Implication
CPU time per request10 ms (free), 30 s (paid)Compute-heavy code must move to origin
Wall-clock time30 s including waitsLong-running outbound work doesn't fit
Memory128 MBBig data structures don't fit
Outbound fetch per request50 (free), 1000 (paid)Fan-out APIs have a ceiling
Script bundle size1 MB (free), 10 MB (paid)Pick light dependencies
Concurrent requests per isolate~1000Mostly fine; explosive traffic spawns more isolates

The patterns: early-exit on obvious responses, stream large responses rather than buffer, lazy-load heavy code, fan-out cautiously.

Cold Starts and Hot Isolates

V8 isolates spin up in 1-5 ms — orders of magnitude faster than container-based serverless. But:

  • First request to a new POP for your Worker takes longer (script needs to be loaded).
  • After ~30 seconds idle, the isolate may be evicted.
  • Globally distributed traffic keeps more POPs warm.

Practical implications:

  • Don't precompute expensive globals at module load — they run on every cold start.
  • Use top-level await carefully — it gates the first request.
  • Synthetic traffic to your endpoints keeps hot regions warm (a CDN's job, but you can add scheduled pings to less-trafficked routes).

Vercel Edge and Deno Deploy have similar characteristics. Fastly Compute@Edge boots WASM modules in sub-ms — even better cold starts.

Observability

Edge logs are scattered across hundreds of POPs. Strategies:

MechanismUse
console.log shipped to a backendCloudflare Logpush, Vercel Logs, Fastly real-time logs — pipe to your central log store
Structured loggingJSON to stdout; ship to Loki / Elasticsearch / your SaaS
TracingPropagate traceparent headers; emit spans from the edge
Sampled loggingDon't log every request at full detail — sample 1-5%

Cloudflare's Logpush integration writes worker logs to S3, R2, GCS, or HTTP endpoints in batches. Vercel ships logs to their dashboard or via integrations. Whichever platform, get logs off the edge into your normal stack.

Errors and Failures

export default {
  async fetch(request, env, ctx) {
    try {
      return await handleRequest(request, env, ctx);
    } catch (error) {
      // Log to your error tracker
      ctx.waitUntil(reportError(error, request, env));

      // Fall through to origin or static fallback
      return fetch(request).catch(() =>
        new Response('Service temporarily unavailable', { status: 503 })
      );
    }
  },
};

Every edge handler should have a top-level try/catch with a fallback. A bug at the edge is in front of every user.

For error reporting, Sentry, Datadog, and Bugsnag all have edge SDKs.

Secrets

Don'tDo
Hardcode secrets in sourcewrangler secret put NAME, vercel env add, etc
Commit .dev.vars to git.gitignore it; share via your team's secret manager
Log secretsMask if necessary; prefer not logging them at all
Send secrets to the browserEdge code is private; client code isn't

Most platforms encrypt secrets at rest and inject them at runtime — they don't appear in logs unless you explicitly log them.

Dev Workflow

The standard workflow:

# Local dev — same runtime as production
wrangler dev               # Cloudflare Workers
vercel dev                 # Vercel Edge Functions
deno run --watch ...       # Deno Deploy
netlify dev                # Netlify Edge Functions
fastly compute serve       # Fastly Compute@Edge

Test against a local runtime that matches production. Don't develop on Node.js and discover Web-API differences at deploy time.

CI:

  • Lint / type-check with tsc or biome.
  • Unit tests with vitest or your test runner.
  • Integration tests against a deployed preview environment.
  • Smoke tests post-deploy hitting health endpoints.

Deploy Strategy

Edge functions deploy fast (5-20 seconds globally). That's a double-edged sword — easy to ship broken changes worldwide.

Mitigations:

  • Preview deployments — Cloudflare Workers, Vercel, Netlify all support per-branch preview URLs. Test there first.
  • Gradual rollout — Cloudflare Workers supports wrangler deploy --gradual 25 (Workers for Platforms / Versioning).
  • Same blue-green pattern as classic apps — deploy worker-v2, swap routes, leave v1 for rollback.

Cost

Edge function pricing is per-million-requests, very cheap at small scale, accumulating at large scale.

ConcernMitigation
Subrequests dominateCache aggressively; avoid fan-out per request
KV readsLower the chattiness; cache results in-isolate per request
Durable Object requestsUse sparingly — they're more expensive than KV
Image transformationsThey're cheap per-transform but multiply with traffic
D1 / R2 / queue opsPer-operation pricing — usually rounding error, watch at scale

Cloudflare's Workers Paid plan ($5/month) covers most small-to-medium teams. Vercel and Deno Deploy similar generous tiers. At enterprise scale, costs scale proportional to request volume; far cheaper than equivalent Lambda usage.

Compatibility Gotchas

Edge runtimes are not Node.js. Common surprises:

Looks likeActually
require('crypto').createHash(...)Use crypto.subtle.digest(...) (Web Crypto)
fs.readFile(...)Doesn't exist; bundle assets or fetch from R2 / KV
process.env.XOften available, but prefer env.X (binding)
Express middlewareDoesn't work; build with Web API primitives or use itty-router / hono
Large npm packagesMany have Node-only deps; check engines, look for "isomorphic" variants

The ecosystem has converged on Web-standard APIs. The fewer Node.js-specific dependencies you use, the easier the edge story.

Frameworks built for edge: Hono (excellent for Workers/Deno), itty-router (tiny), Next.js (Edge runtime), SvelteKit, Remix, Astro, Nuxt — all support edge deployment for their server logic.

When Edge Is Wrong

Honest signs you're using the wrong tool:

  • You keep hitting CPU/memory/subrequest limits.
  • You're polyfilling Node APIs.
  • You need pinning to a region (data residency, single DB).
  • Your bundle is 50 MB.
  • You're trying to run a database / cache server (it's not a long-running process).

In these cases: move the workload to a regional container or Lambda; keep the edge for the request-shape work that does belong there.

Common Pitfalls

PitfallSymptomFix
Single-region DB calls from edgeEach request has 100+ ms tail latencyCache reads at edge; do writes in a sidecar
Inline secretsLeaked on Git pushUse wrangler secret / platform secrets
Big dependenciesSlow deploys, hit bundle limitTrim deps; use lighter alternatives
Synchronous CPU-heavy workHit CPU limit, request failsMove to origin
No error fallbackEdge bug = 500 for everyoneTop-level try/catch with origin fallback
Long-lived connectionsThey don't work; isolates are ephemeralUse queue patterns, not long-poll
Per-isolate global stateInconsistent across requestsDon't rely on in-isolate state; use KV / DO

Checklist

Production edge functions checklist

  • Workload genuinely fits the edge niche (request transformation, not heavy compute)
  • Top-level try/catch with origin or static fallback
  • Secrets via platform secret store, never inline
  • Logs shipped off-edge to your central log store
  • Tracing context (traceparent) propagated through fetch
  • Errors reported to a tracker (Sentry, Datadog)
  • Bundle size under platform limits, with headroom
  • Local dev matches production runtime (wrangler dev etc)
  • Preview deployments tested before production
  • Synthetic checks against deployed endpoints
  • Documented limits and what to do when hitting them
  • KV reads optimized; per-request caching of repeated lookups
  • Cost dashboard reviewed; alerts on unexpected usage spikes
  • Rollback path documented (versioning / blue-green / wrangler rollback)

On this page