Moving beyond prompt crafting with GitHub Copilot
When developers first start using GitHub Copilot, much of the focus lands on prompt crafting — the skill of providing good context and information to get quality suggestions. But context doesn't stop at typing a couple of lines into Copilot Chat in VS Code. To get the most out of Copilot, it's important to make sure it considers the right files when performing operations, that those files are easy for Copilot to read, and that Copilot has any extra guidance it might need about the project or the specific task at hand.
How Copilot reads your code
Aside from agent mode — which performs external tasks — Copilot doesn't build or run your code as it generates suggestions. Instead, it behaves like a pair programmer: it reads the code and comments of the files you've pointed it at, much as another developer would.
But unlike a human teammate, Copilot doesn't have "institutional knowledge" — the background information that comes from experience. It doesn't know why things were built a certain way when that reasoning isn't documented anywhere, which internal libraries or frameworks should always be used, or what patterns need to be followed. This background matters for generating useful suggestions. For instance, if you're using a data abstraction layer (DAL) but Copilot is generating raw SQL, the suggestions aren't helpful — not because the code is invalid, but because Copilot lacks the context to generate code in the format and structure your project needs.
The role of comments in Copilot's suggestions
There's a common belief that quality code shouldn't need comments and that adding them signals a "code smell." But even when code is as readable as possible, what's clear to one developer isn't necessarily clear to all developers. And since Copilot reads code like a developer would, a few lines of comments can significantly improve its understanding.
Following established documentation conventions, like using docstrings for Python functions and modules, helps Copilot understand what code does and how it does it. This, in turn, allows Copilot to generate higher-quality suggestions by following the same patterns already in place in your codebase.
| 💡 Pro tip: When you open a file, it’s always a good idea to leave it in a better state than when you found it. One of the little improvements you could make is to add a few comments to places to help describe the code. You could always ask Copilot to generate the first draft of the comments, and you can add any additional details Copilot missed! |
Project-wide context with custom instructions
Copilot benefits from knowing the technology and frameworks you're using, the coding standards to follow, and background about what you're building. Custom instructions let you set these ground rules — specifying things like which APIs to call, naming patterns, and stylistic preferences.
To get started, place this guidance in a file named copilot-instructions.md inside your .github folder. As a markdown file, it can include sections like Project structure, Technologies, and Coding standards, plus guidance for tasks where Copilot might choose the wrong approach.
Keep in mind that custom instructions are included with every single chat request, so stick to information relevant to the whole project. Too much detail can make it harder for Copilot to determine what's important. Provide project-level guidance and overviews so it best understands the environment it's working in. An outline for a monorepo with client and server components for a web app might look like this:
# Tailspin Toys Crowd Funding
Website for crowd funding for games.
## Backend
The backend is written using:
- Flask for the API
- SQLAlchemy for the ORM
- SQLite for the database
## Frontend
The frontend is written using:
- Astro for routing
- Svelte for the components and interactivity
- Tailwind CSS for styling
## Code standards
- Use good variable names, avoiding abbreviations and single letter variables
- Use the casing standard for the language in question (camelCasing for TypeScript, snake_casing for Python, etc.)
- Use type hints in all languages which support them
## Project structure
- `client` contains the frontend code
- `docs` contains the documentation for the project
- `scripts` contains the scripts used to install services, start the app, and run tests
- `server` contains the backend code
This tells Copilot about the project, its structure, the technologies in use, and how you want code created — without getting into task-specific details like how to write unit tests. There's a separate mechanism for that.
Task-specific instructions with .instructions.md files
VS Code and Codespaces support .instructions.md files, which work like the repository-level copilot-instructions.md file but are scoped to specific types of tasks. You place them in .github/instructions.
For example, in a project building Flask Blueprints for API routes, you might have requirements for how files should be structured and how tests should be written. You could create a file called flask-endpoint.instructions.md, add it as context when asking Copilot to create a new endpoint, and include specific details about how endpoints should be created. These instruction files can include hyperlinks to other files in the project — both existing files for Copilot to use as examples and other instruction files for additional context.
# Endpoint creation guidelines
## Endpoint notes
- Endpoints are created in Flask using blueprints
- Create a centralized function for accessing data
- All endpoints require tests
- Use the `unittest` module for testing
- All tests must pass
- A script is provided to run tests at `scripts/run-server-tests.sh`
## Project notes
- The Python virtual environment is located in the root of the project in a **venv** folder
- Register all blueprints in `server/app.py`
- Use the [test instructions](./python-tests.instructions.md) when creating tests
## Prototype files
- [Endpoint prototype](../../server/routes/games.py)
- [Tests prototype](../../server/tests/test_games.py)
You can also associate instructions with file types based on naming patterns. If tests live in server/tests and begin with test_, add metadata to the top of your instruction file to ensure Copilot always includes these instructions when working on a test file:
---
applyTo: server/tests/test_*.py
---
This offers flexibility in ensuring Copilot has the right information at the right time. You can pull in instruction files explicitly by adding them to the chat, or implicitly through pattern-matching. Because these files live in your repository as artifacts, it takes time to build a collection — but the investment pays off in higher-quality code and better developer productivity.
Extending Copilot with MCP servers
To ensure information is accurate and up to date, Model Context Protocol (MCP) lets organizations expose their services or data to generative AI tools. When you add an MCP server to your IDE, Copilot can "phone a friend" for information or perform tasks on your behalf. For example, the Playwright MCP server assists with creating end-to-end Playwright tests, while the GitHub MCP server provides access to GitHub services like repositories, issues, and pull requests.
If you add the Playwright MCP server to your IDE and ask Copilot to create a test validating functionality on your website, Copilot consults an authoritative source before generating code. You can even build your own MCP servers — for instance, a custom server with a facade that lets Copilot search an internal codebase or suite of libraries and suggest code based on your internal environment.
Rethinking what "good prompt" means
Prompt crafting is a foundational skill for anyone working with Copilot. But a well-crafted prompt is only one element in what Copilot considers when generating an answer. By combining good code comments, repository- and task-level custom instructions, and MCP servers where needed, you can help Copilot understand not just what you want it to build, but how you want it built.



