Skip to content

factory-pitfalls

Synced from factory-kit/skills/factory-pitfalls.md at v0.3.0. The source of truth is the factory-kit repo.

The skills are now structured Principle → Why → Recipe → Failure mode, with each anti-pattern co-located with the principle it violates. This file is a flat scan across those failure modes plus process-level pitfalls that don’t fit any one skill.

  • At project kickoff. Read this index; ensure the starting setup avoids the top-tier pitfalls (no test coverage, no DECISIONS.md, hardcoded allowlist).
  • In code review. Scan recent diffs for matches; link the relevant skill section in the PR comment.
  • After incidents. Add the failure mode to the skill where its principle lives (not here); if it doesn’t fit any skill, add a process pitfall below.

Each entry: one line, pointing at the skill section that owns it.

  • Mixed tRPC + server actions — factory-api.md §API style — pick one
  • Custom auth adapter when an official one exists — factory-auth.md §Better Auth — plugin composition
  • Triple-fallback auth surface (Clerk → token → header) — factory-auth.md §The wrapper interface
  • No auth at all (publicProcedure everywhere) — factory-auth.md §Auth from day one
  • Hardcoded email allowlist — factory-auth.md §Hardcoded email allowlists
  • Admin client at module scope — factory-auth.md §Admin client — always wrapped
  • Monolithic 1,500-line form — factory-forms.md §Modular section files from day one
  • No tests under src/ — factory-testing.md §Tests-before-merge — coverage gates, not test-first dogma
  • Mock-only tests passing while prod fails — factory-testing.md §Test the boundaries; trust the framework
  • Snapshot tests as the only coverage — factory-testing.md §E2E owns user flows; unit owns behavior
  • Two-way state-DB sync — factory-frontend.md §One direction of truth
  • Currency formatting drift across views — factory-frontend.md §Format helpers
  • Palette-position token names (primary / base-100) — factory-design.md §Token vocabulary — name intent, not palette position
  • Hex literal in a component file — factory-design.md §Token source — CSS variables, bridged into Tailwind
  • dark: variants sprinkled on individual elements — factory-design.md §Mode is a variable swap, not a parallel palette
  • Spacing tokens (--space-section-y-md) that duplicate Tailwind’s scale — factory-design.md §What gets a token, what stays a utility
  • Components referencing primitive-layer names (bg-blue-500) instead of semantic — factory-design.md §One layer or two
  • “I’ll lift this later” inline component — factory-design.md §Promote drift into a primitive
  • Token sprawl (60+ tokens, role names diluted into palette-noise) — factory-design.md §Hold the line on vocabulary size
  • Half-replaced theme library coexisting with new tokens — factory-design.md §When the existing surface is daisyUI / Bootstrap / Material
  • Querying inside JSONB at app speed — factory-data-layer.md §Custom attributes as JSONB
  • Raw SQL with hand-mapped row→object — factory-data-layer.md §ORM pick
  • Mixed migration-file naming — factory-data-layer.md §Migration file naming

Database migrations (destructive prod writes)

