Production Readiness
Production Readiness: a worked guide from route registry through to a production build.
Example
Verify the locked published packages, tests, production renderer, generated routes, and deployment fallback in the same order CI uses.
npm ci
npm run lint
npm run test
npm run buildGoal and architecture
Production hardening in Askr is mostly about wiring the hooks the server already exposes: ServerAppOptions.probes for health checks, ServerAppOptions.onError for a last-resort error handler, and @askrjs/otel's createTelemetry() for tracing, rather than bolting on separate infrastructure. createTelemetry() is explicitly a bridge to whatever OpenTelemetry SDK you install — the package 'never installs an SDK, processor, backend, or exporter' — so you own the actual exporter configuration.
Implementation
Set probes: { livez, readyz, startupz, targetz } on createServerApp so your orchestrator (Kubernetes, a load balancer) can distinguish 'process is alive' from 'ready to take traffic'; each ProbeHandler returns a boolean, a custom Response, or void for a default response. Wrap request handling in telemetry.span('askr.request', fields, work) or use the narrower helpers (.loader, .action, .apiOperation, .ssrRender) so spans line up with Askr's own lifecycle events, and call telemetry.extract/.inject to propagate trace context across service boundaries. In front of the process, use serve(app, { port, host, signals }) from @askrjs/node, which installs signal handlers for graceful shutdown by default unless you pass signals: false.
Failure states
An unhandled exception in a handler should hit ServerAppOptions.onError(error, context) and return a safe response — without it, an uncaught error can leak stack traces or internal details to the client, so treat a missing onError as a launch blocker, not a nice-to-have. A readyz probe that always returns true defeats the purpose — wire it to a real dependency check (database reachable, cache warm) so your orchestrator actually stops routing traffic during a bad deploy instead of load-balancing into a broken instance.
Verification
Load-test the probe endpoints themselves under contention to confirm they don't become a bottleneck or false-negative when the process is merely busy, not actually unhealthy. Kill the process mid-request in a staging environment and confirm serve()'s signal handling drains in-flight requests before closing, rather than dropping connections immediately. Verify a deliberately thrown error in a handler is caught by onError and produces the sanitized response you expect, not a stack trace.