# Production Readiness

> Production Readiness: a worked guide from route registry through to a production build.

Source: [https://askrjs.com/docs/guides/production-readiness](https://askrjs.com/docs/guides/production-readiness)

Status: stable. Packages: @askrjs/askr.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

Verify the locked published packages, tests, production renderer, generated routes, and deployment fallback in the same order CI uses.

```sh
npm ci
npm run lint
npm run test
npm run build
```

## Goal 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.

## Documentation navigation

[Previous](https://askrjs.com/docs/guides/testing-http-applications/index.md) | [Next](https://askrjs.com/docs/guides/migration-from-react/index.md)
