Development

Lazydev Mode

Understand the development-only build tag and control-plane endpoints used by lazy.

By Guillermo Alvarez - Published - Updated

What lazydev means

The default lazy development command builds your app with the lazydev build tag. That tag enables local-only behavior such as disk-backed views, logical asset paths, detailed error pages, editor opening from error frames, route inspection, and view-cache reloads.

Production builds do not include lazydev handlers.

Development control plane

When lazy starts the app, it also passes CONTROL_PLANE_ADDR so the app can serve development endpoints on an internal control-plane address:

GET  /routes
GET  /buildinfo
GET  /dependencies
GET  /dependencies/shutdown
POST /dependencies/shutdown
GET  /dependencies/shutdown/events
GET  /cache
GET  /cache/entry
POST /cache/on
POST /cache/off
GET  /jobs
POST /views
POST /_golazy/views/reload
POST /_golazy/open-editor

GET /routes returns the registered route table. GET /buildinfo returns the running app's Go module build information from runtime/debug.ReadBuildInfo, including Go version, command path, main module, dependencies, replacements, and build settings such as VCS metadata when the toolchain recorded them. GET /dependencies returns the lazydeps application service graph with the app root, service nodes, and directed dependency edges. The shutdown endpoints expose a lazydev-only simulation: POST /dependencies/shutdown can delay for test traffic, mark /readyz not ready, report active requests and connections, wait for active requests to finish, and cancel services while GET /dependencies/shutdown/events streams each state change. GET /cache returns cache enabled state, stats, keys, and inspectable entry metadata exposed for development inspection. GET /cache/entry?key=... returns a selected entry body when the backend supports inspection. POST /cache/on and POST /cache/off toggle the app cache. GET /jobs returns lazyjobs definitions, counts, and recent job state. POST /views reloads disk-backed views without rebuilding or restarting the app. The /_golazy/views/reload route remains as a compatibility alias.

Detailed error pages call POST /_golazy/open-editor when you click a frame. The request includes an absolute file path and line number, and GoLazy starts $EDITOR without waiting.

Development request traces

The lazy development proxy assigns request correlation before forwarding app traffic. It preserves safe incoming X-Request-ID and valid W3C traceparent headers, or generates both when they are missing. The app telemetry middleware uses those headers for its request id, request span, logs, metrics labels, and the response X-Request-ID.

lazy does not force OTEL exporters into the child app. Lazydev builds install request telemetry unless OTEL_SDK_DISABLED=true is present, but detailed request monitoring is off by default. Enable it from the development panel to write local files:

.tmp/traces/<request-id>.trace
.tmp/traces/<request-id>.spans
.tmp/traces/<request-id>.log.json

The .trace file is a Go runtime trace. The .spans file is JSON with request and span metadata plus request-level allocation counts, memory deltas, system memory, stack, GC counters, and lazydev-only per-region allocation samples. The .log.json file is JSONL for request-local logs emitted through lazytelemetry or lazylogs. Runtime tracing is process-wide, so lazydev serializes traced requests while each .trace file is recorded.

The Requests tab reads the app control plane's /requests/traces snapshot, which is built from the .spans and .log.json sidecars. Its search is server-side path filtering through the q query parameter, and its type filter uses type=framework, type=assets, type=other, or all requests. Request type is derived from the last traced middleware that handled the request without calling the next handler. The tab shows recent captured request paths, then opens request detail tabs for Headers, Tracing, and Logs. The Tracing tab starts with a request status strip, then a backend-sorted region table and a flamegraph. Turn on Include golazy to include middleware, router, dispatch, and other framework regions. Table headers sort by total or self time, allocation count, and memory bytes; the flamegraph scale follows the selected metric. Flamegraph bars use square classical flamegraph colors, show only the span name inside the bar, and keep full timing and allocation details in the browser tooltip. The region metrics table above the flamegraph keeps compact rows without reducing text size. The flamegraph pane grows to the graph's full height; once the table is at its minimum height, long graphs make the request detail area scroll for inspection instead of clipping inside the flamegraph pane. When the sidecar includes lazydev allocation samples, those region allocation values are process-wide runtime.ReadMemStats deltas sampled when spans start and end, so they are development estimates rather than exact runtime.mallocgc attribution.

Those request spans include framework child regions for middleware, routing, dispatch, controller setup, action calls, view rendering, layouts, and partials. Region names include concrete middleware, controller/action, template, and partial identifiers for Go runtime trace inspection.

Read Development Panel for the panel UI and Control Plane for production control-plane configuration.