> ## Documentation Index
> Fetch the complete documentation index at: https://vlyai-1c28d863.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# The five-layer method

> A reusable framework for writing vly prompts. Five layers — Goal, User, Data, Behavior, Constraints — that take 20 minutes to learn and pay off across every project.

export const PromptBlock = ({children, variant}) => <div className={`vly-prompt ${variant || ""}`}>{children}</div>;

The **five-layer method** is the framework we recommend for non-trivial prompts. It works because it forces you to answer five questions the agent would otherwise have to guess at. Once internalized, you stop writing it explicitly — the layers become how you think.

## The five layers

<Steps>
  <Step title="Goal — what user-facing thing is changing?">
    A one-sentence summary in the user's voice. "I can mark a task as done." Not "we add a `completed` field."
  </Step>

  <Step title="User — who does this affect, and how do their roles play in?">
    Logged-out visitors? Logged-in members? Admins only? Owners of the resource? This drives auth and permissions.
  </Step>

  <Step title="Data — what new entities, fields, or indexes are needed?">
    Names matter. Use the same name you'll use in the prompt body. "Add a `priority` field to `tasks`."
  </Step>

  <Step title="Behavior — what happens when?">
    The interactive flow: clicks, navigation, side-effects, real-time updates. Be explicit about edge cases.
  </Step>

  <Step title="Constraints — what shouldn't change, what limits apply?">
    "Don't break existing X." "Limit to 100 per page." "Read-only for non-admins." Constraints prevent the agent from over-editing.
  </Step>
</Steps>

## A worked example

You want to add a "shareable link" feature to tasks.

<PromptBlock>
  **Goal**: I can share a task with someone outside my team via a link.

  **User**:

  * Any team member can generate a share link for any task on their team.
  * Visitors with the link can view the task without signing in.
  * Visitors cannot edit. Logged-in team members with the link can edit (same as direct access).

  **Data**:

  * Add `shareToken` (optional, unique string) to the `tasks` table.
  * Add an index `by_share_token` on `tasks`.

  **Behavior**:

  * On the task detail page, add a "Share" button in the header. Clicking it generates a `shareToken` (if not set), copies the URL `/share/{shareToken}` to the clipboard, and shows a toast.
  * Visiting `/share/{shareToken}` shows the task in read-only view.
  * If a logged-in team member visits the share URL, they see the editable view (not the read-only one).
  * "Revoke link" button on the same modal — clears `shareToken`, the URL stops working.

  **Constraints**:

  * Share links never expire (unless revoked).
  * Generated tokens must be unguessable (32+ chars, URL-safe).
  * Don't change existing direct-access permissions.
</PromptBlock>

Submit this and you get the entire feature in one build, with edge cases handled, the right schema indexes, and the right auth behavior. No iteration needed.

## When to use which layers

You don't always need all five. Rule of thumb:

<CardGroup cols={2}>
  <Card title="Tiny edit" icon="edit-2">
    **Goal only.** "Change the button color to violet."
  </Card>

  <Card title="Add a feature to one user role" icon="user">
    **Goal + Behavior.** "Add a Mark as done button on each task. Clicking it sets `completed=true` and the row fades out."
  </Card>

  <Card title="Schema-touching feature" icon="database">
    **Goal + Data + Behavior.** Most new features land here.
  </Card>

  <Card title="Multi-role feature" icon="users">
    **Goal + User + Data + Behavior.** Add User layer when permissions matter.
  </Card>

  <Card title="Anything risky or large" icon="alert-triangle">
    **All five.** Protect against scope creep with the Constraints layer.
  </Card>
</CardGroup>

## Why each layer matters

### Goal — keeps you honest

Writing the user-facing summary often reveals the prompt isn't fully formed. If you can't say it in one sentence, you don't yet know what you want.

### User — drives auth and permissions

The agent will scope data to "the current user" by default, which is correct \~70% of the time. The other 30% (admin-only, team-scoped, public-with-link, etc.) needs to be stated.

### Data — makes the schema explicit

Schema changes are the most expensive thing to undo. Stating them upfront — with names — makes the [Plan mode](/building/plan-mode) review trivial.

### Behavior — pins down the interaction

"Add notifications" can mean ten things. "Show a bell with an unread count, dropdown opens on click, etc." can only mean one. The behavior layer is where vague prompts become specific.

### Constraints — prevents scope creep

The agent will often *also* fix nearby code it considers suboptimal. Sometimes that's helpful; sometimes it breaks things you didn't want touched. The constraints layer pins it.

## Common patterns

<Tabs>
  <Tab title="Permission-scoped CRUD">
    ```text theme={null}
    **Goal**: Customers can manage their own subscriptions.

    **User**: Logged-in customers. Each customer only sees and edits
    their own subscription.

    **Data**: existing `subscriptions` table; no new fields.

    **Behavior**:
    - /account/subscription page lists the customer's subscription
    - Pause / Cancel / Resume buttons
    - Each action is a Convex mutation that confirms with a modal first

    **Constraints**:
    - All actions check `subscription.customerId === currentUser._id`
    - Pausing is reversible, Canceling is not (after a 7-day grace)
    ```
  </Tab>

  <Tab title="Real-time feature">
    ```text theme={null}
    **Goal**: Team members see each other typing in the same task's
    comment box.

    **User**: Authenticated team members.

    **Data**:
    - Add `typing` table: { taskId, userId, lastTypedAt }
    - Index: by_task

    **Behavior**:
    - On keypress in the comment box, debounce 1s, then upsert a
      `typing` record with current timestamp
    - On the task detail page, query `typing` for the task,
      filter to records with lastTypedAt < 3s ago, render as
      "Alice is typing..." pills
    - Convex's reactive sync makes this auto-update

    **Constraints**:
    - Don't show "X is typing" to the user themselves
    - Old typing records cleaned up by a scheduled function every 5 min
    ```
  </Tab>

  <Tab title="Migration">
    ```text theme={null}
    **Goal**: Tasks have priorities (low / medium / high). Backfill
    existing tasks to medium.

    **User**: Affects all users.

    **Data**:
    - Add `priority` enum field to tasks (default 'medium')
    - Run a one-time migration to set existing rows to 'medium'

    **Behavior**:
    - Show priority as a colored chip on the task list and detail
      pages (low=gray, medium=blue, high=red)
    - Allow editing priority from a dropdown on the task detail page

    **Constraints**:
    - Don't break existing pages
    - Don't change the default sort order
    ```
  </Tab>
</Tabs>

## Tips

<Tip>
  **Write the layers as headers (`**Goal**:`, `**User**:`, etc.).** The agent parses headers reliably and follows the structure. Bullets within each layer are fine.
</Tip>

<Tip>
  **You can skip layers, but always include Goal.** Even a one-line edit benefits from a stated goal — it makes the prompt re-readable in [version history](/building/version-history) months later.
</Tip>

<Tip>
  **The Constraints layer is the most-skipped, and the highest-leverage.** When a prompt produces a working result that breaks something else, the cause is almost always a missing constraint.
</Tip>

## Related

<CardGroup cols={3}>
  <Card title="Fundamentals" icon="message-square" href="/prompting/fundamentals">
    The principles behind the layers.
  </Card>

  <Card title="Prompt library" icon="library" href="/prompting/prompt-library">
    30+ five-layer templates for common features.
  </Card>

  <Card title="Debugging prompts" icon="bug" href="/prompting/debugging-prompts">
    What to do when the result misses.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.