# Backend Performance — Derived Rules > Derives from `domains/performance/first-principles.md` P1 (Measure First), P3 (Complexity Awareness), P4 (Resource Bounds). ## Measure First (P1) - p50, p95, p99 latencies. The average hides the long tail. - Throughput (req/s) under load. Saturation point (where latency rises). - Resource utilization: CPU, memory, I/O, network. Each is a budget. ## N+1 Queries (P3 Complexity Awareness) - A query in a loop is an N+1. It is O(N) queries instead of O(1). - Detect with a query counter in tests. A test that issues 100 queries is failing. - Fix with a JOIN, a batch load, or a dataloader. Never "we'll fix it later." ## Caching (P5 Caching with Intent) - Cache what is: expensive to compute, stable, read often. - Invalidation is designed: TTL, event-based, or version-based. Never "we'll just clear it." - A cache without an invalidation strategy is a cache that serves stale data forever. - Multi-level: HTTP cache → CDN → app cache → DB. Each layer has its own rules. ## Async and Concurrency (P7 Async When Independent, see `domains/concurrency/`) - I/O-bound work is async. Don't block a thread on a network call. - CPU-bound work is in a worker, not the request path. - Bounded queues everywhere (P9 Bounded Queues). Unbounded = OOM. ## Database (P4 Resource Bounds, see `domains/data/indexing.md`) - Connection pool: bounded. The DB has a connection limit; the pool respects it. - Slow queries: logged, explained, fixed. A 10-second query is a bug. - Pagination on large tables: cursor, not offset. Offset scans rows. ## Resource Bounds (P4) - Memory: bounded. A request that allocates unbounded memory is a DoS vector. - Timeouts: every external call has one. A call without a timeout is a call that can hang forever (P8 Timeout Discipline). - File handles, DB connections, HTTP connections: all bounded, all pooled. ## What Violates Backend Performance | Violation | Principle | |-----------|-----------| | N+1 query in a loop | P3 | | No timeout on an HTTP call | P4, P8 (concurrency) | | Unbounded in-memory sort | P4 | | Cache with no invalidation | P5 | | `SELECT *` | P4 (data P10) | | Connection pool size = 1000 | P4 (DB limit) |