Prompt library

Prompts worth stealing

The prompt is the interface now. These are the ones that actually work — copy, paste, swap in your details. Organised by what you're trying to do.

Planning

4 prompts
  • Break this into shippable, ordered steps

    You have a feature that's too big for one sitting and you want it staged so each step is independently reviewable and never leaves the app in a broken state.

    Break this feature into a sequenced build plan. Don't build anything yet.
    
    Feature: [DESCRIBE THE FEATURE]
    Stack: [e.g. Next.js App Router, Supabase, Drizzle ORM, Clerk, Tailwind]
    
    Rules for the plan:
    - Order steps by dependency — each builds on the last.
    - Every step must leave the app working and committable. No half-finished migrations that break the build.
    - Sequence: schema/DB changes and migrations first, then API/server logic, then UI, then polish.
    - For each step give me: what it does, which files it touches, and the exact manual check or test that proves it works.
    - Keep steps small. If a step touches more than ~5 files, split it.
    - Mark which steps are optional or reorderable so I can see the MVP cut.
    
    Output as a numbered checklist I can work one item at a time.
    • ·'Leave the app working after every step' is the load-bearing rule — it's what lets you stop, ship, or hand off at any point.
    • ·Feed the checklist back one item at a time ('do step 3 only'). Handing the agent the whole list invites it to sprint past your review.
    • ·For anything touching auth, payments, or data deletion, tell it to isolate that step so you can test it on its own.
  • Explore first, plan second, code never (yet)

    Starting any non-trivial feature or change in an existing codebase. Coding agents default to editing files on the first turn; this forces them to understand the system and get your sign-off before touching anything.

    Before writing any code, investigate the codebase and produce a written implementation plan.
    
    Goal: [WHAT YOU WANT TO BUILD OR CHANGE]
    
    Do this:
    1. Read the relevant parts of the codebase and explain how the current system works in the area we're touching. Cite the actual files and functions — don't describe it from memory.
    2. List every file you'd create or modify, with what each change is.
    3. Lay the work out as ordered steps.
    4. Call out risks and open questions: what could break, what's ambiguous, what you need me to decide.
    
    Constraints:
    - Do NOT write or edit any code this turn. Plan only.
    - If the request is unclear or underspecified, ask me instead of planning around a guess.
    - Match the patterns already in this codebase. Don't introduce [NEW LIBRARY OR PATTERN] unless you justify why it's worth it.
    
    When the plan is solid, write it to PLAN.md and stop. Wait for my go-ahead before building.
    • ·The 'do NOT edit code this turn' line is doing 80% of the work — keep it. If the agent still jumps to editing, the task is probably small enough to just let it run.
    • ·Having it write PLAN.md means you can edit the plan directly, then tell it 'follow PLAN.md, step 1 only.'
    • ·If it starts guessing instead of reading files, add: 'Cite a file:line for every claim about how the current code works.'
  • Give me the options, then make the call

    A fork-in-the-road decision — a library choice, a data model, an architecture — where you don't want the agent silently committing you to the first idea it had.

    I need to decide how to implement [THE THING]. Don't write code — help me choose.
    
    Context: [CONSTRAINTS: current stack, scale, timeline, and what matters most — speed to ship, cost, or long-term maintainability]
    
    Give me 2-3 real approaches. For each:
    - How it works, in a few sentences.
    - What it's good at and where it hurts.
    - Effort to build, and effort to maintain later.
    - What it locks me into.
    
    Then recommend ONE and say why, given my constraints. If the honest answer is the boring option, say so. If two are close, name the tie-breaker.
    
    Don't hedge with 'it depends' and leave it there — make the call and own it.
    • ·Give real constraints or you'll get a textbook comparison. 'Solo dev, shipping this week, low traffic' yields very different advice than 'team of 10 at scale.'
    • ·Library tradeoffs move fast — add 'flag anything that changed recently' so you're not deciding on stale info.
    • ·Once you pick, feed the choice straight into the 'Explore first, plan second' prompt to turn the decision into a real plan.
  • Poke holes in this before I build it

    You have a plan — yours or the agent's — and you want the failure modes and hidden assumptions surfaced before you sink hours into it.

    Here's the plan for [WHAT YOU'RE BUILDING]:
    
    [PASTE THE PLAN]
    
    Don't agree with me. Pressure-test it:
    - The 3-5 biggest risks or things most likely to go wrong.
    - What am I assuming that might not hold? Check the codebase to confirm instead of guessing.
    - The hardest or most uncertain part — the thing I should prove with a throwaway spike before committing to the whole build.
    - Where the edge cases bite: [empty states / auth / concurrency / rate limits / error handling / mobile] — whichever apply.
    - What's missing from the plan entirely.
    
    For each risk, tell me how bad it is and the cheapest way to find out whether it's real. Be blunt — I'd rather hear the problem now than at 2am.
    • ·Run this against the agent's OWN plan from a previous turn. Models will defend a plan if you ask 'is this good?' but find real holes when told to attack it.
    • ·If it surfaces a scary unknown, ask for a 20-line spike to prove the risky part works before you build the rest.
    • ·'Don't agree with me' is not optional — drop it and you get flattery instead of a review.

Scaffolding

