Home / Articles

Bounded Prompts: Stopping AI Assistants from Inventing a Parallel Stack

Why vague feature requests make coding assistants add duplicate libraries and wrappers, and how a bounded prompt with paths, invariants and hard stops fixes it.

Publisher: REACTAPP.TOP6 min read1323 words

This article is published in English.

Sources: AI Doesn’t Need Better Prompts. It Needs Boundaries.

Ask a coding assistant to "build a user settings page" and you will usually get something that compiles, renders and looks finished. Look closer and the diff often brings its own state store, its own fetch helpers and a validation library your repository has never used. Nothing breaks, which is exactly why the problem slips through review. This article explains why vague requests fail this way, why adding adjectives does not help, and how to write a bounded prompt, plus a reusable checklist, that keeps generated code inside your existing architecture.

How a vague request fails softly

Picture a team working on a client admin app built with React and TypeScript. The codebase already has form primitives, a shared API client and a notification hook. Someone asks the assistant for a settings page in one short sentence. The result includes tabs, forms, toast messages, loading skeletons and a small state store. Underneath, the change quietly disagrees with the codebase:

  • Form state is managed by a library the project does not use.
  • New fetch wrappers sit beside the shared API client.
  • Toasts are built inline instead of going through the project's notification hook.
  • Validation schemas ignore the shapes already defined in types/.
  • A client-side "settings store" holds three fields that really belong on the server.
  • The skeleton loaders do not match the rest of the dashboard.

None of this throws an error, and it might survive a quick review unless someone opens package.json. That is the danger: vague prompts fail soft. They introduce a second way of doing something the team already solved, and every future maintainer inherits both. The assistant is good at the code it can see; the files it never opened are what decide whether the feature actually belongs in your app.

Why a longer prompt is not the fix

The usual advice is to be more specific. A natural second attempt asks for a "polished, production-ready" settings page with "clean UX, good TypeScript and solid error handling."

That produces the same parallel architecture, only delivered with more confidence. The words describe taste, and taste leaves the model entirely free to invent structure. What was missing was never eloquence. It was boundaries: explicit statements of what the model may use, what it must not add, and what counts as done.

Writing the bounded version

The bounded prompt below asks for the same feature but reads like a job description. It names the exact components, hook, API client and schema file to reuse, lists the tab and submission behavior, defines the tests, and ends with explicit prohibitions.

Build a user settings page in React and TypeScript.
Use ONLY these existing pieces:
- `src/components/FormField.tsx`
- `src/components/Button.tsx`
- `src/hooks/useNotify.ts`
- `src/api/client.ts` (no new fetch wrappers)
- validation schemas from `src/types/userSettings.ts`
Behavior:
- Tabs: Profile, Notifications, Security
- Preserve unsaved edits when switching tabs
- Show server field errors beside the matching inputs
- Disable submit while in flight
- Ignore duplicate submits
Tests:
- loading, success, server error, duplicate click
- empty required fields
Do NOT:
- add a state-management library
- add a toast library
- create a new API client
- invent types that duplicate `src/types/userSettings.ts`

This is less a nicer prompt than an architecture note the model has to follow. Compared with the one-liner, it communicates seven things that were previously left to chance:

  1. Which existing components to reuse.
  2. Where the source of truth for validation lives.
  3. Interaction rules, such as keeping unsaved edits across tabs.
  4. Where errors should appear.
  5. Concurrency behavior: the in-flight state and duplicate submits.
  6. Tests that define what "done" means.
  7. Hard stops on new dependencies.

With those constraints, the resulting diff becomes pleasantly boring. It uses the same FormField, the same useNotify, the same api/client. The tabs keep their dirty state without a new store, and duplicate submission is handled by a disabled button and a test rather than a "handle this later" comment. Established codebases already contain patterns; the job of the prompt is to point at them so clearly that the model cannot miss them.

This also explains why experienced developers tend to get better results from assistants. Their advantage is not phrasing; it is knowing in advance which details carry weight. What matters is communicating architecture, not decorating a prompt.

A quick test for boundedness

If your prompt would make sense pasted into any repository in the world, it is not bounded yet. A good prompt should be slightly useless outside your own source tree, because it references your paths, your types and your conventions.

A reusable pre-flight checklist

Keeping a template above each feature request makes the bounded style a habit. The one below groups the questions into context, invariants, behavior, constraints and a definition of done. The invariants section is the one most often skipped: deciding whether data lives on the server, in the URL or in local UI state prevents the "store for three fields" problem before it starts.

CONTEXT
- Stack: React + TypeScript
- Relevant paths: ...
- Patterns to copy: ...

INVARIANTS
- Where does data live (server / URL / local UI state)?
- What must survive refresh?
- What is the source of truth for types/validation?

BEHAVIOR
- Happy path
- Empty / invalid input
- Server errors (field-level + form-level)
- Slow network / double submit
- Permission failure

CONSTRAINTS
- Do not add: ...
- Do not invent parallel: API client / store / toast / form lib
- Prefer editing: [files]

DONE
- Tests for: ...
- Manual checks: ...

Use the template as a readiness gate. If you cannot fill in the constraints and the relevant paths, you are not ready to generate code; you are still designing. Generating while the design is open is precisely how a second architecture creeps in. Stable parts of this checklist can live in a project instruction file; see our guide to writing an effective CLAUDE.md file.

What still needs a human review

Bounded prompts remove most of the noise, but they do not replace judgment. When reviewing the diff, still check for:

  • A new package nobody approved.
  • A second way of calling the API.
  • Types drifting away from the server contract.
  • Client-side state for data the server should own.
  • Tests that exercise nothing beyond the happy path the model assumed.

Writing the prompt is the cheap part; knowing which boundaries matter in this codebase is the expensive part, and that knowledge lives with the team.

The smallest change to make today

If a full checklist feels heavy, add just three lines to your next feature request: which paths to use, which libraries not to add, and what "done" looks like, including at least one failure path.

Use only: [paths]
Do not add: [libs]
Done when: [2–3 behaviors + one failure path]

Wrapping up

  • Vague prompts rarely crash; they create duplicate infrastructure that looks finished and costs you later.
  • More adjectives change the tone of the output, not its structure. Constraints change the structure.
  • A bounded prompt names the files to reuse, the source of truth for data and types, the edge-case behavior, the tests, and the dependencies that are off-limits.
  • When the model invents something outside the lines, treat it as a missing boundary: fix the prompt and regenerate instead of polishing the wording.