Skip to content

Code Generation

The backend relies on five code generators. All of their output is committed to the repository, and CI fails if any of it is stale relative to its source.

The OpenAPI spec drives oapi-codegen, which generates the chi strict-server bindings and shared types from the bundled spec (dist/openapi.bundled.yaml). The generators are configured by backend/oapi-codegen.yaml (server) and backend/oapi-codegen-types.yaml (types).

Terminal window
make backend-generate-openapi-server

This target first bundles the spec, then generates the strict-server code and the types package. See API & OpenAPI for the spec-first workflow.

The dependency graph is wired with Wire. The generated internal/container/wire_gen.go is committed, and Wire is pinned via the go.mod tool directive so runs are reproducible in CI.

Terminal window
make backend-wire-gen # regenerate internal/container/wire_gen.go
make backend-wire-check # regenerate, then fail if it differs from the committed file

backend-wire-check catches DI drift — a hand-edited wire_gen.go, or a changed provider signature that was never regenerated. It is idempotent on a clean tree.

Service interfaces are mocked with mockery so handlers and services can be unit-tested against fakes.

Terminal window
make backend-mock-generate # regenerate all mocks
make backend-mock-check # drift gate (also run in CI)

Mockery runs without --all: the per-package interfaces: lists in backend/.mockery.yaml are authoritative, so mockery emits exactly those interfaces. Adding a mockable interface means adding its entry to .mockery.yaml first: a mock committed without an entry disappears on the next regenerate and fails backend-mock-check.

The generated mock_*.go files are committed. See Testing for how the mocks are used.

The nested config.Config struct drives a JSON-schema generator (backend/cmd/gen-config-schema) that produces the committed backend/config.schema.json. The schema gives editors (VS Code / JetBrains via the YAML language server) validation and autocomplete for config.yaml and config.example.yaml — see Configuration.

Terminal window
make backend-generate-config-schema # regenerate backend/config.schema.json
make backend-config-schema-check # regenerate, then fail if it differs from the committed file

backend-config-schema-check runs in CI and catches a changed Config struct that was never regenerated. It is idempotent on a clean tree.

Every running instance serves its spec at /openapi.yaml and /openapi.json. The served bytes are a committed, go:embed-ed bundle in internal/server/openapispec/, regenerated with Redocly:

Terminal window
make backend-generate-openapi-bundle # regenerate the embedded bundle
make backend-openapi-bundle-check # regenerate, then fail if it differs from the committed files

CI and pre-commit run the drift check. Regenerate the bundle in the same change as any spec edit.

GeneratorCommandOutput (committed)
oapi-codegenmake backend-generate-openapi-serverstrict-server handlers + internal/server/gen/types
Wiremake backend-wire-gen (backend-wire-check to verify)internal/container/wire_gen.go
mockerymake backend-mock-generate (backend-mock-check to verify)mock_*.go files
gen-config-schemamake backend-generate-config-schema (backend-config-schema-check to verify)backend/config.schema.json
OpenAPI bundlemake backend-generate-openapi-bundle (backend-openapi-bundle-check to verify)internal/server/openapispec/openapi.bundled.{yaml,json}