Skip to content

Implementation Prompts

Implementation is where agents do their best work and their most expensive damage: the code arrives fast, compiles, and looks native — so review pressure drops exactly when invented decisions are densest. Every pattern below assumes the prompt anatomy; this page covers what changes per implementation type. Ready-to-fill versions live in the implementation prompt library.

One rule dominates all four patterns: point at existing code instead of describing style. "Follow the pattern in src/routes/invoices.ts" is worth more than any paragraph about your error-handling philosophy, because the agent reads the actual file and copies real structure — middleware order, error envelope shape, naming — instead of interpreting your abstract description. Every adjective you use ("clean", "idiomatic", "consistent") is a decision you just delegated; every file path is a decision you kept.

Product prompts

Building a user-visible capability end to end — new flow, new screen-plus-API, new integration.

When to use: the task is a product outcome, not a code change. Required inputs: the user story with the why, the entry point in the existing product, which existing flows it must resemble, and what's explicitly deferred. What to avoid: describing the feature purely from the user's perspective with zero code anchors — the agent will invent an architecture for it; and bundling more than one user-facing behavior per prompt.

For anything larger than a single flow, don't prompt at all — write a spec first (Idea to Spec) and prompt against the spec.

Mini checklist for reviewing the output:

  • [ ] The flow matches an existing flow's structure, not a parallel invented one
  • [ ] Deferred items were actually deferred, not half-built
  • [ ] No new top-level directories or "framework" abstractions for a single feature

Feature / implementation prompts

The workhorse: a scoped change to an existing system with a known design.

When to use: design decided, code needed. Required inputs: files that define the pattern to follow, precise behavior, acceptance criteria, out-of-scope list. What to avoid: leaving data shapes implicit ("store the preferences" — where, keyed how, migrated how?).

Weak:

text
Add rate limiting to the API.

The agent must pick: which endpoints, what limits, keyed by IP or user or API key, in-memory or Redis, response code and headers, and whether to add a middleware framework. Expect a new express-rate-limit dependency with an in-memory store that silently breaks the moment you run two replicas.

Strong:

text
Add rate limiting to the public API endpoints in src/routes/public/.

Context:
- We run 3 replicas behind an ALB; state must live in Redis.
  Redis client is already configured in src/lib/redis.ts — reuse it.
- Middleware conventions: src/middleware/auth.ts — same style,
  same registration point in src/app.ts.

Behavior:
- 100 requests/minute per API key (key extraction already exists:
  src/middleware/auth.ts exports getApiKey).
- Sliding window, not fixed window.
- On limit: 429, JSON error envelope matching src/lib/errors.ts,
  Retry-After header in seconds.
- Internal routes (src/routes/internal/) untouched.

Constraints: no new dependencies; implement the sliding window
against Redis directly (ZADD/ZREMRANGEBYSCORE approach is fine).

Acceptance:
- Test: 101st request within a minute returns 429 with Retry-After.
- Test: two different API keys don't share a bucket.
- Test: Redis unavailable → fail OPEN (allow request, log error).
  This is deliberate: availability over strict limiting.

Out of scope: no per-endpoint configurable limits, no admin UI,
no changes to auth middleware itself.

The fail-open line deserves attention: fail-open vs. fail-closed is precisely the kind of judgment call an agent resolves by coin flip, and either default is defensible — which means it will pick without asking. Decisions that are defensible-either-way are the most important ones to state.

Expected output: one middleware file, registration in src/app.ts, tests. Anything beyond that is scope creep.

Mini checklist:

  • [ ] Diff touches only the files the task implies
  • [ ] Pattern files were actually followed (compare middleware shape side by side)
  • [ ] Every acceptance criterion has a corresponding test, and the tests test behavior, not mocks

Backend prompts

Data-layer and service work: endpoints, jobs, queries, schema changes.

When to use: any change where the failure mode is data corruption, not a bad pixel. Required inputs: everything a feature prompt needs plus explicit data semantics — idempotency, transaction boundaries, null/duplicate handling, volume assumptions. What to avoid: letting the agent design a schema unsupervised (schema is an architecture decision); leaving concurrency behavior unstated.

Weak:

text
Create a webhook handler for Stripe payment events that updates
invoice status.

Missing: signature verification, idempotency (Stripe retries — this handler will receive duplicates), event-type filtering, out-of-order delivery, and whether "update status" may ever move an invoice backwards from paid. The weak version yields a handler that marks invoices paid twice and emails the customer twice.

Strong:

text
Create a Stripe webhook handler: POST /api/webhooks/stripe.

Context:
- Route + error conventions: src/routes/invoices.ts
- Invoice model: src/models/invoice.ts (status enum: draft, sent,
  paid, void)
- Stripe SDK already configured: src/lib/stripe.ts

Behavior:
- Verify the Stripe signature (constructEvent with
  STRIPE_WEBHOOK_SECRET); invalid signature → 400, log, no body echo.
- Handle only payment_intent.succeeded and payment_intent.
  payment_failed; other events → 200 immediately (ack, ignore).
- Idempotent: store processed event IDs in a stripe_events table
  (migration included); duplicate event ID → 200 no-op.
- succeeded → invoice status 'paid' + paid_at. Status transitions
  only move forward: a 'void' invoice never becomes 'paid' — log a
  warning instead.
- DB writes for one event happen in a single transaction.

Acceptance:
- Test: duplicate delivery of the same event ID mutates nothing
  the second time.
- Test: bad signature → 400 and no DB writes.
- Test: succeeded event on a 'void' invoice leaves it 'void'.

Out of scope: no refund handling, no customer emails (that's the
existing on-status-change hook's job — do not duplicate it).

Expected output: handler, one migration, tests using Stripe's event fixtures. Mini checklist:

  • [ ] Idempotency test actually replays the same event, not just a similar one
  • [ ] Transaction boundaries match the prompt (grep for writes outside it)
  • [ ] Migration is reversible and touches only the new table

UI implementation prompts

When to use: building against an existing design system. Required inputs: the component(s) to imitate by path, the states to handle (loading / empty / error / success), and data-source pointers. What to avoid: adjectives as design direction — "modern and clean" is how you get an agent-flavored redesign of one corner of your app; and unstated state handling, because the happy path is the only one agents build unprompted.

The four states are the UI equivalent of backend idempotency: never assumed, always stated.

text
Build the invoice-export panel in src/components/invoices/.

Follow ExportPanel patterns from src/components/reports/
ReportExportPanel.tsx — same layout primitives, same hooks style,
same Tailwind conventions. Do not introduce styled-components or
new CSS files.

States (all four required):
- idle: date-range picker (reuse src/components/ui/DateRange.tsx)
  + disabled Export button until range valid
- loading: button spinner, inputs disabled (see ReportExportPanel)
- error: inline error via src/components/ui/Alert.tsx, form stays
  filled and retryable
- success: browser download triggers; panel returns to idle

Data: call GET /api/invoices/export via the existing api client
(src/lib/api.ts) — no raw fetch.

Out of scope: no changes to DateRange or Alert, no new routes,
no storybook stories in this pass.

Expected output: one component + test; zero changes under src/components/ui/. Mini checklist:

  • [ ] All four states reachable and tested
  • [ ] No new styling system smuggled in; class usage matches the pattern file
  • [ ] Shared components consumed, not forked into local copies

A field manual for AI-native software engineering.