What Are You Delegating?
Every abstraction is a trade: it removes work by making decisions for you. Those decisions are still being made — just not by you, and not visibly. For each abstraction: what it genuinely handles, what remains yours, and the escape hatch.
An ORMAn HTTP client or SDKA managed database or cloud platformAn identity providerAn LLM or coding agentA web frameworkA library functionA managed message queueA cacheA container runtime and orchestrator
What are you delegating to a web framework?
ArchitectureYou write
1app.post('/orders', validate(schema), async (req, res) => res.json(await createOrder(req.body)))The abstraction handles
- ✓Routing, parsing, content negotiation, error rendering
- ✓The request lifecycle: middleware order, streaming, keep-alive
- ✓Validation, serialization, CSRF, CORS defaults
- ✓Dependency injection, configuration, lifecycle hooks
- ✓Years of edge cases you will never have to meet
Still your responsibility
- →The runtime model — one event loop or a thread pool; what a blocking call costs
- →HTTP semantics — status codes, idempotency of methods, caching headers are contract decisions
- →Backpressure — the framework accepts connections faster than your database serves them
- →What "the framework is slow" actually means — usually one middleware, one query, or one synchronous call in the path
- →Boundaries — frameworks make everything equally easy, including the things that should be hard (a transaction across an HTTP call)
Know your escape hatch
When: A request behaves differently from what the framework's abstraction promises: timing, ordering, streaming, connection handling.
Drop to: The lower-level runtime and protocol: the event loop, the socket, the raw request lifecycle.
Frameworks remove repetitive work. When something is slow, incorrect or unreliable, the answer is one layer down.
Go deeper on this one: