0012 — Integrations are seams, and stay unconfigured until they earn it
Unconfigured is a supported state that fails at the call site with the variable's name — not at build time, and not with an opaque 401.
Context
A starter that wires in Sentry, PostHog, an email provider and three AI vendors looks generous and is not. Every one is a decision the buyer did not make, a key they must obtain before the app boots, and a dependency they will remove.
Decision
Each integration is a seam: one small module every caller goes through, so swapping providers or putting a gateway in front of one is an edit in a single file rather than a hunt through features.
The seams are deliberately thin. The AI provider has no retry logic, no fallback chains, no model routing — "real needs at scale and premature before it", and the SDK's middleware is where they belong when an incident makes the case.
Unconfigured is a supported state. Missing keys fail at the call site naming the variable, not at build time and not with an opaque 401 from the vendor.
Error reporting is the clearest example: the Sentry seam exists and no vendor is wired in. "Deliberately absent until a real incident makes the case."
Consequences
The exceptions prove the rule and each is documented where it lives. Inngest is
allowed to be unconfigured in a way Stripe is not, because it works against
npx inngest-cli dev with no keys at all — so an unconfigured Inngest is a
perfectly normal way to run the app.
Two route handlers exist in a codebase that otherwise mandates Server Actions,
and both say why in the file: Stripe's webhook, because a third party has to POST
and cannot call an action; and AI chat, because streaming is a Response
concern — actions resolve once, and forcing a stream through one means reaching
for experimental streamable values.
Expected failures are outcomes, not incidents, and never page.
The cost
This is the decision most likely to read as incompleteness. A buyer comparing starters sees "Sentry integrated" on one and a seam on the other, and the seam looks like less.
It is less. It is also the difference between a template that assumes your observability vendor and one that does not, and the seam is roughly twenty lines either way.
0011 — State management has a single answer
Server state is RSC and server actions. URL state is nuqs. Local state is useState. Everything else is a lint error.
0013 — The stack choices, including the four nobody wrote down
Drizzle, Better Auth, Server Actions and Inngest. What the code proves about each, and where the honest answer is that the reasoning was never recorded.