Custom instructions are the highest-leverage Copilot configuration, and the one most often written badly.
They are always on: applied to every request within their scope, without you asking. That is what makes them powerful and what makes a long instructions file counterproductive — every line is charged against the context that would otherwise hold your code.
The types
Section titled “The types”Four kinds, differing in scope and in which surfaces read them.
| Type | Location | Scope |
|---|---|---|
| Repository-wide | .github/copilot-instructions.md | Everything in this repository |
| Path-specific | .github/instructions/**/*.instructions.md | Files matching a pattern |
| Agent instructions | AGENTS.md, CLAUDE.md, GEMINI.md | Read by agent surfaces |
| Personal | Your profile or IDE settings | You, across projects |
| Organisation | Organisation settings | Everyone in the organisation |
Repository-wide is where most teams should start. One file, in the repository, in review, applying everywhere.
Path-specific is the one that most improves quality once the basics are in place, because criteria genuinely differ by area. What matters in a database migration is not what matters in a React component, and a single file covering both is either vague or enormous.
Agent instructions — AGENTS.md and its siblings — are a cross-tool convention: instructions
intended for AI agents generally rather than for Copilot specifically. Copilot CLI reads AGENTS.md,
copilot-instructions.md and CLAUDE.md.
Personal instructions follow you rather than the project. Good for “I prefer explanations before code”; wrong for anything about the codebase, which belongs in the repository where the team gets it.
Organisation instructions apply across repositories, which suits genuinely universal standards and suits nothing else — an organisation instruction that is wrong for one team is wrong on every request that team makes.
The support matrix
Section titled “The support matrix”The part that causes the most confusion. Support is not uniform, and assuming it is produces configuration that silently does nothing.
Verified against GitHub’s published matrix:
| Surface | Repository-wide | Path-specific | Agent (AGENTS.md etc.) |
|---|---|---|---|
| Copilot Chat on github.com | Yes | — | — |
| Copilot cloud agent | Yes | Yes | Yes |
| Copilot code review | Yes | Yes | Yes |
| VS Code chat | Yes | Yes | AGENTS.md |
| Visual Studio chat | Yes | Yes | — |
| JetBrains chat | Yes | Yes | — |
| Eclipse chat | Yes | — | — |
| Xcode chat | Yes | Yes | — |
| Copilot CLI | Yes | Yes | Yes |
Two readings worth taking from that.
Repository-wide instructions work everywhere. If you write one thing, write that. It is the only mechanism with universal support, which makes it the right foundation for a team.
Path-specific and agent instructions have good but not universal coverage. Excellent on the agent surfaces and code review; patchier in some IDEs.
Writing instructions that work
Section titled “Writing instructions that work”The difference between a file that improves output and one that costs context for nothing.
Be specific to this repository
Section titled “Be specific to this repository”Useless:
- Write clean, maintainable code- Follow best practices- Add appropriate error handlingEvery line is true of every codebase, which means none of them changes anything. A model already attempts all three.
Useful:
- All outbound HTTP goes through `clients/base.py`, which adds tracing headers. Never call `requests` or `httpx` directly.- Raise `AppError` subclasses, never bare exceptions. See `errors.py`.- Tests use pytest with fixtures from `tests/conftest.py`. No unittest classes.- The `legacy/` directory is frozen. Do not modify it.Every line is a fact about this repository that a model cannot infer and would otherwise get wrong.
The test for whether a line belongs
Section titled “The test for whether a line belongs”Would a new engineer need to be told this, and would they be unable to work it out from the code?
If yes, it belongs. If they could work it out by reading two files, it is costing context for something the model can also work out.
Keep it short
Section titled “Keep it short”Instructions are charged on every request. A 2,000-line file:
- Crowds out the code the model needs to see
- Is followed less reliably, not more — a long list dilutes each item
- Is nobody’s job to maintain, so it decays
A page is a reasonable target. If it is longer, that is usually a signal that some of it should be path-specific.
Describe constraints, not aspirations
Section titled “Describe constraints, not aspirations”“Prefer composition over inheritance” is a philosophy. “Handlers must not import from models/
directly; go through the service layer” is a constraint that can be complied with or violated.
Constraints are actionable, checkable, and frequently pairable with a lint rule — which is what turns an instruction into enforcement.
Path-specific instructions
Section titled “Path-specific instructions”Where quality improves once the basics are in place.
---applyTo: "migrations/**/*.sql"---
- Every migration must be reversible. Include the `down` operation.- Never `DROP COLUMN` in the same release that stops writing to it. Two releases: stop writing, then drop.- Adding a NOT NULL column requires a default or a backfill; state which.- Index creation on large tables must be concurrent.---applyTo: "src/components/**/*.tsx"---
- Components are function components with typed props. No class components.- Styling uses the design tokens in `styles/tokens.ts`. No inline hex colours.- Every interactive element needs an accessible name.The applyTo frontmatter is what scopes the file. The value of this mechanism is that each set of
rules is short, specific, and only present when relevant — which is the opposite of the single large
file problem.
AGENTS.md and the cross-tool files
Section titled “AGENTS.md and the cross-tool files”AGENTS.md, CLAUDE.md and GEMINI.md are agent instruction files — a convention for guidance
intended for AI agents rather than for one vendor’s product.
The practical position:
AGENTS.md is the portable one. It is read by Copilot’s agent surfaces and by other tools that
adopt the convention. For a repository where contributors use different assistants, this is where
shared agent guidance belongs.
Copilot CLI reads AGENTS.md, copilot-instructions.md and CLAUDE.md.
Do not duplicate content across them. Three files with the same rules is three files to keep in
sync, and they will diverge. Where a file reference mechanism is available — @ followed by a relative
path, in Copilot CLI — one file can include another rather than repeating it.
The decision is covered in full in Repository Instructions, which is the page for choosing between all of these.
A complete example
Section titled “A complete example”A repository-wide file for a Python service, at the length this page argues for.
# Copilot instructions
## Stack
Python 3.12, FastAPI, SQLAlchemy 2.x (not 1.x — the APIs differ substantially),Alembic for migrations, pytest for tests.
## Conventions that are not obvious from the code
- All outbound HTTP goes through `clients/base.py::request()`, which adds tracing headers and retry handling. Never call `httpx` or `requests` directly.- Raise subclasses of `AppError` from `errors.py`. Bare `Exception` and `ValueError` are not caught by the error middleware.- Database access goes through a repository class in `repositories/`. Handlers must not import from `models/` directly.- Times are UTC everywhere, stored as timezone-aware datetimes.
## Testing
- pytest, with fixtures from `tests/conftest.py`. No unittest-style classes.- Every new function in `services/` needs a corresponding test.- Use the `db_session` fixture rather than creating sessions directly.
## Constraints
- `legacy/` is frozen. Do not modify files there.- Do not add dependencies without saying so explicitly in your response.- Do not reformat files you are not otherwise changing.Roughly thirty lines. Every one is a fact about this repository that a model would otherwise get wrong, and three of them exist specifically to prevent behaviours that agent mode exhibits by default.
Note what is absent: no directory tree, no explanation of what FastAPI is, no general advice about code quality. All of that is either visible in the repository or already known.
How instructions combine
Section titled “How instructions combine”Several instruction sources can apply at once, and understanding how they stack prevents a common confusion.
They combine rather than override. Repository-wide, path-specific and personal instructions apply together where all are supported. A path-specific file adds to the repository-wide rules rather than replacing them.
Conflicts are not resolved deterministically. If your repository-wide file says one thing and a path-specific file says the opposite, the outcome is not guaranteed. That is a reason to avoid contradictions rather than to memorise a precedence order — write the general rule broadly and the specific rule as an addition rather than a contradiction.
Concretely: if the general rule is “use the shared logger”, the path-specific rule for a CLI tool should say “CLI entry points log to stderr directly; the shared logger is not initialised there” rather than “do not use the shared logger”. The first is an exception with a reason, which reads coherently alongside the general rule.
Organisation instructions apply on top of repository ones, which is why they should be genuinely universal. An organisation instruction that suits the main product and not the tooling repository is wrong on every request in the tooling repository, and no repository-level file can undo it cleanly.
What instructions do not do
Section titled “What instructions do not do”The boundary, stated plainly because it is easy to over-trust a file that mostly works.
They do not enforce. An instruction shapes a probabilistic process. It reduces the frequency of a behaviour; it does not prevent it.
They do not apply where unsupported. Configuration for a surface that does not read it is configuration doing nothing, silently.
They do not survive being too long. Past some length, adding a rule makes the existing ones less reliable.
They do not replace tests and linters. Anything that must hold needs a mechanism that fails.
The correct relationship:
Instruction → improves the first draftLint rule → catches the violationTest → proves the behaviourRuleset → decides what mergesAn instruction saying “always use the shared logger” pairs with a lint rule that fails the build. The instruction means you write it correctly the first time; the lint rule means it cannot ship otherwise.
Instructions across surfaces, in practice
Section titled “Instructions across surfaces, in practice”The support matrix says what is read. What it does not say is that the same instruction has different effects depending on where it lands, which is worth knowing when you write them.
In chat, instructions shape a single answer. The effect is immediate and easy to test: ask for something that would violate a rule, see whether it complies.
In completions, instructions have less room to act. A single-line completion has little scope for architectural guidance, though conventions about naming and idiom do come through.
In agent mode, instructions matter most. An agent editing six files applies (or ignores) a
convention six times without asking, and the constraints — “do not touch legacy/”, “do not
reformat” — are doing work on every action.
In code review, instructions become review criteria. “Every new function in services/ needs a
test” turns into a review comment when one does not. This is the surface where instructions most
directly change what a human sees, and it deserves its own tuning —
custom instructions for code review.
In the cloud agent, instructions are most of the steering you get, because you are not present to correct anything. A repository with thin instructions is a repository where delegation produces generically reasonable, locally wrong work.
The consequence for writing: instructions that read as slightly over-explicit in chat are usually right for agents. “Do not add dependencies without saying so” is unnecessary in a conversation where you would see it happen; it is load-bearing when nobody is watching.
Measuring whether they work
Section titled “Measuring whether they work”Instructions are easy to write and easy to leave unexamined. Three checks.
Does the correction stop recurring? The direct test. You wrote the rule because you kept correcting something; has the correcting stopped? If not, the rule is either not being read on that surface, or it is buried in a file that is too long.
Does an obvious violation get caught? Ask for something that breaks a rule. If it complies, the instruction is reaching the model. This takes thirty seconds and is the only positive confirmation available.
Is the file still true? The quiet failure. A rule describing a convention you migrated away from does not become inert — it actively pushes output toward the old pattern, and the person who wrote it is not the person hitting the problem six months later.
There is no metric worth tracking beyond these. Suggestion acceptance rates and similar measure usage rather than fit, and a team can have high acceptance of output that is subtly wrong for the codebase.
Getting started
Section titled “Getting started”-
Note what you correct for a week. The things you keep saying are the instructions.
-
Write ten lines. Repository-wide, specific, constraint-shaped.
-
Test it. Ask for something that would violate a rule. Does it comply?
-
Add path-specific files where criteria genuinely differ — migrations, infrastructure, front-end.
-
Review it like code. It changes every contributor’s tooling; a CODEOWNERS entry is reasonable.
-
Prune quarterly. Rules for conventions you abandoned actively push output the wrong way.
Step 1 is the one that produces a good file. Instructions written from first principles describe an idealised codebase; instructions written from observed corrections describe yours.
Security-relevant instructions
Section titled “Security-relevant instructions”Instructions are a reasonable place for security conventions, with one boundary that must be clear.
Worth putting in instructions:
- “All user input reaching a query must go through the parameterised helpers in
db/query.py.” - “Secrets come from the secret manager via
config.get_secret(). Neveros.environdirectly for credentials.” - “Any new endpoint requires an explicit authorisation decorator; there is no default-deny middleware.”
- “Do not log request bodies. They contain personal data.”
Each is a fact about this system that a model cannot infer and would otherwise get wrong in a plausible way.
Not a substitute for controls. An instruction saying “never commit secrets” does not prevent it; push protection does. An instruction saying “validate all input” does not enforce it; tests and code scanning find where it did not happen.
The relationship is the one from earlier in this article, applied to security specifically: the instruction improves the first draft, and the control catches the case where it did not.
One thing not to put in instructions: anything sensitive. The file is in the repository, readable by everyone with access, and its contents reach a model on every request. An instruction containing an internal hostname, a credential format, or a description of an unpatched weakness is disclosure with extra steps.
Instructions and the other mechanisms
Section titled “Instructions and the other mechanisms”A brief orientation, since instructions are one of five things and the boundaries matter.
Instructions versus prompt files. Instructions are always on and describe how to work here; prompt files are invoked and describe a specific task. “Use pytest” is an instruction. “Generate a test suite for this module, following our conventions” is a prompt file.
Instructions versus skills. Both carry knowledge; the difference is loading. Instructions are charged on every request. A skill is loaded when the agent judges it relevant, which means a skill can be long — a full release-notes procedure, a security review checklist — without costing anything on unrelated requests. If it is long and only sometimes relevant, it is a skill.
Instructions versus custom agents. An agent is a persona with restricted tools. Instructions apply to whatever agent is running; an agent definition determines which agent is running and what it can do.
Instructions versus hooks. Instructions request; hooks enforce. A preToolUse hook can deny a tool
call outright, which no instruction can.
The decision table lives on Repository Instructions, which is the page for choosing between all of them deliberately rather than defaulting to whichever you learned first.
Common mistakes
Section titled “Common mistakes”Generic advice. “Write clean code” changes nothing and costs context.
Too long. Followed less reliably, and crowds out the code.
Documenting what the code shows. Directory structure and file names are visible.
Assuming universal support. Check the surface.
Personal instructions for project facts. The team does not get them.
Treating instructions as enforcement. They shape; linters and rulesets enforce.
Never pruning. A rule describing an abandoned convention is worse than no rule.
Duplicating across AGENTS.md and copilot-instructions.md. They diverge, and you will not notice
which one is being read.
Adopting this on a team
Section titled “Adopting this on a team”Instructions are a shared artefact, which makes adoption a social problem more than a technical one.
One person should draft it. A file written by committee accumulates everybody’s preferences and hits the length problem immediately. One person writing from observed corrections, reviewed by the team, produces something shorter and more useful.
Review it like code. It changes how every contributor’s tooling behaves. A pull request, a reviewer, and a CODEOWNERS entry if the repository has one.
Expect disagreement about conventions to surface. This is a feature. Writing down “we always use X” frequently reveals that half the team did not know that, or disagrees. Resolving it is valuable independently of the tooling.
Do not let it become a style guide. The temptation is to write down every preference. The test remains: is this something a new engineer would need told and could not infer? Style that a formatter enforces does not need an instruction — the formatter already won that argument.
Revisit after a significant change. A framework migration, a new service boundary, a convention you deliberately changed. Stale instructions are worse than absent ones because they actively steer toward the old approach.
The realistic maintenance burden is small: a page-long file, touched a few times a year, reviewed when it changes. That is proportionate to something applied on every request every contributor makes, and it compares well with correcting the same thing individually forever.
Mental model
Section titled “Mental model”Instructions are the standing brief you would give a competent contractor on their first day: the conventions, the constraints, the things that would otherwise be discovered by getting them wrong. Brief and specific beats comprehensive, because the brief is repeated on every single request.
What you learned
Section titled “What you learned”- Instructions are always on, and every line costs context on every request
- Repository-wide instructions are the only type with support across all surfaces
- Path-specific instructions scope rules with
applyTofrontmatter and keep the main file short AGENTS.mdis the cross-tool convention; Copilot CLI also readscopilot-instructions.mdandCLAUDE.md- Personal instructions are about you; project facts belong in the repository
- Write constraints, not aspirations — checkable, and pairable with lint rules
- The test for a line: would a new engineer need telling, and could they not infer it?
- Instructions shape behaviour; linters, tests and rulesets enforce it
- Write them from observed corrections, and prune them on a schedule
Exercise
Section titled “Exercise”Use a repository you work in regularly.
-
For one working day, note every correction you make to a Copilot suggestion.
-
Write ten lines of repository-wide instructions from that list.
-
Ask for code that would violate one. Predict: does it comply?
-
Add fifty lines of generic advice to the file and repeat step 3. Predict: does compliance improve, stay the same, or get worse?
-
Remove the padding. Add a path-specific file for one directory with different rules. Ask for code inside and outside that directory. Predict: does the scoping work?
-
Try the same instruction in a second surface — the CLI, github.com, or code review. Predict: does it apply?
-
Pair one instruction with a lint rule. Predict: which one actually prevents the violation shipping?