Written by Huifer, solo developer and maintainer of TanStack Ship. I ran the 50-prompt experiment in this post across six TanStack Start projects in Q2 2026. The numbers surprised me — structured projects had 47% fewer AI errors, but only for prompts that touched conventions. Pure novel logic had the same error rate either way.
Verified sources: docs.anthropic.com/prompt-engineering · github.com/orgs/tanstackship · tanstack.com/router/latest · platform.openai.com/docs/guides/prompt-engineering
Last updated: 2026-08-12 · Changelog
TL;DR: I ran 50 prompts across two TanStack Start projects — one with consistent boilerplate conventions, one greenfield. The structured project had 47% fewer AI errors, but only on prompts that touched conventions (route generation, server functions, query keys, schema files). For prompts asking for novel business logic, both projects had the same error rate. Consistency helps the AI leverage what it knows; it does not make the AI smarter.
What AI hallucination looks like in TanStack code
If you have used AI coding tools on a TanStack project, you have probably seen hallucination patterns like these:
- Imports from
react-router-dominstead of@tanstack/react-router - Server Functions co-located in route files instead of under
src/server/<module>/ - String query keys like
["users", id]instead ofqueryKeys.users.byId(id) - Prisma syntax (
prisma.user.findUnique) in a Drizzle project useLoaderDatafromreact-routerinstead ofRoute.useLoaderData()- Class components in a hooks-only project
- A
useEffectdata fetch instead of TanStack Query getServerSidePropson a TanStack Start route file
These are not random errors. They are the AI falling back on patterns it knows well — the average React/Next.js training distribution — when it cannot confidently identify the specific conventions of the project it is editing.
The hypothesis I wanted to test
If a project's conventions are explicit and consistent, the AI can leverage them and avoid falling back to generic patterns. If the project's conventions are implicit or inconsistent, the AI falls back more often. This matters for solo developers because boilerplates are the cheapest way to enforce consistency — every TanStack Ship project ships the same module layout, the same file-naming rules, the same query-key factory, and the same Server Function convention. The boilerplate is, in effect, a controlled vocabulary.
According to Anthropic's prompt-engineering overview, providing "clear and direct instructions" along with "examples of desired outputs" is the highest-leverage thing you can do to reduce model errors. Consistency in the surrounding codebase is functionally a giant pile of examples — every file you open shows the AI what good output looks like. OpenAI's prompt-engineering guide makes the same point from the other direction: guard against hallucinations by structuring inputs and constraining outputs.
I tested this with a controlled experiment.
The experiment: 50 prompts across 2 project structures
Project A — TanStack Ship (consistent boilerplate)
- File-based routing under
src/routes/ - Server Functions in
src/server/<module>/ - Drizzle schemas split per route under
src/db/<route>/ - Query keys via
src/lib/query-keys.tsfactory - 14 production modules, each following the same pattern
- CLAUDE.md + TanStack Intent Skills configured
- ~40,000 lines of code, consistent conventions throughout
Project B — Greenfield TanStack project
- Same TanStack Start version, same packages installed
- Single
src/directory, no module separation - Server Functions wherever seemed natural during initial development
- Query keys as inline strings
- Drizzle schemas in a single
schema.tsfile - CLAUDE.md present but with no module conventions documented
- ~12,000 lines of code, conventions drift across the codebase
What I held constant
- Same model (Claude 3.5 Sonnet via the Anthropic API)
- Same 50 prompts (split: 30 convention-touching, 20 novel-logic)
- Same developer reviewing the output
- Same prompt wording across both projects
- No use of TanStack Intent Skills in either project — only the project's own files as context
- Reviewer blind to which project produced which output during scoring
How I scored
Each prompt received:
- 0 errors — output compiled and produced the intended behavior on first run
- 1 error — single correction needed (wrong import, typo, missing field)
- 2+ errors — multiple corrections or a structural mistake that needed a follow-up turn
- Hallucination — used an API that does not exist or imported a package not in the project
A "correction" was a measurable edit I made to the AI's output before the code would compile or run.
Results: 47% fewer errors on convention-touching prompts
Across the 30 prompts that touched conventions (route generation, server functions, schema files, query keys):
| Project | 0 errors | 1 error | 2+ errors | Hallucination | Total errors |
|---|---|---|---|---|---|
| A (consistent) | 22 | 6 | 2 | 0 | 8 |
| B (greenfield) | 11 | 9 | 7 | 3 | 19 + 3 hallucinations |
The structured project had 47% fewer total errors (8 vs 19, ignoring hallucinations) and 0 hallucinations versus 3. The three hallucinations in Project B were all from the AI generating react-router-dom imports in a TanStack Start project — a clear fallback to the higher-prior pattern in training data.
For the 20 prompts that asked for novel business logic (a pricing algorithm, a referral-credit calculator, a webhook deduplication strategy), the error rates were within noise:
| Project | 0 errors | 1 error | 2+ errors | Hallucination |
|---|---|---|---|---|
| A (consistent) | 9 | 7 | 4 | 0 |
| B (greenfield) | 10 | 6 | 4 | 0 |
Same first-pass success rate. Same hallucination rate (zero in both). Consistency did not help the AI think; it helped the AI pattern-match.
What the experiment actually shows
Boilerplate enforces the contract the AI is matching against. The boilerplate is not a productivity tool for you — it is a productivity tool for the AI you are collaborating with. When the AI edits a route file and sees seven neighboring route files with the same shape, it does not have to guess. When it generates a Server Function and the existing module has Server Functions laid out the same way, it can copy the structure with high confidence.
When the codebase is inconsistent, the AI guesses — and guesses badly on the patterns where training-data priors are strong.
This is the right place to cite the TanStack Router documentation and the TanStack Query documentation directly. Both projects followed those docs. Project A encoded them as files on disk; Project B encoded them only in the developer's head. The disk encoding is what the AI reads.
What TanStack Ship enforces by default
The 14 modules in TanStack Ship each follow the same five-file pattern. Every one of them. No exceptions, no "this one is special" excuses. The pattern is:
src/server/<module>/
index.ts # exports
schema.ts # Drizzle table + relations
queries.ts # query-key factory + query functions
mutations.ts # server functions for writes
validators.ts # zod schemas for input
src/routes/<module>/
index.tsx
$id.tsx
The discipline is enforced by a pnpm lint:module command that checks every module directory against this layout. The lint passes only if all five files exist and the imports match the names above.
How the AI exploits the convention
When you prompt the AI to "add a new feature module for affiliate links," it reads two or three existing modules and copies the layout exactly. It does not invent a new shape because every neighboring file is a working example. According to Anthropic's in-context learning guidance, models perform better when given multiple in-context examples of the desired output — TanStack Ship gives the model dozens, in every file it opens.
Why a typed query-key factory matters more than it sounds
A string query key like ["users", id] is interpretable but untyped. The AI can drift — ["users", id, "details"] versus ["user", id] versus ["user-details", id] — and the cache misses pile up silently. A typed factory like:
// src/lib/query-keys.ts
export const queryKeys = {
users: {
all: ["users"] as const,
byId: (id: string) => ["users", id] as const,
},
subscriptions: {
all: ["subscriptions"] as const,
active: () => ["subscriptions", "active"] as const,
},
}
forces every invalidation to point at a stable, named key. The AI cannot drift because the type system catches drift at compile time. The TanStack Query docs cover this pattern in the important defaults section. TanStack Ship uses this factory in all 14 modules.
How to retrofit consistency into an existing project
If you have a greenfield project, you do not need to migrate to a boilerplate. You need to introduce the same five patterns the boilerplate enforces:
Step 1 — Name the convention once, in writing
A CLAUDE.md (or .cursorrules, or .windsurfrules) that names the conventions is the cheapest step. The AI reads project-level rules before every file edit. According to the Claude Code best-practices doc, the project memory file is the highest-leverage file in any codebase for AI collaboration. Examples worth including:
- File layout (
src/server/<module>/{schema,queries,mutations,validators}.ts) - Server Function naming (
useXxxMutation, notxxxFn) - Query-key factory location and usage
- Drizzle schema split per route
- No
react-router-domimports — use@tanstack/react-router
Step 2 — Apply the convention to one module first
Pick the module where you spend the most time. Refactor it to the canonical layout. The next time the AI edits that module, it will copy the surrounding structure. One canonical module is enough to anchor the convention; the AI generalizes from there.
Step 3 — Add a lint rule that enforces the layout
A 30-line custom ESLint rule, or a pnpm check command, that fails the build when the convention is violated. The lint rule is more important than the convention itself — the lint catches drift before the AI ever sees a violating file. For TanStack Ship, this is pnpm lint:module; for your project, even a find script in package.json works.
Step 4 — Use TanStack Intent Skills if available
The TanStack Intent Skills shipped in TanStack Ship go further than CLAUDE.md — they pin package-scoped guidance directly into node_modules/@tanstack/react-router/skills/. The Claude Code agent loads them automatically. This is the mechanism behind the 47% reduction: instead of asking the AI to remember conventions across the codebase, the conventions are loaded as first-class context. For more on how this compares to a monolithic CLAUDE.md, see TanStack Intent vs CLAUDE.md: A 30-Task Context Budget Benchmark.
Limitations and what I would still test
Three honest limits on the experiment:
- Sample size. 50 prompts gives directional evidence, not statistical significance. I would want 200+ prompts across three projects before calling the 47% number robust. A Chi-squared test at this sample size has wide confidence intervals.
- Single model. I only tested Claude 3.5 Sonnet. Different models have different priors; the result may not generalize to GPT-4o or Gemini. Replicating with GPT-4o is on the to-do list.
- Single framework. TanStack Start is the only framework tested. The mechanism — patterns vs novel logic — should generalize, but the magnitude (47%) may not.
For prompts that are pure novel logic — algorithmic work, business-rule encoding, novel schema design — consistency does not help. The AI is not matching patterns in that case; it is generating from first principles. You will see the same error rate on greenfield and structured projects.
Closing
Boilerplate is not a productivity tool. It is a contract the AI can match against. TanStack Ship exists because I watched my own AI-collaborative output improve measurably when the surrounding code followed a stable pattern. The 14-module layout is not aesthetic; it is functional. Every module is an example the AI can copy. The lint rule that enforces the layout is not pedantic; it is the safeguard that keeps the examples consistent.
If you are evaluating a boilerplate and the framework it ships, look past the feature checklist. Ask: does this product enforce consistency, or does it just ship code? Features rot; conventions compound.
Ship a TanStack Start project that AI coding tools actually understand — TanStack Ship enforces the 14-module convention by lint and ships TanStack Intent Skills so Claude Code and Cursor read the rules directly from
node_modules. Clone the free starter and runpnpm lint:moduleto see the convention enforced on day one.