Most copilot-instructions.md files are too long, too vague, or too generic. The ones that work are short, specific, and testable.

The 30-second version: A good custom instructions file is under 500 words, tells Copilot about your project’s specific conventions (not general advice), and can be verified. A bad instruction file is a wall of text that says “write clean code” and “use best practices.” Copilot already knows that. Tell it what your codebase does differently.

⚠️ This page assumes you know what instruction files are. If you haven’t read that page, start there. This page is about how to write good custom instructions, not what instruction files are.

GitHub’s official tutorial covers how to use custom instructions. This page builds on that foundation with practical patterns for team adoption, testing your instructions, and avoiding common anti-patterns.

The difference between instructions that help vs instructions that are ignored

Copilot reads your instruction file as part of its prompt context. But context windows are finite. If your instruction file is 2000 words, Copilot will skim it. If it’s generic advice (“write clean code”), Copilot already has that in its training data and will ignore it.

Instructions that help:

  • Tell Copilot something it doesn’t already know
  • Are specific to your codebase
  • Can be verified by a test or linter

Instructions that are ignored:

  • Repeat general advice (“follow best practices”)
  • Are too vague to act on (“make sure the code is maintainable”)
  • Contradict each other (“always use TypeScript strict mode” + “use any when types are complex”)

Personal custom instructions vs repo custom instructions

There are two places you can put custom instructions:

  1. Personal custom instructions (applies to all your projects)
  2. Repo custom instructions (applies to one specific repo)
FileScopeWhat to put there
Personal settings in your IDEGlobal — all your projectsYour personal coding style, preferred frameworks, languages you use
.github/copilot-instructions.mdThis repo onlyTech stack, naming conventions, project structure, testing rules

Use personal custom instructions for:

  • “I prefer functional programming over OOP”
  • “Always use Python type hints”
  • “Prefer React functional components over class components”

Use repo custom instructions for:

  • “API routes are in /src/routes/api/”
  • “All database queries use Prisma — never raw SQL”
  • “Test files must be colocated with source files (e.g., Button.tsx + Button.test.tsx)”

Team adoption strategies

Custom instructions become most powerful when adopted across a team. Here’s how to get buy-in:

Start with one high-impact rule
Don’t write 50 rules on day one. Start with one thing that causes the most PR review friction (e.g., “Use Tailwind classes, not inline styles”). Get the team to agree on it. Add it to .github/copilot-instructions.md. Monitor whether Copilot follows it.

Make it a living document
Custom instructions go stale. Schedule quarterly reviews. When a new convention emerges (e.g., migrating from Jest to Vitest), update the file in the same PR that updates the code.

Test before you commit
Before merging custom instructions changes, test them. Write a prompt that should trigger the new rule and verify Copilot follows it. If it doesn’t, refine the instruction.

Version it like code
Treat .github/copilot-instructions.md as code. Require PR reviews. Discuss changes in team meetings. This file shapes what Copilot generates — it’s infrastructure, not documentation.

What Copilot actually reads carefully vs what it skims

Copilot’s context window is limited. It prioritizes:

  1. The current file you’re editing (highest priority)
  2. Files you have open in your IDE
  3. Instruction files (this is where your instructions fit)
  4. Other files in the workspace (if relevant)

Within the instruction file, Copilot reads:

  • The first 200–300 words most carefully — put your most important rules at the top
  • Bulleted lists — easier to scan than paragraphs
  • Code examples — concrete and unambiguous

Copilot skims or ignores:

  • Long paragraphs — hard to parse quickly
  • Redundant advice — “always write tests” + “testing is important” is one rule, not two
  • Negatives without alternatives — “don’t use callbacks” is less useful than “use async/await instead of callbacks”

Anti-patterns: what NOT to do

Anti-pattern 1: Walls of text

Bad example:

This project uses TypeScript and we have very strict rules about how TypeScript 
should be used. Always use strict mode. Never use any. If you don't know the type, 
use unknown instead of any. Always define interfaces for objects. Prefer type over 
interface unless you need to extend it. Use readonly for properties that don't change. 
Always use const instead of let unless you need reassignment...

Why it fails: This is 100 words and covers 8 different rules. Copilot will skim it and remember maybe 2 of them.

Good version:

## TypeScript Rules
- Use `strict: true` in tsconfig.json
- Never use `any` — use `unknown` if type is unclear
- Prefer `type` over `interface` (unless extending)
- All objects must have explicit types

Why this works: 4 rules, bulleted, scannable. Copilot can read this in one glance.

Anti-pattern 2: Listing everything

Bad example:

We use React, TypeScript, Tailwind, Prisma, Next.js, ESLint, Prettier, Jest, 
React Testing Library, Storybook, Docker, GitHub Actions, and AWS.

Why it fails: This is a dependency list, not instructions. Copilot can see your package.json — it already knows your stack.

Good version:

## Stack Notes
- Next.js: All API routes are in `pages/api/` — do NOT put them in `src/routes/`
- Tailwind: Use Tailwind classes, not inline styles or CSS modules
- Prisma: All database queries use Prisma Client — never raw SQL

Why this works: These are conventions specific to this project. Copilot can’t infer these from package.json.

Anti-pattern 3: Using negatives without alternatives