4 prompts
  • Full CRUD resource in one shot

    When you need the boring-but-load-bearing plumbing for a new entity — schema, endpoints, types, a basic UI — and don't want to hand-write six near-identical files.

    Scaffold complete CRUD for a new resource: [RESOURCE, e.g. invoice].
    
    Fields:
    - [field]: [type + constraints, e.g. amount: integer cents, required, > 0]
    - [field]: [type, e.g. status: enum('draft','sent','paid'), default 'draft']
    - [field]: [...]
    (plus id, createdAt, updatedAt — add these automatically)
    
    Ownership/scoping: every row belongs to [SCOPE, e.g. the current org]. All queries must be scoped to it and reject cross-tenant access.
    
    Generate, matching this repo's existing patterns:
    1. Drizzle schema + a migration
    2. Typed create/read/update/delete server functions, with zod validation and the tenant scoping above
    3. List + detail + create/edit form UI, reusing existing components
    4. Optimistic UI on create/delete if that's how the rest of the app behaves, otherwise plain
    5. Two tests: the happy path, and one that asserts cross-tenant access is rejected
    
    Handle these now, not later: empty-list state, validation errors surfaced in the form, and a not-found state on the detail page.
    • ·Spell out enum values and constraints in the prompt — that's exactly the detail agents invent wrong when you leave it vague, and it's cheap to specify up front.
    • ·The 'reject cross-tenant access' line plus its test catches the single most common scaffolding security hole in multi-tenant apps.
    • ·Naming the edge cases you care about matters: empty / error / not-found states are the first thing a fast scaffold quietly skips.
  • New feature that matches the codebase

    Adding a feature to an existing project when you want it to look like the same person wrote it — not a bolt-on that fights your conventions.

    I want to add [FEATURE, e.g. team invitations] to this codebase.
    
    Before writing any code:
    1. Study how an existing comparable feature is built end to end. Use [REFERENCE FEATURE, e.g. the existing 'members' feature] as the model. Read its data layer, route handlers / server actions, validation, UI components, and tests. Summarize the pattern back to me in ~8 bullets.
    2. List every file you'll create or change and where it goes, so it mirrors that pattern exactly: naming, folder structure, error handling, and how data fetching / state is done.
    
    Then implement [FEATURE] as a full vertical slice:
    - Data: schema + migration
    - Server: route handler(s) / server actions, with input validation using [zod / whatever this repo already uses] and the same auth checks as the reference feature
    - Client: UI that reuses existing components ([e.g. our Button, Dialog, Form primitives]) instead of new one-offs
    - Tests: mirror the reference feature's test style
    
    Constraints: match existing conventions over your own preferences. Don't add a new library if one already in the repo does the job. Don't refactor unrelated code. Show me the file list and plan, then wait for my go-ahead before implementing.
    • ·Pointing at a reference feature is the highest-leverage line here — it turns 'guess my conventions' into 'copy this one that's already right.'
    • ·The forced pause after the plan saves you from watching an agent confidently build the wrong thing for ten minutes.
    • ·If it proposes a new dependency, treat that as a signal to check whether it genuinely needs one or just didn't look hard enough at what's already installed.
  • Wire in a third-party service

    Adding Stripe / Resend / any external API when you want the full boilerplate — client, env, webhook, and a way to prove it works — done in one clean pass.

    Set up [SERVICE, e.g. Stripe] in this project for [PURPOSE, e.g. one-time checkout payments].
    
    Do all of this:
    1. Install the official SDK and create a single shared client module (e.g. `lib/[service].ts`) that reads config from env. No SDK calls scattered across the codebase — everything routes through this module.
    2. Add every required key to `.env.example` with a comment on where to get it. Tell me exactly which values I paste in and where I find them in the [service] dashboard.
    3. Implement [THE CORE FLOW, e.g. a checkout session endpoint + success/cancel redirects].
    4. If this service uses webhooks: add a handler that verifies the signature, handles [EVENTS, e.g. checkout.session.completed], and is idempotent (safe to receive the same event twice). State how you achieve idempotency.
    5. Give me a concrete local test path — the exact CLI command (e.g. `stripe listen --forward-to ...`) or curl call — and what I should see when it works.
    
    Use test/sandbox mode. Handle failures explicitly: log and return a clean error, never swallow exceptions. Don't build UI beyond what's needed to trigger the flow.
    • ·Forcing one shared client module now prevents the sprawl where SDK calls leak into a dozen files and you can never swap, mock, or rate-limit the service later.
    • ·Idempotent webhook handling is not optional — providers retry, and a non-idempotent handler double-charges or double-sends. Make the agent explain how it guarantees it.
    • ·Demand the local test command in the same prompt. A webhook you can't exercise locally is a webhook you'll be debugging in production.
  • Zero-to-running full-stack app

    Day zero of a new project, when you want a real running skeleton — not a README full of TODOs — before you write a line of feature code.

    Scaffold a new [APP TYPE, e.g. B2B SaaS dashboard] called [PROJECT NAME].
    
    Stack (use exactly these, latest stable versions):
    - Next.js (App Router, TypeScript, `src/` dir)
    - Tailwind CSS
    - [Supabase / Neon] for Postgres, Drizzle ORM for schema and migrations
    - Clerk for auth
    - [Resend for email / Stripe for billing / none yet]
    
    Requirements:
    1. Initialize the project, install deps, and get `npm run dev` actually running before anything else. Commit that as the first commit.
    2. Create `.env.example` with every key the app needs, each documented with a comment. Never hardcode secrets.
    3. Build ONE real vertical slice to prove the wiring works: a protected `/dashboard` route that reads the signed-in user and lists rows from a `[RESOURCE, e.g. projects]` table. Include the Drizzle schema, a migration, and a seed script with 3 example rows.
    4. Set up ESLint + Prettier + a `typecheck` script, and make sure `npm run build` passes.
    
    Do NOT: add features I didn't ask for, generate lorem-ipsum marketing pages, or leave any file with a stubbed `// TODO: implement`. If a decision is ambiguous, pick the boringest sensible default and record it in `DECISIONS.md`.
    
    When done, print the exact commands I run to (a) start dev, (b) apply migrations, (c) seed the db.
    • ·Pin the stack explicitly. Leave it open and the agent drifts to whatever was trendy in its training data — naming the tools and versions kills a whole class of 'why did it pick that' surprises.
    • ·The single vertical slice is the entire point: it forces the auth -> db -> UI path to actually connect, which is exactly where scaffolds silently break.
    • ·Keep DECISIONS.md. Six weeks later it's the only record of why your project is shaped the way it is.

Debugging

