← Back to Blog

Managing GitHub Copilot Instructions Across 50 Repos Without Losing Your Mind

E2E Agentic Bridge·March 5, 2026

Managing GitHub Copilot Instructions Across 50 Repos Without Losing Your Mind

GitHub Copilot reads a file called .github/copilot_instructions.md in every repository. Write good instructions, and Copilot generates code that follows your conventions. Write nothing, and Copilot guesses — usually wrong.

The problem isn't writing instructions for one repo. It's maintaining them across 50. Or 200. Every repo needs the same security rules, the same coding standards, the same testing conventions. Copy-paste them manually, and they drift within a week. Someone updates the Node.js repo but forgets the Python one. Security adds a new rule but only tells three teams.

This article is the technical deep-dive into solving this. No theory — just the architecture, the code, and the gotchas we've learned from teams running this in production.

The Architecture: Central Repo + Submodules + Actions

The system has three components:

  1. Central standards repo — one source of truth for all Copilot instructions
  2. Git submodules — every repo links to the central repo
  3. GitHub Actions — automated daily sync and commit-level compliance

Here's how they connect:

┌─────────────────────────┐
│  copilot-standards      │  ← Central repo (one per org)
│  ├── instructions/      │
│  ├── skills/            │
│  └── actions/           │
└──────────┬──────────────┘
           │ submodule
     ┌─────┼──────┬──────────┐
     ▼     ▼      ▼          ▼
  repo-1  repo-2  repo-3  repo-N
  (.github/copilot_instructions.md)

Step 1: Structure Your Central Repo

Don't dump everything into one file. Split instructions by concern:

copilot-standards/
├── instructions/
│   ├── 00-global.md          # Universal rules
│   ├── 01-security.md        # Security requirements
│   ├── 02-testing.md         # Testing standards
│   ├── 03-error-handling.md  # Error patterns
│   └── 04-api-design.md     # API conventions
├── stacks/
│   ├── typescript-next.md    # Next.js + TypeScript stack
│   ├── python-fastapi.md     # Python + FastAPI stack
│   └── go-grpc.md           # Go + gRPC stack
├── skills/
│   ├── pr-review.md          # How Copilot should review PRs
│   └── refactor.md          # Refactoring guidelines
├── scripts/
│   ├── build-instructions.sh # Assembles per-repo instructions
│   └── validate.sh          # Checks instruction format
└── .github/
    └── workflows/
        └── distribute.yml    # Syncs to all repos

The numbered prefixes in instructions/ control assembly order. Security comes before testing. Global rules come first.

Writing Effective Instructions

Bad instructions:

Write good code. Follow best practices. Be secure.

Good instructions:

## Error Handling
- Wrap all async operations in try/catch blocks
- Use custom error classes that extend BaseError
- Include error codes: ERR_AUTH_FAILED, ERR_NOT_FOUND, ERR_VALIDATION
- Log errors with structured format: { code, message, context, timestamp }
- Never expose internal error details to API consumers
- Return RFC 7807 Problem Details format for HTTP errors

## Database
- Use parameterized queries exclusively — never concatenate SQL strings
- All queries must have a timeout of 30 seconds max
- Use connection pooling — max 20 connections per service
- Read replicas for GET endpoints, primary for writes
- All migrations must be reversible

Be specific. Copilot follows concrete rules much better than vague principles.

Step 2: The Build Script

Each repo needs a single .github/copilot_instructions.md file. A build script assembles it from the central repo's components:

#!/bin/bash
# scripts/build-instructions.sh
# Assembles copilot_instructions.md for a specific repo

REPO_TYPE="${1:-typescript-next}"  # Stack type
OUTPUT=".github/copilot_instructions.md"
STANDARDS_DIR=".copilot-standards"

echo "# Copilot Instructions (auto-generated)" > "$OUTPUT"
echo "# Do not edit — managed by copilot-standards" >> "$OUTPUT"
echo "# Last updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$OUTPUT"
echo "" >> "$OUTPUT"

# 1. Global instructions (numbered, in order)
for f in "$STANDARDS_DIR"/instructions/[0-9]*.md; do
  [ -f "$f" ] && cat "$f" >> "$OUTPUT" && echo -e "\n" >> "$OUTPUT"
done

# 2. Stack-specific instructions
STACK_FILE="$STANDARDS_DIR/stacks/$REPO_TYPE.md"
if [ -f "$STACK_FILE" ]; then
  echo "## Stack-Specific Standards" >> "$OUTPUT"
  cat "$STACK_FILE" >> "$OUTPUT"
  echo -e "\n" >> "$OUTPUT"
fi

# 3. Repo-specific overrides (local, not from central)
LOCAL_FILE=".github/copilot_instructions.local.md"
if [ -f "$LOCAL_FILE" ]; then
  echo "## Repo-Specific Instructions" >> "$OUTPUT"
  cat "$LOCAL_FILE" >> "$OUTPUT"
fi

echo "Built $OUTPUT ($(wc -w < "$OUTPUT") words)"