Section titled “Database migrations (destructive prod writes)”
  • Silently coercing historical data to fit a wrong constraint — factory-db-migration.md §The data is ground truth
  • Single-file migration mixing preflight + mutate + verify — factory-db-migration.md §Three-stage write contract
  • Untested rollback shipped to prod — factory-db-migration.md §Three-stage write contract
  • Idempotency claim asserted in a comment, not proven by re-run — factory-db-migration.md §Idempotency via natural keys
  • Natural key with bare = on nullable columns — factory-db-migration.md §Idempotency via natural keys
  • Two-branch CASE that conflates NULL with FALSE in tri-state UPDATE — factory-db-migration.md §Bidirectional update semantics
  • Constraint error with no row index — parser didn’t validate — factory-db-migration.md §Validate at parse, not at constraint
  • Prod destructive write with no Layer C snapshot taken — factory-db-migration.md §Layered backup independence
  • LLM auto-runs runbook commands “because they’re safe” — factory-db-migration.md §Human gate at every step
  • Constraint dropped without auditing downstream layers (server actions, RPC filters, dashboard rollups) — factory-db-migration.md §Defense in depth
  • Local-cycle-then-prod, skipping ephemeral staging — factory-db-migration.md §Migration testing protocol
  • Pre-built libs/py-libs/ before second consumer — factory-data-pipelines.md §Don't pre-build shared libs
  • Pydantic models copy-pasted across entry points — factory-data-pipelines.md §Three-entry-point pattern
  • Pydantic state for LangGraph — factory-llm-workflows.md §State shape
  • No versioning on editable content (chat vs claims) — factory-llm-workflows.md §Version anything editable later
  • In-memory rate limiter on serverless — factory-security.md §Rate limiting
  • PHI in email without runtime BAA check — factory-security.md §PHI in email/SMS
  • AI-generated code without a review queue — factory-security.md §AI-generated code — read-only by default
  • Migrations at runtime (in Cloud Run CMD) — factory-deployment.md §Migrations — CI, never runtime
  • Claude reviewer wired as advisory, not required — factory-ci.md §Claude Code reviewer is a required check, not an advisory bot
  • Required-checks list drifts from workflow jobs — factory-ci.md §Branch protection — short list, load-bearing
  • Pre-push hook treated as the merge gate — factory-ci.md §Pre-push hooks — fast feedback, not the gate
  • Verification theater (green checks approved without comprehension) — factory-verification.md §Guardrails at the boundary, never the inner loop
  • Silent verification gap (pitfall documented but enforced by nothing) — factory-verification.md §Guardrails at the boundary, never the inner loop
  • Gating a PR on the absolute score instead of the delta — factory-verification.md §Gate the delta, contextualize the absolute
  • A precise score (87.3/100) over a heuristic instrument — factory-verification.md §Score the codebase, disclose the instrument
  • Regenerated trace IDs at service hops — factory-observability.md §Trace ID — propagate, don't regenerate
  • Commits with no Linear linkage — factory-commits.md §Tie every commit to a Linear issue

These are kit-shape and project-shape failures that don’t fit any one skill’s domain. They live here.

Three competing solutions for the same problem

Section titled “Three competing solutions for the same problem”

Legacy applicationProgress.ts, intermediate sectionProgress.ts, and unified progress-calculator.ts all live in the same repo. The newer file is the source of truth but the older ones never got deleted.

Right move: when you write a unifier, delete the inputs in the same PR. Half-finished refactors are worse than untouched code — they imply the newer file is the truth while leaving the older ones as plausible alternatives that future contributors will pull from.

Decision-criteria choices (which auth, which UI lib, which API style) get relitigated each session. The kit’s factory-stack.md documents the criteria; the project’s DECISIONS.md records the picks.

Right move: every new project starts with a DECISIONS.md containing one-line entries per decision-criteria choice from factory-stack.md. Update on every architectural call.

Implies intent without value. New contributors interpret the empty directory as “this is where convention lives” and put unrelated things there.

Right move: delete empty stubs. If intent matters, write the placeholder explicitly with TODO: so future-you knows what was planned.

CLAUDE.md describing the architecture you wish you had, not the one the code actually has. New contributors get the wrong mental model. AI agents read it and propose work against a fiction.

Right move: treat CLAUDE.md as code. Update in the same PR as the refactor. If you write an AGENTS.md or CLAUDE.md, make it load-bearing or delete it.

Inconsistent CLAUDE.md formats across repos

Section titled “Inconsistent CLAUDE.md formats across repos”

Each new project relitigates the format. The kit’s template (see CLAUDE.md in the kit’s root) is the canonical shape — extend it per project, don’t reinvent.

Right move: copy the kit’s CLAUDE.md template into the project’s root, fill in the project-specific bits (domain, decisions, layout). Same shape every time.