4 prompts
  • Break out of the fix-fail-fix loop

    You (or the agent) have tried to fix the same bug three or more times, every attempt fails or breaks something new, and you're spiraling. Reset and get systematic before burning more context.

    Stop. We've tried to fix [BUG] several times and every attempt has failed or broken something else. Reset your thinking — the assumptions we've been running on are probably wrong.
    
    What we've tried and what happened:
    - [ATTEMPT 1 -> RESULT]
    - [ATTEMPT 2 -> RESULT]
    - [ATTEMPT 3 -> RESULT]
    
    New rules:
    1. Do NOT propose another fix yet. First, list every assumption we've been making about WHY this bug happens. One of them is almost certainly false — flag which you'd bet on.
    2. Instead of fixing, instrument. Add logging at each step of the failing path so we can SEE the real values and the actual control flow. Tell me exactly what to run and what to look for in the output.
    3. I'll run it and paste the output back. We diagnose from real data, not from theory.
    4. Only once the logs confirm the actual mechanism do you propose a fix — and explain why each previous attempt failed in light of what we now know.
    
    If two parts of the system disagree about state — cache vs database, client vs server, two sources of truth — call that out. That's the usual culprit in loops like this.
    • ·Pasting the failed attempts is what stops the agent from confidently re-proposing something you already tried an hour ago.
    • ·If the context is polluted from going in circles, start a fresh session and paste this in clean. A poisoned context is often why the loop began.
    • ·The instrumentation step is the unlock. Stubborn bugs survive because everyone keeps theorizing instead of printing the actual value at the actual moment.
  • It worked before — find what broke it

    A feature that used to work is now broken and you don't think you touched it. Use git history to pin the exact change instead of re-reading current code and guessing.

    [FEATURE/BEHAVIOR] used to work and is now broken. I didn't knowingly change it.
    
    Broken now: [WHAT'S BROKEN + how to observe it]
    Last time I know it worked: [DATE / COMMIT HASH / "before I added [X]"]
    
    Find the regression from history — don't theorize from the current code:
    1. Reproduce the current failure first so you know exactly what "broken" looks like.
    2. Run `git log` on the affected files within that time window and list the commits that touched this code path.
    3. Use `git bisect` (or check out a suspected earlier commit) to find the exact commit where it broke. Give me the hash and the commit message.
    4. Show me the specific hunk in that commit that caused it, and explain why that change breaks this behavior.
    5. Propose a fix that restores the old behavior WITHOUT reverting unrelated changes bundled in that same commit.
    
    If the breaking commit is actually correct and it merely exposed a latent bug elsewhere, tell me that instead of forcing the old behavior back.
    • ·Give it a real "last known good" anchor — a date or a commit. "It worked a while ago" makes bisect crawl; a tight window makes it seconds.
    • ·If you have a test or script that reproduces the bug, say so — `git bisect run [command]` fully automates the hunt.
    • ·Watch for the lazy full revert. Reverting the whole commit usually throws away good changes that shipped alongside the one bad line.
  • Root cause before you touch a line

    You can reproduce a bug and you want the agent to actually diagnose it instead of slapping on the first plausible patch and declaring victory.

    There's a bug I need fixed, but do NOT change any code until we agree on the root cause.
    
    Symptom: [WHAT GOES WRONG]
    How to reproduce: [EXACT STEPS / INPUT / URL]
    Expected: [WHAT SHOULD HAPPEN]
    Actual: [WHAT HAPPENS INSTEAD]
    Where to look (if you have a hunch): [FILES] or "not sure"
    
    Do this in order:
    1. Reproduce it yourself. Run the failing path — the test, a script, or a curl — and confirm you see the same failure I do. If you can't reproduce it, stop and tell me what you need.
    2. Trace the actual code path this input takes. Show me the real chain of files/functions involved, not a guess from skimming.
    3. Give me your single most likely root cause in 2-3 sentences, plus the exact line(s) responsible and the evidence that points there.
    4. Wait for my go-ahead. Then fix ONLY that root cause. No drive-by refactors, no "while I'm here" changes.
    
    If you catch yourself wanting to add a try/catch, a null check, or a retry just to make the symptom disappear — stop. That's a band-aid. I want the cause.
    • ·The "wait for my go-ahead" line is the whole point. It stops the agent from committing to its first hunch. Read the root cause, sanity-check it, then let it run.
    • ·If it genuinely can't reproduce, that's a finding, not a failure — usually the repro steps are wrong or the bug is environment-specific (env var, seed data, timezone).
    • ·Paste the actual error output, don't describe it. "It crashes" throws away the stack trace that names the file.
  • Trace this error to its origin

    You've got an error and a stack trace — from the terminal, the browser console, Vercel logs, or Sentry — and you want the real cause, not the line that happened to throw.

    Here's an error from [WHERE: local dev / Vercel logs / browser console / Sentry]:
    
    [PASTE THE FULL ERROR + STACK TRACE]
    
    This happens when: [WHAT THE USER OR SYSTEM WAS DOING]
    The input/request that triggered it: [PASTE IF YOU HAVE IT]
    
    Work backwards to the source:
    1. Read the trace top to bottom and tell me which frames are OUR code vs framework/library code. The throwing line is almost always a symptom; the bug is usually upstream in our code.
    2. Open the relevant files and figure out what value or state got execution into this branch. What was null / undefined / the wrong shape — and where did that bad value first enter the system?
    3. Tell me the root cause and the origin point, not just where it exploded.
    4. Propose the fix at that origin. If the correct fix genuinely IS at the throw site (e.g. real missing input validation at a boundary), say so and justify why.
    
    Do not wrap the throwing line in error handling just to make the trace go away.
    • ·For async or serverless errors the stack often stops dead at the framework boundary. Tell the agent the trace is truncated so it goes hunting instead of trusting where it ends.
    • ·A stack trace without the triggering input is half the picture — paste the request body, the query params, or the row of data that set it off.
    • ·In Sentry/Vercel, grab the error with the highest event count, not the scariest-looking one-off. You want the bug that's actually hurting you.

Refactoring

4 prompts
  • Kill the any's and make bad states impossible

    TypeScript that compiles but lies — `any` everywhere, optional fields that are always present, magic strings that should be unions, objects where half the field combinations are invalid. Endemic in AI-written code.

    The types in [FILE/MODULE] are too loose and don't reflect reality. Tighten them WITHOUT changing runtime behavior. Don't edit yet — start by listing the loosest/most dangerous types you see and your plan.
    
    Once I approve:
    - Replace every `any`, `as` cast, and `@ts-ignore` with a real type. If a value can genuinely be several shapes, model it as a discriminated union — not `any`.
    - Make illegal states unrepresentable: if two fields can't both be set, use a union; if a field is always present after [some point], stop marking it optional; if a string is really one of a fixed set, make it a union/enum.
    - Push validation to the boundary. Parse untrusted input (API responses, form data, env vars) with [Zod / the existing validator] and let the parsed type flow inward, so internal code stops re-checking.
    - Surface problems, don't bury them. If tightening reveals a real bug or an unhandled case, STOP and show me — do not paper over it with a cast or a `?.`.
    
    Constraints:
    - Types-only. No runtime behavior changes (except adding boundary validation, if I approve it).
    - Do NOT weaken tsconfig, add eslint-disable, or cast to make it pass.
    - Run `[TYPECHECK COMMAND]` and `[TEST COMMAND]` at the end and show me every place tightening surfaced a real problem.
    • ·The win isn't 'no more red squiggles' — it's the compiler catching a real bug the loose types were hiding. Make the agent call those out explicitly.
    • ·When it reaches for `as SomeType`, that's usually a lie: a cast asserts 'trust me,' a parse actually checks. Prefer a Zod parse at any boundary.
    • ·Watch for the agent 'fixing' types by making everything `?` or `| undefined` — that's loosening disguised as tightening. You want fewer possible states, not more.
  • Lock behavior first, then refactor

    You need to restructure code that has few or no tests and you can't afford to change what it does — the classic 'this file scares me, but it works.' Use this before any risky cleanup on load-bearing code.

    I want to refactor [FILE/MODULE/FUNCTION] but its behavior must stay exactly the same. Do NOT start refactoring yet.
    
    Step 1 — Pin the current behavior. Write characterization tests that capture what this code does RIGHT NOW, including edge cases and any ugly behavior (bugs included — we lock them for now, do NOT fix them). Cover at least: [KEY PATHS, e.g. happy path, empty input, null/undefined, the error branch]. Run them and confirm they pass against the CURRENT code.
    
    Step 2 — Show me the tests and wait for my OK before you touch any production code.
    
    Step 3 — Once I approve, refactor in small steps. After each step run the full suite (`[TEST COMMAND]`) and the typecheck (`[TYPECHECK COMMAND]`). If anything goes red, STOP and show me — do not edit the test to match the new behavior.
    
    Constraints:
    - Exported signatures / public API stay identical unless I say otherwise.
    - No behavior changes, no bug fixes, no new features, no dependency bumps.
    - One logical change per commit — keep the diff reviewable.
    
    Goal of the refactor: [e.g. extract pricing into pure functions / kill the nested callbacks / split this 600-line file].
    • ·If the agent says the code is 'untestable,' that's the actual smell — have it extract the pure logic into a function first, test that, then continue. Untestable usually means tangled, not impossible.
    • ·Locking a bug is intentional: characterization tests document reality, not the ideal. Fix bugs in a SEPARATE follow-up so the behavior-changing diff is tiny and obvious.
    • ·For UI you can't unit-test, swap the safety net for a Playwright flow or a Storybook/snapshot capture before you let it move anything.
  • One source of truth for [X]

    The same logic — a fetch wrapper, a currency formatter, a permission check, a Zod schema — exists in four slightly-different copies that have drifted. You want one canonical version without shipping a regression.

    There are multiple copies of [WHAT, e.g. the currency-formatting logic / the auth check / the API error handling] scattered across the codebase and they've drifted apart.
    
    Step 1 — Find them all. Search for every implementation and near-duplicate. List each location AND note how they differ. The differences matter — some are probably intentional.
    
    Step 2 — Show me the differences and propose ONE canonical implementation that covers the real cases. Where copies disagree, tell me which behavior is correct and which look like drift/bugs — do NOT silently pick one and move on.
    
    Step 3 — After I confirm the canonical version, put it in [LOCATION, e.g. lib/format.ts], replace every call site with it, and delete the dead copies.
    
    Constraints:
    - Preserve behavior at each call site, except where I explicitly flagged a difference as a bug to fix.
    - Don't over-abstract. If two things are only similar by accident, leave them separate — I'd rather have two honest functions than one with a `mode` flag and five branches.
    - Run `[TEST COMMAND]` and `[TYPECHECK COMMAND]` after. If nothing tested this before, add a couple of tests for the canonical version.
    • ·The 'how do they differ' step is the whole game. Blindly merging drifted copies is exactly how you ship a regression — the differences are often load-bearing.
    • ·Push back on the agent's urge to DRY everything. Ask: 'would these two ever need to change for different reasons?' If yes, keep them separate.
    • ·Good targets: validation schemas, API clients, formatting/parsing, permission checks. Bad targets: React components that share a shape but mean different things.
  • Split the god file without breaking every import

    A single file grew to 800+ lines doing five jobs (very common with AI-generated code). You want cohesive modules, not one blob — and you don't want to spend an afternoon fixing import paths.

    [FILE] has grown too big and does too many things. I want it split into focused modules. Don't edit anything yet.
    
    First, give me a plan:
    - List the distinct responsibilities currently living in this file.
    - Propose a file/folder structure — names plus what each file holds.
    - Flag anything that's genuinely coupled and should stay together.
    
    After I approve the plan, do the split with these rules:
    - Public API stays the same. Either keep [FILE] as a barrel that re-exports the pieces so existing imports don't break, OR update every import site consistently — tell me which you're doing and why.
    - MOVE code, don't rewrite it. This is cut-and-paste, not a redesign. No logic changes while moving.
    - Group by feature/responsibility, not by kind. No `utils.ts` junk drawer, no `helpers.ts` dumping ground.
    - Each new module should stand on its own and have one clear reason to exist.
    - When done: run `[BUILD/TYPECHECK COMMAND]` and `[TEST COMMAND]`, confirm green, then grep for now-dead exports and remove them.
    
    Show me the final file tree and the full diff.
    • ·Insist on the plan first. Let it start moving code immediately and you get an arbitrary split you'll have to redo.
    • ·'Move, don't rewrite' is the load-bearing line. Without it the agent quietly 'improves' logic mid-move and you lose the ability to trust the diff.
    • ·Circular imports are the usual casualty. If the typecheck complains after the split, tell it to pull the shared type/util down into a leaf module that both sides import.

Code review

4 prompts
  • Now tear apart the code you just wrote

    Run it immediately after an agent generates a feature. LLMs are confidently wrong — make it argue against itself before you trust a single line.

    You just wrote [FEATURE / FILE]. Switch roles: you're now a skeptical reviewer who assumes the author (you) cut corners. Assume there IS a bug — your job is to find it, not to reassure me.
    
    Hunt specifically for:
    - Happy-path bias: what inputs did the code silently assume? Empty array, null, 0, negative number, huge string, unicode, concurrent calls, the second time it runs.
    - Fabricated APIs: did you call any method, prop, config key, or package that you're not certain exists in the INSTALLED version of [LIBRARY]? List every one you're under 95% sure about so I can verify it before running.
    - Half-done edges: TODOs, unhandled promise rejections, error states with no UI, loading states that never resolve.
    - Unverified assumptions about my codebase: that a table, column, env var, or function you referenced actually exists.
    
    Then give me a checklist I can run to prove it works, including the one input most likely to break it. If you genuinely find nothing, tell me what you'd need to test to be sure — don't pretend it's flawless.
    • ·The fabricated-API check is the highest-value part — hallucinated method names and config keys are the number-one way generated code eats your afternoon.
    • ·Run it in a fresh message so it reviews the code, not its own reasoning about writing the code.
    • ·Treat the "prove it works" checklist as your real test plan, not decoration.
  • The 'is this safe to ship' security pass

    Before exposing anything to the internet — a new API route, an auth flow, a file upload, a form that writes to your database — especially code you vibe-coded and don't fully trust.

    You are a penetration tester reviewing [FILE / ROUTE / FEATURE] before it goes live. Assume the client is hostile and every input is attacker-controlled. Walk the actual code path from request to response — don't pattern-match on keywords.
    
    Check specifically:
    - AuthN/AuthZ: can an unauthenticated user reach this? Can user A read or mutate user B's rows? Are we checking ownership on every query, or trusting an ID from the request body? [If Supabase: is there an RLS policy on every table this touches, or is the service-role key bypassing enforcement?]
    - Injection: raw SQL string interpolation, unsanitized input passed to a shell or eval, [Drizzle/Prisma] .raw()/$queryRawUnsafe calls.
    - Secrets: any key, token, or connection string that could reach the client bundle or logs. Any server-only module imported into a "use client" component.
    - Input validation: is the request body parsed through a schema (Zod) before use, or trusted as-is?
    - Data exposure: does the response return fields the user shouldn't see — password hashes, other users' rows, internal IDs?
    
    For each issue: the exact request an attacker would send to exploit it, and the fix. Rank by exploitability. Tie every finding to a specific line — no generic OWASP lecture.
    • ·"Trace request to response" forces it to follow real data flow instead of grepping for scary function names.
    • ·For Next.js, explicitly ask it to flag any server-only import inside a "use client" file — that's the classic way an API key ends up in the browser bundle.
    • ·This complements tooling, it doesn't replace it. Keep running your dependency and secret scanners.
  • The database and migration review

    Before running a schema migration or merging any change that touches queries — where a mistake means slow pages, locked tables, or lost rows.

    Review the database changes in this diff — [MIGRATION FILES / schema changes / new queries]. Production has real data and real traffic; assume this runs against a table with [~ROW COUNT] rows.
    
    Check:
    - Data loss / irreversibility: any DROP, a type change that narrows, a NOT NULL added to a column that has existing nulls, or a rename that a still-running old deploy would break. Is there a safe rollback?
    - Locking: will this take a lock that blocks reads or writes on a large table — adding an index without CONCURRENTLY, or a full table rewrite?
    - N+1: any query inside a loop or a .map that should be a single join or batched query.
    - Missing indexes: new WHERE, ORDER BY, or foreign-key columns with no supporting index.
    - Constraints: should there be a unique constraint, FK, or check here that the app is currently trusting itself to enforce?
    
    For each issue: the production symptom (which page gets slow, what breaks mid-deploy) and the safer approach. Call out anything that must be a two-step expand/contract deploy rather than one migration.
    • ·Give it the real row count. "This table has 4 million rows" turns "looks fine" into "this locks the table for 30 seconds" — the agent can't guess your scale.
    • ·Explicitly ask which changes need expand/contract. A single destructive migration is the textbook vibe-coding prod incident.
    • ·Pair it with an actual EXPLAIN on your slowest query — the agent reasons about the plan, the database tells you the truth.
  • The pre-merge diff review

    Your default gate before merging any PR or committing a chunk of AI-generated work. The workhorse you run on everything.

    Review the diff between [BASE_BRANCH] and my current branch as a senior engineer who has to maintain this code after I'm gone. Do not summarize what the code does — I know what it does. Find what's wrong with it.
    
    Output findings as a table ranked by severity: BLOCKER (data loss, security hole, breaks prod), HIGH (bug under realistic input, race condition, missing error handling), MEDIUM (footgun, unclear ownership, will confuse the next person), LOW (naming, style). For each finding give: file:line, the concrete failure scenario — the specific inputs or state that trigger it, not "this could be bad" — and the fix.
    
    Rules:
    - If a function's error path is never handled or tested, that's at least HIGH.
    - Flag anything that silently swallows errors or catches exceptions without logging or rethrowing.
    - Flag any new [ENV VARS / config / secrets] that aren't documented or validated at startup.
    - Do not invent problems to fill the table. If a severity tier is empty, say so.
    - End with the single most important thing to fix before merge.
    • ·Run it against the actual git diff, not the whole repo — narrow scope means the agent reads every line instead of skimming.
    • ·The "concrete failure scenario" rule is what separates a real review from a wall of "consider adding validation." If it can't name inputs that break the code, it isn't a real bug.
    • ·Paste the output back and tell it to fix only BLOCKER and HIGH. Leave the bikeshedding for a quiet afternoon.

Tests

4 prompts
  • Find the tests that pass but protect nothing

    You have a big green suite you don't fully trust — a lot of it was AI-generated and you suspect chunks of it assert nothing real. Run this before you let that green checkmark gate a deploy.

    Audit the tests in [DIR / FILE] for FALSE CONFIDENCE — tests that pass but wouldn't catch a real bug. Do NOT rewrite anything yet. Give me a report first.
    
    Flag each of these anti-patterns with file:line:
    - Tests that mock the very thing they claim to test (mock returns X, test asserts X).
    - Assertion-free or trivial tests (no expect, `expect(true).toBe(true)`, "doesn't throw" only, or a snapshot with no meaningful content).
    - Tests coupled to implementation details — asserting internal calls or private state instead of observable behavior.
    - Over-mocking that stubs out all the real logic, so the test literally cannot fail.
    - Tests that don't actually run: [.skip / .only / xit / commented-out], or whose assertions are unreachable.
    
    To PROVE a test is weak, mutate the code it covers — introduce a deliberate bug (flip a condition, return the wrong value, drop a call) and show me the test still passes. List each mutation you tried and which tests failed to catch it.
    
    Output: a ranked list of the weakest tests, one line on why each is weak, and a one-line fix for each. Then stop — I'll tell you which to rewrite.
    • ·The mutation trick is the real test of a test: if you can break the code and the suite stays green, the test is decorative.
    • ·Grep for `.only`, `.skip`, `xit`, and `xdescribe` first — those are free findings the audit surfaces in seconds.
    • ·Run this right after a burst of agent-generated tests, while the code is fresh — that's exactly when the fake-green problem creeps in.
  • Force real coverage, not one happy-path test

    The agent just wrote (or is about to write) a function or endpoint and you want tests that actually exercise the nasty inputs — because left alone, agents write one green happy-path test and declare victory.

    Write tests for [FUNCTION / ENDPOINT / MODULE]. Before writing ANY test, produce a table of the cases you plan to cover, grouped as: happy path, edge cases, error/failure paths, and boundary values. I want to see [null/undefined, empty string/array, zero, negative, very large, malformed input, unauthorized, duplicate, concurrent request] each considered explicitly — mark any that don't apply and say why.
    
    Then:
    - Wait for me to approve the table, then write the tests in [Vitest / Jest / pytest].
    - One behavior per test, descriptive names ("returns 401 when the token is expired"), Arrange-Act-Assert.
    - Assert on actual outputs, error messages, and status codes — never just "did not throw".
    - No over-mocking: mock ONLY [the network / DB / clock / randomness]. Use the real code for everything else.
    - Run them, paste the output, and report line + branch coverage for the target file specifically.
    
    Call out any case that was hard to test — that usually means the code is missing a seam.
    • ·Making the agent print the case table first is the whole trick — you catch the missing edge cases before any code exists to argue with.
    • ·'No over-mocking' is load-bearing: a test that mocks the function it's testing proves nothing. Restrict mocks to the network, the clock, and randomness.
    • ·Ask for coverage on that one file, not the repo — a gap hides easily inside an 80% project-wide average.
  • Kill the flaky test — root cause, no band-aids

    A test passes sometimes and fails others (usually only in CI) and you're tempted to just hit re-run. Don't. Use this to force a real diagnosis instead of a sleep() and a shrug.

    The test [TEST NAME / FILE] is flaky — it passes locally but fails intermittently [in CI / on rerun]. Here's a failing run: [PASTE ERROR + STACK TRACE].
    
    Diagnose the ROOT CAUSE before proposing any fix. Banned 'fixes': adding sleeps, bumping timeouts, adding retries, or skipping/quarantining it — unless you can prove the flakiness is genuinely external and outside our control.
    
    Steps:
    - Reproduce it: run the test in a loop [e.g. `vitest run --repeat 30` or a shell loop] until it fails, and capture the failing output. Don't touch anything until you've seen it fail.
    - Investigate the usual suspects: shared/leaked state between tests, test-order dependence, real timers/dates, timezone, unawaited promises / race conditions, network or DB not isolated, random data, non-deterministic ordering (Set/Map/object key order), and parallel workers.
    - Tell me which one it is and how you PROVED it.
    - Fix the actual cause (isolate state, fake the clock with [vi.useFakeTimers / Sinon], await properly, seed the randomness, make assertions order-independent).
    - Prove the fix: run the loop again [50+ times] with zero failures and paste the result.
    • ·Reproducing the failure in a loop before changing anything is non-negotiable — a fix for a failure you never reproduced is a guess wearing a lab coat.
    • ·Test-order dependence is the #1 hidden cause: run the suite in random order and again with a single worker to isolate whether state is leaking between tests.
  • Safety net before you let the agent refactor

    You (or the agent) are about to refactor, rename, or move a chunk of untested code and you want a net that screams the instant behavior changes — pinned down BEFORE a single line moves.

    I'm about to refactor [FILE / MODULE / FUNCTION] and there are no tests protecting it. Before we change anything, write characterization tests that pin down its CURRENT behavior — including any quirks or bugs. We are NOT fixing anything right now; we are freezing behavior so the refactor is safe.
    
    Rules:
    - Read the code first and list every observable behavior, branch, and edge case you can see (empty input, null/undefined, error paths, boundary values, side effects). Show me that list before writing a single test.
    - Write the tests in [Vitest / Jest / pytest] asserting on real return values, outputs, and side effects. Do NOT mock the thing under test.
    - If a behavior looks like a bug, DO NOT fix it. Add a `// TODO: looks like a bug, pinned as-is` comment and assert the current (wrong) behavior.
    - Run the tests against the UNCHANGED code and confirm they pass. Paste the run output.
    - Do not touch the production code in this step.
    
    When green, tell me which behaviors you couldn't reach and why.
    • ·Commit the passing tests first, then start the refactor in a separate commit — the whole value is a clean diff where the tests never changed while the code did.
    • ·If the agent starts 'improving' the code mid-task, stop it. Pinning tests are worthless the moment the behavior shifts underneath them.
    • ·Keep these tests after the refactor lands — they're now a permanent regression net for a module that had zero coverage yesterday.

Rules & context

4 prompts
  • Read before you write (stop the agent inventing patterns)

    Before any non-trivial change in an unfamiliar part of the codebase. Kills the number-one failure mode: the agent writing plausible, tutorial-flavored code that ignores the patterns you already have.

    Do NOT write any code yet.
    
    I want to [describe the change]. Before you touch anything:
    
    1. Find and read the files most relevant to this change. List them with a one-line note on what each does.
    2. Find the closest EXISTING example of something similar already in this codebase — [a comparable API route / component / migration / test]. I want the new code to match that, not a generic template.
    3. Tell me the patterns you'll follow: file location, naming, how errors/loading/auth are handled here, and which existing [helpers / hooks / utils] you'll reuse instead of writing new ones.
    4. Call out anything ambiguous or any decision that could reasonably go two ways, and ask me before you pick.
    
    Then STOP and show me the plan. I'll say go before you write a line of code.
    • ·The 'find the closest existing example' step is the whole trick — it forces the agent to copy your real patterns instead of a framework tutorial's.
    • ·If the plan reuses a helper that doesn't actually fit, correct it now. Fixing the plan costs one sentence; fixing shipped code costs a full review.
    • ·Works even better mid-session when context is polluted — it snaps the agent back to what's really in the repo.
  • Rules for this session (constrain the blast radius)

    When you're giving the agent some autonomy but there are hard boundaries — don't add dependencies, don't touch the schema, don't reformat half the repo. Paste it at the very top of the session.

    These are hard rules for everything you do in this session. If a task seems to require breaking one, STOP and ask — do not quietly work around it.
    
    Scope:
    - Only modify files under [src/features/checkout]. Do not touch [anything in src/lib or the db schema].
    - No new dependencies. Use what's already in package.json. If you genuinely think we need one, propose it and wait.
    
    Stack & patterns:
    - [Next.js App Router] only — no [pages/] code.
    - Data access goes through [the existing Drizzle queries in src/db/queries], never raw SQL in components.
    - [Server Components by default; add 'use client' only when you need interactivity, and say why.]
    
    Quality bar:
    - Every change must pass [pnpm typecheck && pnpm lint]. Run them yourself before telling me you're done.
    - Match the surrounding code style. Do not reformat files you aren't otherwise changing.
    - No [console.log] left behind, no commented-out code, no TODO without a reason.
    
    Never:
    - Edit [.env, migrations, generated files, CI config].
    - Delete or skip tests to make a suite pass.
    - Push or open a PR unless I explicitly ask.
    
    Confirm you understand these before starting.
    • ·'Never delete tests to make them pass' and 'no new deps without asking' are the two rules that save the most grief — keep them even if you trim everything else.
    • ·Agents weight early instructions heavily but drift over long threads. If the session runs long, re-paste this instead of assuming it still holds.
    • ·Make the scope line real. 'Only touch src/features/checkout' turns a scary autonomous run into one you can review in five minutes.
  • Turn this mistake into a rule

    The moment right after the agent does something wrong that you've just corrected. This is the highest-value time to capture a rule — it builds your rules file organically from real friction instead of imagined problems.

    You just [did X — e.g. used fetch directly in a Server Component instead of the existing apiClient / wrote a new date-format helper when formatDate already exists in src/lib/date]. That's a recurring class of mistake, not a one-off.
    
    Do two things:
    1. Add a short, specific rule to [CLAUDE.md / AGENTS.md] under [Conventions / Gotchas] that would have prevented this. Write it as an imperative with the reason in a clause — e.g. 'Use apiClient from src/lib/api for all fetches; it handles auth headers and retries.' Not a vague principle like 'be consistent with data fetching.'
    2. Grep the codebase for other places this same mistake already exists and list them, so I can decide whether to fix them now.
    
    Keep the new rule to one or two lines. Do not rewrite the rest of the file.
    • ·Do it in the moment. The best rules come from real corrections you just made, not from a brainstorm of what might theoretically go wrong.
    • ·Point every rule at a concrete symbol — a function, a file path, a command. 'Use formatDate from src/lib/date' is enforceable; 'format dates consistently' is not.
    • ·Every few weeks, reread the accumulated rules and delete the ones that no longer apply. A rules file is a garden, not a landfill.
  • Write my CLAUDE.md from what the repo actually does

    Starting work in an existing codebase, or when the agent keeps guessing your conventions wrong. This is the single highest-leverage rules file you can set up — do it before anything else.

    Explore this repository and write a [CLAUDE.md / AGENTS.md / .cursorrules] file at the root that captures how this project ACTUALLY works — not how it should work in theory.
    
    Before writing anything, inspect:
    - package.json / [pyproject.toml / go.mod] for the real dependencies and scripts
    - The folder structure and where [components / API routes / db queries / tests] actually live
    - 2-3 representative files in each major area to learn the real patterns: naming, imports, error handling, file layout
    - Config files: [tsconfig, eslint, prettier, drizzle.config.ts, tailwind.config]
    - How the app is run, built, tested, and deployed
    
    Then write the file with these sections:
    1. Stack — exact packages and versions that matter
    2. Commands — the real dev/build/test/lint/typecheck commands, copy-pasteable
    3. Conventions — patterns you actually observed, one concrete example each
    4. Directory map — where things go
    5. Do-not-touch — [generated files, migrations, vendored code, .env]
    6. Gotchas — anything non-obvious you had to figure out to understand the repo
    
    Rules for the file itself: be concrete and terse. No aspirational 'best practices' I didn't ask for. If you're unsure whether something is a real convention or a one-off, say so instead of stating it as law. Keep it under [200] lines — a rules file nobody reads is worthless.
    • ·Read the output critically once. The agent will occasionally promote a single one-off into a 'convention' — delete anything that isn't actually true across the codebase.
    • ·Regenerate the Conventions section after any big refactor. A stale rule is worse than no rule because the agent trusts it.
    • ·Keep it short. Past ~200 lines agents start skimming the middle and your most important rules get ignored.

Explain & learn

4 prompts
  • Catch what I got wrong (I explain, you grade)

    You think you understand how something works. The fastest way to find out you don't: explain it out loud and have your agent check it against the real code before you rely on it.

    I'm going to explain how [SYSTEM / FEATURE / FLOW] works in my own words. Grade it against the actual code — don't be nice.
    
    My understanding:
    [WRITE OUT YOUR MENTAL MODEL — 3-10 sentences, be specific, guess where you're unsure]
    
    Now:
    - Check every claim against the real code. Mark each Correct, Wrong, or Oversimplified.
    - For anything wrong, show me the code that proves it and what's actually true.
    - Tell me what my model is missing entirely — the parts I didn't mention because I don't know they exist.
    - Don't soften it. A wrong mental model I'm confident about is more dangerous than admitting I don't know.
    • ·Deliberately include a claim you're only 60% sure about. That's where the gold is.
    • ·Do this before touching auth, payments, or anything with security or money on the line — confidently wrong there is expensive.
    • ·After the corrections, explain it again cleaned up and have it confirm you've actually got it now.
  • Explain this like I have to own it Monday

    You're staring at a function, file, or PR you don't fully get, and you're about to modify or debug it — so you need real comprehension, not a vague summary.

    Explain [FILE / FUNCTION / PR / DIFF] to me at the level where I could confidently change it and debug it at 2am.
    
    Don't dumb it down, and don't just narrate what each line does. I want:
    - The job: what this is responsible for in one sentence, and what breaks if it's wrong.
    - The why: why it's written this way instead of the obvious simpler way — what problem forced this shape.
    - Inputs, outputs, and side effects, including the ones NOT visible in the signature (DB writes, network calls, mutated globals, thrown errors).
    - The assumptions and edge cases it's quietly relying on.
    - The 2-3 places most likely to bite me if I change it.
    
    If a part is genuinely just boilerplate, say so and move on. End with the single most important thing to keep in mind.
    • ·Paste the exact code or give the file path — don't let it summarize from memory.
    • ·Run it on a teammate's PR before you approve: 'explain this PR' catches what a skim won't.
    • ·If the 'why' comes back hand-wavy, push: 'check git blame for why this changed.' The real reason usually lives in an old commit.
  • Map the codebase before I touch it

    You just cloned an unfamiliar repo — an open-source project, a client codebase, a teammate's app — and you need the lay of the land before you change anything.

    You are my guide to this codebase. Do NOT change any code — I need to understand it first.
    
    What the app does: [ONE-LINE DESCRIPTION]
    What I'm about to work on: [THE TASK OR AREA]
    
    Do this, using real file and symbol names from the repo:
    1. Find the actual entry points — where execution really starts (Next.js routes, API handlers, background jobs, cron, webhooks) — and list them.
    2. Describe the architecture in words: the main modules, how a request or piece of data flows start to finish, and where state lives (DB, cache, external APIs).
    3. Name the 5-8 files I'll spend most of my time in for my task, one sentence each on what they own.
    4. Flag the non-obvious stuff: conventions this repo follows, anything clever or cursed, and where the footguns are.
    5. Tell me what to read first, second, third.
    
    If something is ambiguous, say so instead of guessing.
    • ·Point it at the real task ('I need to add team invites'). A scoped tour beats a generic one every time.
    • ·Once you know where to look, follow up with 'now walk me through [ONE FLOW] line by line.'
    • ·If there are tests, ask which test best documents the feature you care about — tests lie less than READMEs.
  • Teach me the concept, using my own code as the textbook

    You keep copy-pasting something — Server Components, DB transactions, JWT auth, Drizzle relations, useEffect deps — without actually understanding it, and you want it to stop being magic.

    I keep using [CONCEPT / PATTERN / API] without really understanding it. Teach it to me.
    
    Rules:
    - Use MY codebase as the running example — point at [FILE OR FEATURE where I already use it] and explain what's actually happening there.
    - Start from the problem this exists to solve. Once I understand the problem, the solution stops being magic.
    - Show me the naive/wrong version and why it fails, then the correct version. Contrast teaches faster than description.
    - Call out the specific misconception beginners have about this — I probably have it.
    - Cover the 20% that handles 80% of real use. Skip the trivia.
    
    At the end, give me one small exercise: a change I could make to my own code to prove I understood it.
    • ·Name the exact place you already use it so the lesson stays concrete instead of abstract.
    • ·When it clicks, ask 'where else in this repo should I be using this and I'm not?' — that's where the learning actually lands.

Performance

4 prompts
  • Add caching without serving stale data

    A route recomputes the same expensive thing on every request even though the underlying data barely changes. Caching is the fix — but invalidation is where agents quietly introduce stale-data and data-leak bugs, so gate it.

    [ENDPOINT / QUERY / COMPUTATION, e.g. the /api/stats route, getFeaturedProducts()] is expensive and runs on every request even though the underlying data rarely changes. Add caching.
    
    Stack: [e.g. Next.js App Router on Vercel + Supabase]. Cache layer available: [e.g. Next.js unstable_cache / the fetch cache / Upstash Redis / React cache()].
    
    Before writing any code, answer these and wait for my confirmation:
    1. What exactly are we caching, and what is the cache KEY — which inputs make the result different (user id, org id, locale, filters)?
    2. How stale is acceptable? [e.g. 60s is fine / must be fresh immediately after a write].
    3. What events must INVALIDATE it? List every write path that changes the underlying data.
    
    Then implement:
    - Cache with the key and TTL we agreed on.
    - Wire invalidation into every write path from step 3 (revalidateTag / revalidatePath / explicit delete). Miss one and we serve stale data, so enumerate them all.
    - Include anything user-specific in the key. NEVER cache one user's private data under a key another user can hit.
    
    Show me: what's cached, the key, the TTL, and the full list of invalidation points. Add a comment above the cache saying exactly what invalidates it.
    • ·The security line is not optional. The classic caching bug is serving User A's dashboard to User B because the key didn't include the user id.
    • ·Force it to enumerate every write path before you approve. A cache that's never invalidated is just a stale-data bug on a timer.
    • ·Start with the shortest TTL you can tolerate. You can always lengthen it later; aggressive caching plus one missed invalidation is a support nightmare to debug.
  • Hunt down N+1 queries and missing indexes

    Your app was snappy with 10 rows and crawls with 10,000. Nine times out of ten it's the database. Point this at the endpoint that degrades as data grows.

    [ENDPOINT / PAGE, e.g. GET /api/orders, the team dashboard] gets dramatically slower as data grows. I think it's the database. Find and fix it.
    
    Stack: [e.g. Next.js + Drizzle ORM + Supabase Postgres].
    
    1. Trace every DB query this path fires. Log the actual SQL (Drizzle: enable the query logger; Prisma: log: ['query']). Count them. If one logical request fires N queries in a loop, that's an N+1 — flag it explicitly.
    2. For each N+1: rewrite to a single query using a JOIN or a where...in(...) batch / relational query. Report the query count before and after.
    3. For each remaining slow query, run EXPLAIN ANALYZE against [DATABASE]. Any Seq Scan on a large table over a column we filter, join, or sort on is a missing index. Give me the exact CREATE INDEX statement and say which query it speeds up.
    4. Check we're not selecting columns we never use (especially large text/JSON blobs) and not fetching rows we then filter in application code.
    
    Deliver: the migration with the new indexes, the rewritten queries, and the before/after query count + timing. Don't add indexes speculatively — only ones backed by an EXPLAIN result.
    • ·'Don't add indexes speculatively' matters — agents love to index every column, which slows writes and bloats the table for no gain.
    • ·On Supabase you can run EXPLAIN ANALYZE straight in the SQL editor; paste the output back in if the agent can't reach the DB itself.
    • ·Seed 10k+ realistic rows before you measure, or the N+1 stays invisible and everything looks fine.
  • Profile first, then optimize (no guessing)

    Something feels slow — a page, an API route, a background job — and you're tempted to start 'optimizing' random things. Run this before you touch a single line so you fix the thing that actually costs time.

    [FEATURE / ROUTE / JOB] is slow: [DESCRIBE THE SYMPTOM, e.g. "/dashboard takes ~4s to first byte", "the CSV export job takes 90s"].
    
    Do NOT optimize anything yet. First measure and find the real bottleneck:
    
    1. Reproduce it and get a hard number. Use [PROFILING TOOL, e.g. Chrome DevTools Performance tab / the Next.js build + trace output / console.time / the DB query log / Vercel traces]. Break the total time down by phase: server compute, DB time, network waterfall, render, hydration.
    2. Identify the single biggest contributor to that number. Show me the evidence — the slow query, the waterfall screenshot, the flame-graph observation — not a guess.
    3. List the top 3 suspected causes ranked by cost, with a rough millisecond estimate for each.
    4. Only then propose a fix for the #1 item. State the expected improvement and how we'll verify it with the same measurement.
    
    Constraints: change one thing at a time, re-measure after each change, and stop if a change doesn't move the number. Don't refactor unrelated code. Don't add a caching layer or pull in a new library until we've proven where the time actually goes.
    • ·The 'show me the evidence' line is the whole point. It stops the agent from confidently rewriting code that runs in 2ms while the real cost sits in one query.
    • ·Paste the real before/after numbers back in each round so the agent has a target to beat, not a vibe.
    • ·If it can't reproduce the slowness locally, have it add timing logs to the real deployed path first — local and prod bottlenecks are often different.
  • Shrink the bundle and fix Core Web Vitals

    Lighthouse is red, the page takes forever to become interactive, or your JS bundle quietly ballooned after a few 'quick' feature adds. This is frontend load performance.

    [PAGE / ROUTE, e.g. the marketing homepage, /app/dashboard] loads slowly and/or scores badly on Lighthouse. Targets: [e.g. LCP under 2.5s, Total Blocking Time under 200ms, initial client JS under 200KB gzipped].
    
    Stack: [e.g. Next.js App Router + Tailwind CSS].
    
    1. Measure first, on a PRODUCTION build (next build && next start), never dev. Report current LCP, CLS, TBT, and the largest JS chunks by size using [@next/bundle-analyzer / the build output].
    2. Find the heaviest offenders:
       - Big dependencies in the client bundle (date libs, full icon sets, charting, markdown parsers). Can they be tree-shaken, swapped for something lighter, or moved server-side?
       - Client Components that don't need to be — 'use client' that could be a Server Component.
       - Below-the-fold or interaction-only code that could be dynamically imported / lazy loaded.
    3. Images: using next/image with correct sizes and modern formats? Fix any raw <img> on the critical path.
    4. Fonts: self-hosted via next/font, or a render-blocking external request?
    
    Fix the top offenders one at a time and re-measure after each. Report before/after bundle size and Lighthouse numbers. Don't trade away functionality for score — flag any tradeoff and let me decide.
    • ·Always Lighthouse a production build. Dev-mode numbers are meaningless and will send the agent chasing bottlenecks that don't exist in prod.
    • ·The biggest single win is usually one fat client-side dependency. Ask the agent to name the top 3 chunks by KB before it changes anything.
    • ·Put a real number in the target placeholder. 'Make it faster' gives you nothing to verify against; 'LCP under 2.5s' does.

Migrations & upgrades

4 prompts
  • Incremental file-by-file migration of a large surface

    A big internal migration touching dozens or hundreds of files — Next.js Pages Router to App Router, JavaScript to TypeScript, class components to hooks, moment to date-fns — where one giant PR would be a disaster.

    I'm migrating this project from [OLD PATTERN] to [NEW PATTERN] (e.g. Pages Router to App Router / .js to .ts / class components to function components + hooks). This touches many files, so we do it incrementally, not all at once.
    
    1. Establish the target pattern: pick ONE representative [file/route/component], migrate it fully and correctly, and show me the before/after. This is our reference — I approve the pattern before we scale it.
    
    2. Produce an ordered worklist of every remaining file, grouped so each group is independently shippable and low-risk. Do leaf / low-dependency files first. Keep old and new coexisting where the framework allows (App Router and Pages Router run side by side; TS and JS coexist with `allowJs`).
    
    3. Migrate one group at a time, following the approved reference exactly. After each group: run [TYPECHECK/TEST/BUILD COMMAND], confirm green, commit. Do not start the next group until the current one is clean.
    
    Rules: match existing behavior exactly — this is a migration, not a rewrite. No new features, no refactors beyond what the pattern change requires. If a file does not fit the reference pattern, STOP and ask instead of inventing a new approach.
    • ·Get step 1 genuinely right — every later file inherits that reference pattern, good or bad.
    • ·Enable coexistence up front (`allowJs`, or both routers running) so `main` stays deployable through the entire migration.
    • ·"This is a migration, not a rewrite" is the load-bearing line — without it, agents will happily "improve" things and blow up your diff.
  • Major version upgrade with a breaking-change audit first

    You need to bump a core dependency across a major version (Next.js 14 to 15, React 18 to 19, Tailwind CSS 3 to 4, Drizzle ORM major, etc.) and don't want it to silently break half the app on install.

    I want to upgrade [PACKAGE] from [CURRENT VERSION] to [TARGET VERSION] in this project.
    
    Do NOT run the upgrade blindly. Work in this order and STOP for my approval after step 2:
    
    1. Find the official migration guide / changelog / breaking-changes list for [PACKAGE] between these two exact versions. Read it. Also check whether an official codemod exists (e.g. `npx @next/codemod`, `npx types-react-codemod`, the Tailwind upgrade tool).
    
    2. Audit MY codebase for every place that touches a breaking change. Grep for the affected APIs, config keys, imports, and env vars. Give me a written table: file, line, what breaks, the fix. Flag anything with no clean migration path. Do NOT edit any files yet.
    
    3. After I approve, upgrade on a new branch: bump the version in package.json, run the codemod if one exists, then apply the manual fixes from your audit one logical change at a time, committing after each coherent group.
    
    4. Run [TYPECHECK/LINT/TEST COMMAND] and start the dev server. Fix what breaks. Report anything you could not resolve instead of hacking around it.
    
    Before installing, also check my other dependencies for peer-dependency conflicts with the new version and tell me.
    • ·Pin the exact target version (e.g. `15.1.3`, not `latest`) so you and the agent are reading the same changelog.
    • ·If the step-2 audit comes back huge, upgrade one major at a time (14 to 15, then 15 to 16) instead of leapfrogging.
    • ·Keep the branch surgical. A framework bump plus unrelated feature work in one PR is impossible to review or revert.
  • Swap one provider or library for another

    You're replacing a dependency with a competitor — auth, payments, email, ORM — and want a clean cutover instead of a half-migrated mess where both are wired in forever.

    I want to migrate from [OLD TOOL] to [NEW TOOL] in this project (e.g. Auth0 to Clerk, Prisma to Drizzle, SendGrid to Resend, Stripe Checkout to Stripe Elements).
    
    Map the surface area BEFORE writing code:
    
    1. Grep the codebase for every usage of [OLD TOOL] — imports, API calls, env vars, config, webhooks, middleware. Give me the full inventory as a checklist so we both see the blast radius.
    
    2. For each usage, show the [NEW TOOL] equivalent. Call out anything [NEW TOOL] does differently or does not support 1:1 — I would rather know now than discover it in prod.
    
    3. Propose a migration order. If the two can run side by side, prefer an incremental cutover (feature flag or per-route) over a big-bang swap. If they cannot coexist (e.g. two auth systems on the same session), say so and plan the single cutover carefully.
    
    Then implement against the checklist, one item at a time, committing per item. Keep [OLD TOOL] installed and wired until EVERY checklist item is migrated and verified — do not delete its code, env vars, or config until the end. Update .env.example and the README with the new setup.
    
    Data/state to move: [DESCRIBE — users, subscriptions, templates — or write "none needed"].
    • ·Run the read-only inventory (steps 1-2) as its own turn and actually read it before approving — that's where you catch the "New Tool doesn't support X" landmines.
    • ·For auth and payments, migrate a non-prod environment end to end and test the real login/checkout flow before touching prod keys.
    • ·Remove the old dependency in a separate final commit, so a late-surfacing issue is a trivial one-commit revert.
  • Zero-downtime schema change (expand/contract)

    You're changing a production database schema — renaming a column, splitting a table, tightening a type, adding a NOT NULL — and cannot break live traffic during the deploy window.

    I need to change my production database schema with zero downtime. I'm using [Drizzle ORM / Prisma] on [Postgres / Supabase].
    
    The change: [DESCRIBE — e.g. "rename users.name to users.full_name" / "split a text address into structured columns" / "make orders.customer_id NOT NULL"].
    
    Design this as an expand/contract (parallel change) migration, NOT one destructive migration. Give me discrete, separately-deployable steps:
    
    1. EXPAND: additive schema change only — add the new column/table as NULLABLE. No drops, no renames. Write the migration.
    2. BACKFILL: a batched, idempotent, re-runnable script to populate the new field from existing data. It must NOT lock the table — batch by id and commit per batch.
    3. DUAL-WRITE: update app code to write BOTH the old and new fields so they stay in sync while old code is still running.
    4. CUTOVER: switch reads to the new field.
    5. CONTRACT: only after everything above is deployed and verified — drop the old column and remove the dual-write.
    
    For each step, tell me exactly what must be deployed and confirmed before the next step is safe. Write the migration files and the backfill script. Do NOT combine steps, and do NOT generate a migration that drops or renames a column in the same deploy that running code still depends on.
    • ·Never let an agent rename a column in one shot on a live DB — to your old running instances, a rename is a drop plus an add.
    • ·Test the backfill against a copy of prod data first (Supabase branching or a restored dump), not against an empty local table.
    • ·Ship each step as its own PR/deploy so you can pause or roll back at any point with no data loss.