Teams can still add repo-specific rules in .github/copilot_instructions.local.md — the build script appends them after the global and stack instructions.

Step 3: Submodule Setup

Add the central repo as a submodule in every project:

git submodule add https://github.com/your-org/copilot-standards .copilot-standards
git commit -m "chore: add copilot standards submodule"

Then run the build script:

bash .copilot-standards/scripts/build-instructions.sh typescript-next
git add .github/copilot_instructions.md
git commit -m "chore: build copilot instructions"

The Repo Manifest

How does the sync Action know which stack each repo uses? A manifest file in the central repo:

# repos.yml
repositories:
  - name: api-service
    stack: typescript-next
    extras: [security-strict]
  - name: data-pipeline
    stack: python-fastapi
    extras: []
  - name: mobile-backend
    stack: go-grpc
    extras: [security-strict, hipaa]
  - name: marketing-site
    stack: typescript-next
    extras: []

Step 4: The Daily Sync Action

This runs in the central repo every morning:

name: Distribute Copilot Standards
on:
  schedule:
    - cron: '0 6 * * 1-5'  # 6 AM UTC, weekdays
  push:
    branches: [main]        # Also on merge
  workflow_dispatch:         # Manual trigger

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Read manifest
        id: repos
        run: |
          echo "matrix=$(yq -o=json '.repositories' repos.yml | jq -c '.')" >> "$GITHUB_OUTPUT"

      - name: Sync to repos
        env:
          GH_TOKEN: ${{ secrets.ORG_SYNC_PAT }}
        run: |
          REPOS=$(yq '.repositories[].name' repos.yml)
          for REPO in $REPOS; do
            STACK=$(yq ".repositories[] | select(.name == \"$REPO\") | .stack" repos.yml)
            echo "Syncing $REPO (stack: $STACK)..."

            git clone --depth 1 "https://x-access-token:${GH_TOKEN}@github.com/your-org/$REPO.git" "/tmp/$REPO"
            cd "/tmp/$REPO"
            git submodule update --init --remote .copilot-standards 2>/dev/null || \
              git submodule add "https://x-access-token:${GH_TOKEN}@github.com/your-org/copilot-standards.git" .copilot-standards

            bash .copilot-standards/scripts/build-instructions.sh "$STACK"

            if ! git diff --quiet; then
              git add -A
              git commit -m "chore: sync copilot standards ($(date +%Y-%m-%d))"
              git push
              echo "✅ $REPO updated"
            else
              echo "⏭️ $REPO unchanged"
            fi
            cd -
          done

When you merge a change to copilot-standards, every repo gets updated within minutes. The daily schedule is a safety net for repos that might have missed a push event.

Step 5: Compliance Checks on Every Commit

Add this Action to every repo (distribute it via the central repo too):

name: Copilot Standards Compliance
on: [push, pull_request]

jobs:
  compliance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true

      - name: Check instructions exist
        run: |
          if [ ! -f .github/copilot_instructions.md ]; then
            echo "::error::Missing .github/copilot_instructions.md"
            echo "Run: bash .copilot-standards/scripts/build-instructions.sh"
            exit 1
          fi

      - name: Check instructions are current
        run: |
          bash .copilot-standards/scripts/build-instructions.sh "$(cat .copilot-stack 2>/dev/null || echo typescript-next)"
          if ! git diff --quiet .github/copilot_instructions.md; then
            echo "::warning::Copilot instructions are outdated. The daily sync will fix this, or run the build script manually."
          fi

      - name: Validate instruction format
        run: bash .copilot-standards/scripts/validate.sh

This catches two cases:

  1. Someone deleted or corrupted the instructions file → build fails
  2. Instructions are outdated → warning (non-blocking, daily sync fixes it)

Gotchas and Lessons Learned

Instruction size matters. Copilot has context limits. If your assembled instructions exceed ~8,000 words, Copilot may truncate them. Keep instructions concise and prioritized — the numbered prefix system ensures the most important rules come first.

Don't over-specify. If you dictate every variable name and every pattern, developers fight the system. Focus on security, architecture, and conventions that prevent real bugs. Let Copilot handle style within those guardrails.

Version your standards. Tag releases in the central repo (v1.0, v1.1). If a standard change breaks something, you can pin repos to a specific version while you fix it.

Monitor adoption. Having the infrastructure is step one. Knowing whether it's working is step two. Are developers actually getting Copilot suggestions that follow your standards? Are the suggestions being accepted or dismissed?

Measuring the Impact

You built the governance stack. How do you know it's working?

You need visibility into three things:

  1. Copilot adoption — are developers actually using it?
  2. Suggestion acceptance rates — are the governed suggestions useful?
  3. Code quality metrics — are fewer standards violations getting through review?

CopilotScan gives you the first two. A free, 5-minute read-only scan of your Microsoft 365 tenant that shows Copilot utilization rates, licensing efficiency, and security posture. It's the baseline measurement you need before and after deploying governance.

Scan your tenant for free →

Build the governance stack. Measure the results. Iterate.


Related reading: