generation pipeline
rule
generation flows from the authoritative authored input to its consumers:
schemas and routes → OpenAPI → Fern client → application build. every package
that owns a generator exposes its own generate script; the root pipeline
orchestrates it through Turbo and never recreates generator behavior itself.
why
every downstream artifact is machine-replaceable — any hand repair to it is silently discarded on the next generation run. a one-directional chain with one authored source keeps a single contract; parallel hand-written types or edits to generated output drift while each piece still typechecks locally.
workflow
- change the source schema, route graph, or generator input.
- run the producer's
generatetask. - run the dependent generator, such as Fern.
- typecheck the generated package and consuming application.
- inspect the generated diff; repair its source, never its output.
good and bad
bad
a consumer breaks against a generated route, so the generated client method is edited by hand:
// apps/_client/src/generated/orders.ts — GENERATED FILE// WRONG — hand-edited generated outputexport function createOrder(input: CreateOrderInput): Promise<OrderResponse> {// original signature didn't match; someone changed it here instead of the sourcereturn fetch("/orders", { method: "POST", body: JSON.stringify(input) });}
the application builds and tests pass. the next pnpm generate run regenerates
the client from the backend schema and silently rewrites the hand-edited method
back to its original broken form. nobody notices until production.
good
the backend route contract is corrected at its source, the producer's task runs, and both packages regenerate:
// apps/backend/src/infra/http/orderRoutes.ts — AUTHORED SOURCEexport const orderRoutes = new Hono().post("/orders", async (c) => {const input = CreateOrderSchema.parse(await c.json());return c.json({ orderId: await createOrder(input) });});// Run: pnpm --filter @scope/backend generate// This regenerates OpenAPI from routes.// Then: pnpm --filter @scope/client generate// This regenerates the client from OpenAPI.// apps/_client/src/generated/orders.ts — AUTO-REGENERATEDexport async function createOrder(input: CreateOrderInput): Promise<{ orderId: string }> {// signature matches the authored route; next generate run will keep it stable}
verification
from a clean generated-output state, run the producer then every consumer. inspect that the generated diff corresponds only to the authored contract change, and search for handwritten duplicates of the new public type or route.