Skip to content

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.

the generic suggestion problem
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 code

GitHub 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 configuration options
Space Name: flask-api-project
Description: 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.com

Step 2: Add Your Codebase Context

The most valuable context comes from your actual code. Add repositories that Copilot should understand.

example context from repository
# From your existing codebase: models/user.py
from sqlalchemy import Column, Integer, String, DateTime, Boolean
from 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 = False

Now 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.

documentation structure example
docs/
├── architecture.md # System design decisions
├── api-conventions.md # REST API naming patterns
├── database-schema.md # Table relationships
└── deployment-guide.md # Environment variables

The 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:

coding-instructions.txt
# 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 imports

These 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:

without-context-generic.py
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:

with-context-production.py
from app.exceptions import UserNotFoundError
from app.models import User
from 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 user

The 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.

context prioritization framework
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 code

Keep Context Synchronized

Stale context is worse than no context. I set up a simple check:

context sync check
# Run weekly to verify documentation matches code
#!/bin/bash
python scripts/verify_schema_docs.py
python scripts/check_api_conventions.py

Use Multiple Spaces for Different Contexts

Different tasks need different context. I maintain separate spaces:

space organization strategy
Spaces:
├─ flask-api-project # Full project context
├─ frontend-components # React component patterns
├─ database-migrations # Schema change history
└─ testing-utils # Test helpers and fixtures

Understanding 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:

practical token management
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 patterns

Context Relevance Scoring

Copilot ranks context relevance automatically. Recent or frequently referenced code gets higher priority. I organize my repository structure to support this:

folder organization for context discovery
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 - noise

Measuring Improvement

To justify the setup time, I tracked suggestion quality over two weeks:

suggestion quality metrics
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 hours
Break-even point: Day 4

The 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:

instruction-template-api-endpoint.txt
# API Endpoint Generation Template
Generate endpoints following this structure:
1. Route definition with version prefix
2. Request validation using marshmallow schemas
3. Service layer call with error handling
4. Response formatting per API standards
5. 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:

space hierarchy
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 library

This 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.

incorrect suggestion from stale context
-- 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.

context scope matching
For Prototyping:
└─ Minimal context: just language and framework
└─ Focus: speed over correctness
For Production Code:
└─ Full context: schemas, patterns, conventions
└─ Focus: correctness and maintainability

I 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:

IDE integration points
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:

team space workflow
1. Create space with project lead
2. Export space configuration
3. Import to team members' IDEs
4. Version control the configuration
5. Update through PR reviews

This 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.

context evolution timeline
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 learning

As these tools mature, the skill of curating and maintaining context will become as important as writing the code itself.

Key Takeaways

  1. Context is the missing link between generic AI and useful code suggestions
  2. Quality beats quantity—curate relevant, current context
  3. Maintenance matters—stale context creates bugs
  4. Measure improvement—track acceptance rates to validate setup time
  5. 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