Bad example:

- Don't use callbacks
- Don't use class components
- Don't use var

Why it fails: These tell Copilot what not to do, but not what to do instead.

Good version:

- Use async/await instead of callbacks
- Use functional components instead of class components
- Use const or let instead of var

Why this works: Copilot now knows the alternative. This is actionable.

Good patterns: specific, actionable, short, verified

Pattern 1: Start with tech stack differences

Don’t list your entire stack. List the parts that are non-standard or likely to trip up Copilot.

Example:

## Tech Stack Notes
- This project uses Deno, not Node.js — imports must use full file extensions (`.ts`)
- We use htmx, not React — server-rendered HTML with htmx attributes for interactivity
- Database: SQLite in dev, PostgreSQL in production — use only standard SQL features

Pattern 2: Codify naming conventions

If your team has naming conventions, write them down. Copilot will follow them.

Example:

## Naming Conventions
- React components: PascalCase (`UserProfile.tsx`)
- Utility functions: camelCase (`formatDate.ts`)
- Test files: colocated, `.test.ts` suffix (`formatDate.test.ts`)
- API routes: kebab-case (`/api/user-preferences`)

Pattern 3: Specify testing requirements

If you have rules about when to write tests, make them explicit.

Example:

## Testing Rules
- All exported functions must have unit tests
- API routes must have integration tests
- Do NOT test internal helper functions unless they have complex logic
- Run `npm test` before committing — all tests must pass

Pattern 4: Use code examples for clarity

If a rule is hard to explain in words, show a code example.

Example:

## Error Handling Pattern
Always wrap errors in our custom error classes:

```typescript
// Good
throw new NotFoundError('User not found', { userId });

// Bad
throw new Error('User not found');
```

All API routes must catch errors and return standardized JSON:

```typescript
try {
  // route logic
} catch (error) {
  return res.status(error.statusCode || 500).json({
    error: error.message,
    code: error.code
  });
}
```

This is unambiguous. Copilot can copy the pattern exactly.

How to test your instructions

After writing your instruction file, test it:

  1. Write a prompt that should trigger the instruction
    Example: “Add a new API route for user preferences”

  2. Run it in agent mode or chat
    See if Copilot follows your instructions

  3. Check the output
    Did it follow your naming convention? Did it write tests? Did it use the right error handling pattern?

  4. Iterate
    If Copilot ignored a rule, either:

    • Move it higher in the file (top rules get read first)
    • Make it more specific (vague rules get ignored)
    • Add a code example (concrete examples are easier to follow)

Versioning and maintenance: custom instructions go stale

⚠️ Custom instructions go stale. Your codebase changes. New conventions emerge. Old conventions get deprecated. If your custom instructions say “use callbacks” but your codebase now uses async/await, Copilot will be confused.

Maintenance tips:

  • Review your custom instructions every 3–6 months — does it still match your codebase?
  • Update it when conventions change — if you migrate from Jest to Vitest, update the testing section
  • Version it in git — custom instructions are code. They should be reviewed in PRs.
  • Keep it short — the longer it is, the harder it is to maintain. Aim for under 500 words.
  • Track what’s not working — if Copilot consistently ignores a rule, either the rule is too vague or it contradicts another rule. Debug it like you’d debug code.

Example: a complete, effective custom instructions file

Here’s a real example of a well-structured .github/copilot-instructions.md file for a team:

# Copilot Instructions for acme-api

## Tech Stack
- **Language:** TypeScript 5.0 (strict mode enabled)
- **Framework:** Next.js 14 (App Router, not Pages Router)
- **Database:** Prisma with PostgreSQL
- **Testing:** Jest + React Testing Library

## Code Conventions
- **React components:** Functional components only, no class components
- **Naming:** PascalCase for components, camelCase for utilities, kebab-case for API routes
- **File structure:** Colocate tests with source files (`Button.tsx` + `Button.test.tsx`)
- **Imports:** Use `@/` alias for absolute imports (e.g., `import { db } from '@/lib/db'`)

## Database Rules
- All queries use Prisma Client — never raw SQL
- Always use transactions for multi-step writes
- Include `include` clauses explicitly — avoid lazy loading

## Testing Requirements
- All API routes must have integration tests
- All exported functions must have unit tests
- Run `npm test` before committing

## Error Handling
Use custom error classes from `@/lib/errors`:

~~~typescript
throw new NotFoundError('User not found', { userId });
~~~

API routes must return standardized error JSON:

~~~typescript
catch (error) {
  return NextResponse.json(
    { error: error.message, code: error.code },
    { status: error.statusCode || 500 }
  );
}
~~~

## What NOT to Do
- Don't use `any` — use `unknown` if type is unclear
- Don't bypass TypeScript strict mode with `@ts-ignore`
- Don't commit directly to `main` — always use a feature branch + PR

Why this works:

  • Short: 250 words
  • Specific: Every rule is actionable
  • Verifiable: You can check if code follows these rules
  • Concrete: Code examples are unambiguous
  • Maintained: Versioned in git, reviewed in PRs

Dive deeper

Related Concepts

📍 Layer 4a — Instructions (effective patterns) · Back to the AICA Onion

What’s next?

Updated: