# Constraints and Trade-offs

Course: [Intent Engineering](https://www.devtutorials.ai/learn/intent-engineering) · Section: Real Codebases
Source: https://www.devtutorials.ai/learn/intent-engineering/real-codebases/constraints-and-trade-offs

> Keep the agent's change small and in the team's style. Point it at the file to copy, list the APIs it must not change, limit the paths it may touch, and rank the trade-offs so it picks the right approach.

## A Diff That Grew

You asked the agent to add an Export button.

That sounds like a small change. But when the work came back, the agent had changed eight files.

Only three of them were needed for the export.

The other five were extra:

- A new button with its own look
- New colours and spacing for that button
- A change to a shared data hook
- A change to the grid's public props
- A "tidied" CSV helper

The agent was trying to help. But each extra change was a guess about how the feature should fit into the code.

Some of those guesses can break parts of the app that nobody meant to touch.

**Simple lesson:** a small feature can still turn into a big change if nobody sets limits.

### A Real-World Example

You ask a carpenter to put up one shelf.

The carpenter puts up the shelf. Then they also repaint the trim, move a light switch and tidy your cupboard.

None of that was asked for.

AI agents do the same thing. Without limits, they change more than the feature needs.

Click the button and open the **Preview & Checks** tab.

- **The file list:** files in red are outside the area the team allows.
- **The review checks:** all four fail, because the agent changed too much.

**In simple words:** a **diff** is the full list of changes the agent makes.

**Simple lesson:** without limits, the agent may change much more than the feature needs.

## TL;DR

1. A **constraint** says how a feature must fit into the existing code.
2. A constraint does not change what the feature does. It changes how the code is written.
3. There are four kinds:
   - **Pattern by path** = point at a real file to copy.
   - **Frozen API** = code that must not change, because other code depends on it.
   - **Allowed paths** = the only folders and files the agent may change.
   - **Ranked trade-offs** = what matters most when good options disagree.
4. Point at real files. Do not describe a style.
5. Allowed paths should be narrow, but not so narrow that the feature cannot connect.
6. Write the team's trade-off order, not your own.

## What You Will Learn

In this lesson, you will learn:

1. What a constraint is.
2. How to point the agent at a pattern to copy.
3. How to protect shared code from changes.
4. How to limit where the agent may make changes.
5. How to rank trade-offs so the agent picks the right approach.

**About the diff in this lesson**

- The diff is made by fixed rules. It is not a real AI.
- Each missing constraint adds the same extra files every time.
- The same spec always gives the same diff, so your work can be checked.

## Pattern by Path

### Point at a File, Not a Style

Every team has a usual way of writing code.

But this kind of line does not help much:

```text
Follow our coding style.
```

The agent cannot read a style guide nobody gave it. But it can read a real file.

So point at an example instead.

Example:

```text
Follow the pattern in `src/components/Toolbar.tsx`.
```

Or, for ASP.NET Core:

```text
Follow the pattern in `Endpoints/OrdersEndpoints.cs`.
```

Now the agent has something real to copy.

Without a pattern, the agent may write:

- A new kind of component
- A new way of styling
- A new kind of API

The code may work. But it will not look like the rest of the project.

**Simple lesson:** point at a real file. A file is clearer than a description.

### Pick the Right File

Not every file is a good example.

Many projects have old code and new code side by side.

For example, this file may still exist:

```text
src/components/LegacyToolbar.tsx
```

But the team no longer wants new code written that way.

If you point at that file, the agent copies the old style.

**Simple lesson:** point at the pattern the team is moving to, not the one it is moving away from.

Watch what happens to the diff as each constraint is added:

Each constraint removes a group of extra files. With all of them, only the needed files are left.

## Frozen APIs

### What Other Code Depends On

**Frozen API** = code that must not change in this feature, because other code depends on it.

Examples for React:

- `useOrdersQuery`
- `OrdersGridProps`

Examples for ASP.NET Core:

- `GET /orders`
- `OrderDto`

Other pages, services or teams already use these.

The agent may see a shared hook and think it is the easiest place to add the new feature.

But a change there affects everyone who uses it.

### Name Every One

If something must not change, say so directly.

Example:

```text
Do not change `useOrdersQuery`.
```

Then the agent works around it instead of changing it.

Name every frozen API. If you leave one out, the agent may still change that one.

**Simple lesson:** if other code depends on something, tell the agent not to change it.

## Allowed Paths

### Keep the Diff Reviewable

Even with a pattern and frozen APIs, the agent may still tidy nearby code.

For example, it may:

- Clean up a CSV helper
- Rename a utility function
- Move helper code around

These changes may not be wrong. But now a reviewer has to check work nobody asked for.

**Allowed paths** = the only folders and files the agent may change.

Example:

```text
Only change files in `src/features/export/`
and `src/components/Toolbar.tsx`.
```

This tells the agent:

- Where the feature code goes
- Where it connects to the app
- That everything else stays as it is

**Simple lesson:** a small diff is easier to review, test and trust.

### Not Too Narrow

Allowed paths can also be too strict.

Suppose you only allow this folder:

```text
src/features/export/
```

The agent can build the export. But it cannot add the button to the toolbar. So nobody can use the feature.

Watch the difference:

The first version blocks the one file that connects the feature. The second version allows it, and the checks pass.

**Simple lesson:** allow the feature folder and the one file that connects it, and nothing more.

### Lab Exercise: Shrink the Diff

The tech lead's note gives you three things:

- A pattern file to copy
- The frozen APIs
- The allowed paths

Your task: turn the note into constraint lines in the spec.

Press **Check my spec**, then open **Preview & Checks**.

The goal is a diff of three files, with every check passing.

Leave the **Trade-offs** section empty for now.

**Copy paths exactly.**

- Put every file path in backticks.
- Copy it exactly from the note.
- One wrong letter points at a file that does not exist.

## Ranked Trade-offs

### When Good Options Disagree

Sometimes there is more than one good way to build a feature.

Here are three ways to make the Excel file.

**Option 1: use a tested spreadsheet library**

- Correct
- Fast
- Makes the app bigger

**Option 2: write the file by hand**

- Very small
- Fast
- May break on dates and special characters

**Option 3: build the file on the server**

- Correct
- Small
- Slow for big grids

Without guidance, the agent often picks the smallest change. But smallest is not always best.

### Rank What Matters

**Ranked trade-off** = a list of what matters most, in order.

Example:

```text
correctness > performance > bundle size
```

This means:

1. First, be correct.
2. If more than one option is correct, pick the faster one.
3. If they are still equal, pick the smaller one.

Watch how the order changes the agent's choice:

The same three options, but a different order picks a different winner. That is why the order belongs in the spec.

**Important:** write the team's order, not your own.

- If the team puts correctness first, write that.
- If you put size first instead, the agent may pick an option that breaks dates.

### Lab Exercise: Rank the Trade-offs

Your constraints now keep the diff small. But the agent still picks the wrong way to build the file.

Your task: add the team's order to the **Trade-offs** section.

### Teams Rank Differently

The right order depends on the team.

- **Team A:** correctness > performance > bundle size
- **Team B:** correctness > bundle size > performance
- **Team C:** correctness > performance > bundle size

Teams A and C give the same answer, because only two options are correct.

A different order can lead to a different choice. That is fine, as long as the spec matches the team.

A spec can tell the agent the rules. Only the harness can stop it from breaking them. See [Hooks and Permissions](/learn/harness-engineering/foundations/hooks-and-permissions) in the Harness Engineering course.

The exercise passes when the agent follows the team's order and picks the tested library.

**Simple lesson:** write the order your team agreed on.

## What Changes per Tool

The ideas are the same in every tool. Only the place where you write them changes.

| Tool | Constraints for one feature | Rules for the whole project |
|---|---|---|
| Claude Code | `docs/specs/<feature>/spec.md` | `CLAUDE.md` |
| Codex | `docs/specs/<feature>/spec.md` | `AGENTS.md` |
| Cursor | `docs/specs/<feature>/spec.md` | `AGENTS.md` or `.cursor/rules/*.mdc` |
| Spec Kit | `specs/<NNN-feature>/plan.md` | Constitution files |
| Kiro | `.kiro/specs/<feature>/design.md` | Steering files |

- **Feature constraints** guide one change.
- **Project rules** apply to every change.

Rules that must never be broken should also be checked by tools, not only written down.

## Common Mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| "Match our style" | The agent knows our style. | "Follow the pattern in `src/components/Toolbar.tsx`." | It writes a new style. |
| Pointing at the old pattern | Any example will do. | Point at the pattern the team is moving to. | The agent copies code you are trying to remove. |
| No frozen APIs | The agent will be careful. | "Do not change `useOrdersQuery`." | Four other pages break. |
| No allowed paths | A good agent stays focused. | "Only change files in …" | The diff has "tidied" code nobody asked for. |
| Paths too narrow | Smaller is always safer. | Allow the feature folder and the file that connects it. | The feature cannot be connected. |
| No trade-off order | The agent will pick sensibly. | "correctness > performance > bundle size" | It picks the smallest change, which is wrong. |

## Exercises

These exercises help you practise writing constraints and trade-offs.

Press **Check** when you are ready. Use **Show Hint** if you get stuck.

## What to Remember

- A constraint says how a feature fits into the existing code.
- Point at a real file to copy, not a style.
- A frozen API is code other code depends on. Tell the agent not to change it.
- Allowed paths keep the diff small and easy to review.
- Allowed paths must still include the file that connects the feature.
- A ranked trade-off tells the agent what wins when good options disagree.
- Rules that must never be broken should also be checked by tools.

## Further Reading

- [Intent Engineering Framework for AI Agents, by Product Compass](https://www.productcompass.pm/p/intent-engineering-framework-for-ai-agents): a product manager's view of goals, limits and decision rights for agents.
- [Harness engineering, by OpenAI](https://openai.com/index/harness-engineering/): keeping agents inside the lines with checks.
- [Kiro steering](https://kiro.dev/docs/steering/): repo-wide rules in steering files.

Next: **Decision Rights**. You will decide which choices the agent may make on its own, and when it must stop and ask.
