Most developers use an LLM as an autocomplete with a chat window: they throw in a function, get code back, paste it, and move on. That works until the project grows past a few files — and then the same model that wrote a clean React component starts confidently producing any in half the interfaces and a Node.js layer that nobody wants to touch.
The difference is not the model. It is the prompt. A vague request ("refactor this") gets you a vague diff. A prompt that defines the role, the constraints, the acceptance criteria and the output format gets you something you can actually merge.
Below are twelve prompts I keep in a file and reuse across real projects — architecture, typing, refactoring, performance, deploy. Each one has a working example. Treat them as templates: swap the stack, keep the structure.
Architecture: make the model ask before it answers
1. Project skeleton with explicit trade-offs
Instead of "design me an app structure", force the model to state assumptions first. This one prompt prevents the classic failure where the LLM picks Next.js for a tool that needs a long-running background worker.
You are a senior frontend architect. I'm building [describe product in 2 sentences].
Constraints: team of 3, TypeScript strict, must run on Node 20, no vendor lock-in.
Do NOT write code yet. Output:
1. Three architecture options (framework, state, data layer) with one-line trade-offs each.
2. The questions you need answered before choosing.
3. Your recommendation and what I pay for it.
Why it works: the model is forced into the "options + questions" mode, which surfaces decisions you'd otherwise discover three sprints in. The trade-off line is the part people skip — and it's the part that saves you.
2. Folder structure that survives growth
Given this feature list: [list]. Propose a folder structure for a TypeScript
React + Node monorepo. Rules: features are isolated, shared code lives in /packages,
no barrel files deeper than one level. Explain where a new developer will get lost
in 6 months and how the structure prevents it.
That last sentence is the whole trick. Asking where the structure breaks gets you a real answer instead of a tidy tree that collapses under its own abstractions.
Typing: turn any into a contract
3. Type-first design
Here is a raw API response [paste JSON]. Write TypeScript types for it using
strict mode. Rules: no `any`, no `unknown` at the boundary — use discriminated
unions for optional fields. Then list every place the API can return a shape
that would break the UI.
Failure mode to watch: models love to invent fields that look plausible. Diff the generated types against the real payload — not against your memory of it.
4. Generic utility with constraints
Write a TypeScript utility `groupBy<T, K extends keyof T>(items: T[], key: K)`
that returns a Record with correctly typed values. Add JSDoc, handle empty input,
and show three usage examples where the compiler catches a mistake.
If the model returns something that compiles but loses type information (a Record<string, T[]> instead of a keyed record), you'll see it in the usage examples. That's why you ask for them.
Refactoring: the diff, not the rewrite
5. Surgical refactor with a diff contract
Refactor the function below. Constraints:
- Same public signature, same behavior.
- Output ONLY a unified diff, no prose.
- If a change could alter behavior, stop and ask instead of guessing.
The trade-off: you lose the model's explanations, but you gain something you can apply with git apply and review in seconds. For a 40-line function this is worth it; for a 400-line module, ask for a plan first.
6. Dead code and dependency audit
Analyze this file. List: (1) exports nothing imports, (2) dependencies used once,
(3) logic duplicated elsewhere in the snippet. For each, say what breaks if I delete it.
That last clause is the difference between a cleanup and an outage. Models are happy to suggest deletion; they rarely think about dynamic imports and string-based lookups.
7. Test generation that respects your style
Write Vitest tests for this module. Match the style of the existing test file [paste].
Cover: happy path, empty input, and one case where the current implementation
is probably wrong. Flag that last one separately.
Asking the model to find a bug while writing tests is where it earns its keep. It won't always be right, but the flagged case is worth a look every time.
Performance and React
8. Render-cost diagnosis
Here is a React component tree [paste]. Identify unnecessary re-renders.
For each: the cause, the fix, and the cost of the fix (memo, state lift, context split).
Rank by impact. Do not suggest useMemo without naming the measured problem.
That last line blocks the most common bad advice: wrapping everything in useMemo because it "might help." Memoization has a real cost — comparison on every render — and a prompt that demands a named problem keeps the model honest.
9. Node.js bottleneck hunt
This Express handler is slow under load [paste]. Walk through it line by line.
Mark every synchronous operation on the hot path, every N+1 query pattern,
and every place where an await could be batched. Output a table: line, problem, fix.
A table forces specificity. Vague prose about "optimizing queries" is useless; "line 34: await inside a loop over 500 items" is a ticket.
Deploy and CI
10. CI pipeline from scratch
Write a GitHub Actions workflow for a TypeScript monorepo: lint, typecheck, test,
build, deploy preview on PRs, deploy prod on main. Cache dependencies.
Explain each step in one line. Flag anything that will fail on a fresh runner.
The "fresh runner" question catches the classic mistake: a pipeline that works locally because of a stray node_modules or a global binary.
11. Dockerfile review
Review this Dockerfile for a Node 20 app [paste]. Output: image size estimate,
layer-cache problems, security issues (root user, secrets in layers),
and a corrected version using multi-stage build.
Multi-stage builds are where LLMs genuinely shine — the pattern is mechanical and the model has seen thousands of them.
12. Deploy postmortem prompt
Deploy failed with this log [paste]. Give me: (1) the most likely cause,
(2) the exact command to verify it, (3) the fix, (4) how to prevent it next time.
Do not guess past the log — if the log is insufficient, say what to capture.
The instruction to stop at the log is what separates a useful answer from a confident hallucination. Models fill gaps by default; you have to close the gap explicitly.
What actually changes the output
Across all twelve, the same three levers matter more than wording:
- Role and constraints — "senior frontend architect", "Node 20", "strict mode" narrow the space of answers.
- Output format — diff, table, ranked list. Format is a thinking constraint, not decoration.
- A stop condition — "ask instead of guessing", "say what to capture". This is the single highest-leverage sentence you can add to any prompt.
If you want the same discipline applied elsewhere, the pattern transfers cleanly to code review prompts that catch bugs before merge and to building code with AI when you are not a programmer.
The prompts are not magic. They are the same questions a good tech lead asks in a code review — just written down so a model can answer them. Copy the file, adapt the constraints to your stack, and the next time an LLM hands you any in a strict codebase, you'll know which line of the prompt was missing.