GitHub Copilot is one of the most popular AI coding assistants, but many developers don't know how to configure it for consistent, high-quality suggestions. While Copilot doesn't have a native .cursorrules file like Cursor, you can achieve similar results using workspace instructions and strategic comments.
This guide provides ready-to-use rules that make Copilot work better for your projects.
Before diving into rules, understand how Copilot gets context:
.github/copilot-instructions.md if presentThe key is placing instructions where Copilot can find them.
Create .github/copilot-instructions.md in your repository root:
# GitHub Copilot Instructions
This file provides context for GitHub Copilot to generate better code suggestions for this project.
[Your rules go here]
Copilot will automatically reference this file when generating suggestions.
## Code Style
- Use descriptive variable names (no single letters except loop counters)
- Prefer functional programming patterns over imperative
- Keep functions under 50 lines
- Maximum 3 levels of nesting
- Use early returns to reduce nesting
- Write self-documenting code that minimizes need for comments
## TypeScript/JavaScript Standards
- Always use TypeScript strict mode
- Prefer `const` over `let`, never use `var`
- Use named exports instead of default exports
- Async functions should always handle errors with try-catch
- Use optional chaining (`?.`) and nullish coalescing (`??`)
- Prefer arrow functions for callbacks and short functions
- Use template literals instead of string concatenation
### Type Definitions
- Define interfaces for all object shapes
- Use `type` for unions and intersections
- Avoid `any`, use `unknown` when type is truly unknown
- Make types as specific as possible
## React Conventions
- Use functional components only (no class components)
- Hooks must follow React rules (top level, consistent order)
- Extract custom hooks for reusable stateful logic
- Use TypeScript interfaces for component props
- Prefer named exports for components
- Keep components single-purpose and focused
- Use composition over inheritance
### Component Structure
```typescript
// Preferred component structure:
interface Props {
prop1: string;
prop2: number;
}
export const ComponentName = ({ prop1, prop2 }: Props) => {
// Hooks first
const [state, setState] = useState();
// Event handlers
const handleClick = () => {};
// Render
return <div>...</div>;
};
### Python Rules
``` markdown
## Python Standards
- Follow PEP 8 style guide
- Use type hints for all function signatures
- Prefer list comprehensions over map/filter when readable
- Use f-strings for string formatting
- Handle exceptions explicitly, never bare `except:`
- Use context managers (`with` statement) for resource management
- Docstrings for all public functions (Google style)
### Example Function
```python
def process_data(items: list[dict]) -> list[str]:
"""
Process a list of data items and return formatted strings.
Args:
items: List of dictionaries containing data to process
Returns:
List of formatted string representations
Raises:
ValueError: If items list is empty
"""
if not items:
raise ValueError("Items list cannot be empty")
return [f"{item['name']}: {item['value']}" for item in items]
### Testing Rules
```markdown
## Testing Standards
- Write tests for all business logic
- Use descriptive test names that explain what is being tested
- Follow Arrange-Act-Assert pattern
- Test happy path, edge cases, and error conditions
- Aim for 80% code coverage minimum
- Mock external dependencies
- Keep tests independent and idempotent
### Test Structure (Jest/Vitest)
```typescript
describe('ComponentName', () => {
it('should render correctly with valid props', () => {
// Arrange
const props = { name: 'Test' };
// Act
render(<ComponentName {...props} />);
// Assert
expect(screen.getByText('Test')).toBeInTheDocument();
});
it('should handle error state gracefully', () => {
// Test error condition
});
});
### Error Handling Rules
```markdown
## Error Handling
- Always use try-catch for async operations
- Provide descriptive error messages with context
- Log errors with appropriate severity levels
- Never swallow errors silently
- Return meaningful error responses to users
- Use custom error classes for domain-specific errors
### Example Pattern
```typescript
try {
const result = await riskyOperation();
return { success: true, data: result };
} catch (error) {
console.error('Operation failed:', error);
return {
success: false,
error: 'Failed to complete operation. Please try again.'
};
}
### API Design Rules
```markdown
## API Design Standards
- Use RESTful conventions for HTTP APIs
- Consistent response format across all endpoints
- Proper HTTP status codes (200, 201, 400, 401, 404, 500)
- Version APIs with `/v1/` prefix in URL
- Use plural nouns for resource names
- Validate all input data before processing
### Response Format
```typescript
// Success response
{
success: true,
data: { ... },
meta?: { pagination, etc }
}
// Error response
{
success: false,
error: "Human-readable error message",
code: "ERROR_CODE"
}
### Security Rules
```markdown
## Security Standards
- Never commit secrets, API keys, or passwords
- Use environment variables for all sensitive configuration
- Validate and sanitize all user input
- Use parameterized queries to prevent SQL injection
- Implement rate limiting on public endpoints
- Use HTTPS for all external communications
- Follow principle of least privilege
- Keep dependencies updated and scan for vulnerabilities
### Environment Variables
```typescript
// ✅ Correct
const apiKey = process.env.API_KEY;
// ❌ Never do this
const apiKey = "sk-1234567890abcdef";
### Database Rules
```markdown
## Database Standards
- Use ORMs for type-safe database queries
- Implement soft deletes (deletedAt timestamp) where appropriate
- Add createdAt and updatedAt to all tables
- Use transactions for operations affecting multiple tables
- Create indexes for frequently queried columns
- Normalize data to reduce redundancy
- Use database migrations for schema changes
### Example Migration
```typescript
export async function up(knex: Knex): Promise<void> {
await knex.schema.createTable('users', (table) => {
table.uuid('id').primary();
table.string('email').notNullable().unique();
table.string('name').notNullable();
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
table.timestamp('deleted_at').nullable();
});
}
### Documentation Rules
```markdown
## Documentation Standards
- Use JSDoc/TSDoc for public APIs
- Include @param, @returns, and @example tags
- Document complex algorithms with inline comments
- Keep README.md updated with setup and usage instructions
- Document breaking changes in CHANGELOG.md
- Add comments for "why" not "what" (code should be self-documenting)
### JSDoc Example
```typescript
/**
* Calculates the total price including tax and discount
*
* @param basePrice - The original price before calculations
* @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%)
* @param discountPercent - Discount percentage (e.g., 10 for 10% off)
* @returns Final price after tax and discount
*
* @example
* ```typescript
* const total = calculateTotal(100, 0.08, 10);
* // Returns: 97.2 (100 - 10% discount + 8% tax)
* ```
*/
function calculateTotal(
basePrice: number,
taxRate: number,
discountPercent: number
): number {
const discounted = basePrice * (1 - discountPercent / 100);
return discounted * (1 + taxRate);
}
### Git Commit Rules
```markdown
## Git Conventions
- Use Conventional Commits format
- Write clear, descriptive commit messages
- Keep commits focused and atomic
- Reference issue numbers in commits
### Commit Format