How to Use GitHub Copilot Spaces for Better AI Code Suggestions
I asked Copilot to “Generate a SQL query to find active users.” It gave me three different answers, none of which matched my actual database schema. That’s when I realized: AI is fast, but it isn’t psychic.
The Context Problem
Every developer using AI coding assistants hits the same wall eventually. You type a request, and the AI responds with technically correct but practically useless code. The function names don’t match your conventions. The database queries reference tables that don’t exist. The API calls use outdated patterns.
The issue isn’t the AI’s capability—it’s the missing context.
Your Request: "Generate a REST endpoint for user registration"
Without Context: └─ Generic response using Express.js └─ Standard MongoDB schema └─ Basic validation rules └─ ❌ Doesn't match your stack
With Context (Copilot Spaces): └─ Response using FastAPI (your framework) └─ PostgreSQL schema matching your tables └─ Validation rules from your existing patterns └─ ✅ Production-ready codeGitHub Copilot Spaces solves this by letting you upload documentation, repositories, and instructions that give Copilot context about your specific codebase. It’s like giving your AI assistant a map of your territory before asking for directions.
Setting Up Your First Space
I’ll walk through how I configured Copilot Spaces for a recent project—a Flask API with PostgreSQL and Redis caching.
Step 1: Create a New Space
Navigate to GitHub Copilot in your IDE and select “Spaces” from the menu. Click “Create New Space” and give it a descriptive name.
Space Name: flask-api-projectDescription: Context for Flask REST API with PostgreSQL
Resources to Add: ├─ Repositories: your-org/flask-api-main ├─ Documentation: /docs/architecture.md ├─ Instructions: coding-standards.txt └─ External Links: api-docs.yourservice.comStep 2: Add Your Codebase Context
The most valuable context comes from your actual code. Add repositories that Copilot should understand.
# From your existing codebase: models/user.py
from sqlalchemy import Column, Integer, String, DateTime, Booleanfrom database.base import Base
class User(Base): """User model with soft delete support""" __tablename__ = 'users'
id = Column(Integer, primary_key=True) email = Column(String(255), unique=True, nullable=False) username = Column(String(100), unique=True) created_at = Column(DateTime, default=datetime.utcnow) is_active = Column(Boolean, default=True) deleted_at = Column(DateTime, nullable=True) # Soft delete
def soft_delete(self): self.deleted_at = datetime.utcnow() self.is_active = FalseNow when I ask Copilot to generate a user lookup query, it knows about soft deletes and uses the correct column names.
Step 3: Upload Documentation
Documentation fills gaps that code can’t. I added our API design guidelines and database schema documentation.
docs/├── architecture.md # System design decisions├── api-conventions.md # REST API naming patterns├── database-schema.md # Table relationships└── deployment-guide.md # Environment variablesThe key insight: documentation should explain WHY, not just WHAT. Code shows implementation; documentation shows intent.
Step 4: Define Coding Instructions
Instructions are explicit rules for Copilot to follow. I created a text file with our team’s conventions:
# Project-Specific Instructions
## Error Handling- Use custom exception classes from app/exceptions.py- Always include error codes in API responses- Log errors with context using structlog
## Database Queries- Always check is_active = true for user queries- Use connection pooling via SQLAlchemy engine- Apply rate limiting decorators to all write operations
## API Response Format{ "success": boolean, "data": object | null, "error": { "code": string, "message": string }}
## Import Style- Group imports: standard lib, third-party, local- Use absolute imports from app root- Never use wildcard importsThese instructions create guardrails for Copilot’s suggestions.
Real-World Impact: Before and After
Let me show you the difference context makes with a concrete example.
Request: “Generate a function to get user by email”
Without Copilot Spaces:
def get_user_by_email(email): """Get user by email address""" # Generic implementation return User.query.filter_by(email=email).first()This works but ignores our soft delete pattern and error handling conventions.
With Copilot Spaces context:
from app.exceptions import UserNotFoundErrorfrom app.models import Userfrom app.utils.logging import get_logger
logger = get_logger(__name__)
def get_user_by_email(email: str) -> User: """ Retrieve active user by email address.
Args: email: User's email address
Returns: User object
Raises: UserNotFoundError: If user doesn't exist or is deleted """ user = User.query.filter_by( email=email, is_active=True ).first()
if not user: logger.warning("User not found", email=email) raise UserNotFoundError(f"No active user found with email: {email}")
logger.info("User retrieved successfully", user_id=user.id) return userThe context-aware version includes:
- Type hints (our standard)
- Soft delete awareness (from our schema)
- Custom exception (from our exceptions module)
- Structured logging (from our conventions)
- Docstring format (from our style guide)
Best Practices for Context Curation
After using Copilot Spaces for several months, I’ve learned what works:
Quality Over Quantity
More context isn’t always better. Focus on relevant, up-to-date information.
Essential (Always Include): ├─ Database schemas and models ├─ Core utility functions ├─ Error handling patterns └─ API conventions
Valuable (Include When Relevant): ├─ Architecture decision records ├─ Third-party integrations └─ Complex business logic
Avoid (Creates Noise): ├─ Auto-generated files ├─ Test fixtures ├─ Obsolete documentation └─ Comment-heavy legacy codeKeep Context Synchronized
Stale context is worse than no context. I set up a simple check:
# Run weekly to verify documentation matches code#!/bin/bashpython scripts/verify_schema_docs.pypython scripts/check_api_conventions.pyUse Multiple Spaces for Different Contexts
Different tasks need different context. I maintain separate spaces:
Spaces: ├─ flask-api-project # Full project context ├─ frontend-components # React component patterns ├─ database-migrations # Schema change history └─ testing-utils # Test helpers and fixturesUnderstanding Context Limits
Copilot Spaces isn’t magic—it has constraints you should know about.
Token Limits
Every piece of context consumes tokens. GitHub doesn’t publish exact limits, but I’ve noticed degradation when adding too much:
Safe Zone: └─ 1-2 repositories (~50k tokens) └─ Essential documentation (~10k tokens) └─ Instructions (~5k tokens)
Risk Zone: └─ 5+ repositories └─ Extensive documentation └─ Redundant context files
Result of Overloading: └─ Slower suggestions └─ Irrelevant code snippets └─ Missed critical patternsContext Relevance Scoring
Copilot ranks context relevance automatically. Recent or frequently referenced code gets higher priority. I organize my repository structure to support this:
src/├── models/ # High relevance - referenced often├── services/ # High relevance - business logic├── utils/ # Medium relevance - helper functions├── migrations/ # Low relevance - historical record└── tests/ # Exclude from space - noiseMeasuring Improvement
To justify the setup time, I tracked suggestion quality over two weeks:
Before Copilot Spaces: ├─ Acceptance rate: 28% ├─ Edits required: 4.2 per suggestion └─ Time saved: ~15 minutes/day
After Copilot Spaces: ├─ Acceptance rate: 52% ├─ Edits required: 1.8 per suggestion └─ Time saved: ~45 minutes/day
Setup time: 2 hoursBreak-even point: Day 4The improvement wasn’t just in quantity—suggestions aligned with our architecture, reducing code review cycles from 3-4 rounds to 1-2 rounds.
Advanced Context Techniques
Instruction Templates
I created reusable instruction templates for common patterns:
# API Endpoint Generation Template
Generate endpoints following this structure:
1. Route definition with version prefix2. Request validation using marshmallow schemas3. Service layer call with error handling4. Response formatting per API standards5. Logging at appropriate levels
Example request: [YOUR REQUEST HERE]When I need a new endpoint, I append my specific request to this template and paste it into Copilot.
Context Inheritance
Spaces can reference other spaces for shared context:
Base Space: company-standards └─ Logging conventions └─ Error handling patterns └─ Security requirements
Derived Space: flask-api-project ├─ Inherits: company-standards └─ Adds: Project-specific models
Derived Space: frontend-app ├─ Inherits: company-standards └─ Adds: React component libraryThis avoids duplicating common instructions across projects.
When Context Backfires
Too much or wrong context creates problems. I learned this the hard way.
The Stale Schema Problem
I added our database schema documentation but forgot to update it after a migration. For two weeks, Copilot generated queries referencing a users.password_hash column that we’d renamed to users.password_digest.
-- Generated by Copilot (wrong due to stale docs)SELECT id, email, password_hash FROM users WHERE is_active = true;The fix was simple—update the documentation—but it highlighted a real risk: context requires maintenance.
The Over-Specificity Trap
I once added too much business logic context. Every suggestion became over-engineered, trying to handle edge cases I didn’t need for simple prototypes.
For Prototyping: └─ Minimal context: just language and framework └─ Focus: speed over correctness
For Production Code: └─ Full context: schemas, patterns, conventions └─ Focus: correctness and maintainabilityI now create lightweight “prototype spaces” for experimentation.
Integration with Development Workflow
Copilot Spaces works best when integrated into your existing tools.
IDE Configuration
Most IDEs supporting GitHub Copilot also support Spaces:
VS Code: ├─ Settings: GitHub Copilot > Spaces └─ Shortcut: Cmd+Shift+Space (switch space)
JetBrains IDEs: ├─ Settings: Tools > GitHub Copilot └─ Action: "Copilot: Select Space"
Neovim (with copilot.lua): require('copilot').setup({ spaces = { default = "flask-api-project" } })Team Collaboration
Spaces can be shared across team members, ensuring consistent code generation:
1. Create space with project lead2. Export space configuration3. Import to team members' IDEs4. Version control the configuration5. Update through PR reviewsThis ensures everyone gets suggestions matching team standards.
Future of Context-Aware AI Coding
Copilot Spaces represents a broader trend: AI tools need project-specific knowledge to be truly useful. We’re seeing similar approaches across the industry:
- Cursor’s “codebase indexing” feature
- Claude’s “project context” functionality
- Codeium’s “context awareness” system
The common theme: generic AI models become specialized through context injection.
2022: AI assistants with generic training └─ One-size-fits-all suggestions
2024: Context-aware tools emerge └─ Customizable context sources └─ Project-specific knowledge
2026: Semantic context understanding └─ Auto-discovered patterns └─ Real-time context updates └─ Cross-project learningAs these tools mature, the skill of curating and maintaining context will become as important as writing the code itself.
Key Takeaways
- Context is the missing link between generic AI and useful code suggestions
- Quality beats quantity—curate relevant, current context
- Maintenance matters—stale context creates bugs
- Measure improvement—track acceptance rates to validate setup time
- Match context to task—use different spaces for prototyping vs. production
The setup investment pays off quickly. In my experience, a well-configured Copilot Space saves 30-45 minutes daily and produces code that passes review on the first or second attempt.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments