BTC 71,187.00 +0.62%
ETH 2,161.90 +0.08%
S&P 500 6,591.90 +0.54%
Dow Jones 46,429.49 +0.66%
Nasdaq 21,929.83 +0.77%
VIX 25.33 -6.01%
EUR/USD 1.09 +0.15%
USD/JPY 149.50 -0.05%
Gold 4,532.70 -0.43%
Oil (WTI) 91.50 +1.31%
BTC 71,187.00 +0.62%
ETH 2,161.90 +0.08%
S&P 500 6,591.90 +0.54%
Dow Jones 46,429.49 +0.66%
Nasdaq 21,929.83 +0.77%
VIX 25.33 -6.01%
EUR/USD 1.09 +0.15%
USD/JPY 149.50 -0.05%
Gold 4,532.70 -0.43%
Oil (WTI) 91.50 +1.31%

Customizing AI Code Completion for Your Tech Stack

| 2 Min Read
Generic AI suggestions waste time. Learn to customize Cursor, Copilot, and Claude Code to understand your specific frameworks, patterns, and coding standards. Continue reading Customizing AI Code Comp...
SitePoint Premium
Stay Relevant and Grow Your Career in Tech
  • Premium Results
  • Publish articles on SitePoint
  • Daily curated jobs
  • Learning Paths
  • Discounts to dev tools
Start Free Trial

7 Day Free Trial. Cancel Anytime.

How to Customize AI Code Completion for Your Tech Stack

  1. Identify your tool's config file: .cursorrules for Cursor, .github/copilot-instructions.md for Copilot, or CLAUDE.md for Claude Code.
  2. Create the config file at the correct location in your project repository root (or .cursor/rules/ for .mdc format).
  3. Declare your exact tech stack, including framework names and version numbers, to narrow suggestion scope.
  4. Define 5–10 high-frequency rules covering component patterns, imports, and error handling conventions.
  5. Add negative rules ("never use X") to eliminate entire categories of incorrect suggestions.
  6. Specify file organization, naming conventions, and architectural boundaries your project enforces.
  7. Commit the config file to version control and review rule changes in pull requests alongside code.
  8. Iterate by converting repeated manual corrections into new rules and updating configs during framework upgrades.

Generic AI code suggestions routinely miss the mark. They produce boilerplate that ignores project frameworks, team conventions, and architectural patterns. The result: developers spend more time refactoring AI output than they would have spent writing code manually. But the major AI coding tools now support project-level configuration files that fundamentally change suggestion quality. Cursor uses .cursorrules, GitHub Copilot reads .github/copilot-instructions.md, and Claude Code loads CLAUDE.md. Most developers have not configured any of them. This article provides exact configuration syntax, framework-specific rule patterns, and a stack-specific reference library to change that immediately.

Prerequisites: This article's examples require Cursor 0.40+, GitHub Copilot extension 1.200+ (VS Code), and Claude Code CLI 1.0+. Feature availability differs across earlier versions. Check your tool's changelog if a feature does not appear as described.

Table of Contents

Why Generic AI Code Suggestions Fail Your Stack

AI coding assistants train on massive, heterogeneous codebases. The statistical patterns they learn reflect the average of millions of repositories, not the specific conventions of any single project. When a model suggests a React class component in a codebase that exclusively uses functional components with hooks, or generates an Express-style callback handler in a Fastify project, the suggestion is technically valid JavaScript but architecturally wrong.

The real cost is not the bad suggestion itself. You evaluate it, decide what to keep, rewrite what doesn't fit, and verify the result still meets team standards. This cost compounds across a team of developers accepting dozens of suggestions per day.

The real cost is not the bad suggestion itself. You evaluate it, decide what to keep, rewrite what doesn't fit, and verify the result still meets team standards.

Customization addresses three distinct categories of mismatch:

  • Framework rules define which libraries, APIs, and component patterns the project actually uses.
  • Coding standards cover naming conventions, import ordering, error handling approaches, and formatting preferences.
  • Architectural patterns specify how files are organized, how data flows between layers, and which patterns are explicitly forbidden.

Understanding Context Files Across Tools

How AI Coding Tools Read Project Context

AI coding tools operate on a hierarchy of context. Global user settings form the base. Project-level configuration files layer on top. Inline context from open files and explicit references add further specificity. The active file being edited provides the most immediate signal.

Project-level config files represent the highest-leverage customization point because they apply to every interaction within a codebase without requiring per-prompt repetition. A well-written config file reshapes every suggestion, every chat response, and every code review comment across the entire project.

The Three Configuration Files You Need to Know

Cursor reads .cursorrules at the project root (legacy format, still supported) or .mdc rule files in the .cursor/rules/ directory (current format). These formats are not interchangeable; .mdc files require metadata frontmatter (including description and globs fields) to be loaded by Cursor. Plain text files placed in .cursor/rules/ will not be detected. The .cursorrules file itself is a plain text file with natural language instructions that Cursor injects into its system prompt.

Here is an example of a valid .mdc file with the required frontmatter:

<!-- .cursor/rules/react-conventions.mdc -->
---
description: React component and export conventions for this project
globs: ["**/*.tsx", "**/*.ts"]
---

Always use functional components with TypeScript interfaces for props.
Never use default exports for non-Next.js-reserved files. Use named exports exclusively.

Without the --- frontmatter block containing description and globs, Cursor will silently ignore the file. Always verify new .mdc files appear in Cursor's Settings > Rules panel after creation.

GitHub Copilot reads .github/copilot-instructions.md, a Markdown file that ships with the repository. Copilot parses this file and applies its contents across IDE completions, Copilot Chat, and code review features.

Claude Code loads CLAUDE.md files hierarchically: from the user's home directory (~/.claude/CLAUDE.md; on Windows: %USERPROFILE%\.claude\CLAUDE.md), from the project root, and from subdirectories. Each level adds context for its scope. Claude Code also loads CLAUDE.md from all intermediate directories between the project root and the current working file, which is particularly relevant for monorepo setups. Files can also import other files using @path/to/file.md syntax.

The key difference in capability scope: .cursorrules and copilot-instructions.md primarily influence single-file completions and chat, while CLAUDE.md feeds into Claude Code's multi-file agentic workflows, including test generation, commit formatting, and PR descriptions.

Here is the same rule expressed across all three formats:

# .cursorrules (Cursor)
Always use functional components with TypeScript interfaces for props.
Never use default exports. Use named exports exclusively.
<!-- .github/copilot-instructions.md (GitHub Copilot) -->
## Component Conventions
- Use functional components with TypeScript interfaces for props definitions.
- Use named exports exclusively. Do not use default exports.
<!-- CLAUDE.md (Claude Code) -->
# Code Style
- All React components must be functional components.
- Define props using TypeScript interfaces, not type aliases.
- Use named exports only. Default exports are forbidden.

The content is identical in intent. The format varies: Cursor accepts plain text or Markdown, Copilot expects Markdown, and Claude Code uses Markdown with hierarchical heading structure.

Writing Effective .cursorrules Files

Structure and Syntax

The .cursorrules file lives at the project root. Cursor reads it automatically when the project is opened. The file supports natural language instructions organized under clear headings. Effective files follow a consistent structure: project overview, tech stack declaration, coding conventions, and file naming patterns.

Framework-Specific Rules That Actually Change Output

Declaring the exact tech stack narrows the model's output distribution. For example, specifying "Next.js 14 App Router" instead of just "React" eliminates class-component suggestions, pages-directory patterns, and getServerSideProps calls in one stroke.

Negative rules ("never use X") tend to be more impactful than positive rules ("always use Y") because they eliminate entire categories of incorrect suggestions. A rule stating "Never use the pages/ directory or getServerSideProps" is more effective than "Prefer App Router patterns" because the negative rule is unambiguous.

Negative rules ("never use X") tend to be more impactful than positive rules ("always use Y") because they eliminate entire categories of incorrect suggestions.

# .cursorrules — Next.js 14 App Router + TypeScript + Tailwind CSS

## Project Overview
This is a Next.js 14 application using the App Router exclusively.
All components use server components by default unless explicitly marked
with "use client".

## Tech Stack
- Next.js 14 (App Router only, no pages/ directory)
- TypeScript with strict mode enabled
- Tailwind CSS for all styling (no CSS modules, no styled-components)
- Server Components by default; Client Components only when necessary

## Component Conventions
- Use functional components with TypeScript interfaces for props.
- Name component files in PascalCase: e.g., UserProfile.tsx
- Use named exports exclusively for all components, utilities, and hooks.
- Exception: Next.js reserved files (page.tsx, layout.tsx, error.tsx,
  loading.tsx, not-found.tsx) must use default exports as required by the framework.
- Co-locate component-specific types in the same file.

## Data Fetching
- Use async server components for data fetching. No useEffect for data loading.
- Use Next.js fetch() with appropriate cache and revalidation options.
- Never use getServerSideProps, getStaticProps, or getInitialProps.

## State Management
- Minimize client-side state. Derive UI from server-fetched data using async Server Components where possible.
- For client-side state, use useState and useReducer. No Redux.
- Use nuqs for URL-based state management in search and filter patterns.

## File Organization
- Route files: app/[route]/page.tsx
- Layouts: app/[route]/layout.tsx
- Shared components: components/
- Server actions: app/actions/
- Type definitions: types/

## Error Handling
- Use error.tsx boundary files for route-level error handling.
- Use Zod for runtime validation of external data.

Advanced: Referencing Project Files and Documentation

Cursor supports @docs references that point to external documentation URLs, allowing the model to pull in framework-specific API details beyond what the config file contains. File references using @file can point the model to specific implementation examples within the project. For example: @docs https://nextjs.org/docs or @file src/components/Button.tsx.

For longer, evolving context that changes between tasks, Cursor's Notepads feature is more appropriate than .cursorrules. Notepads are session-scoped and manually activated, making them suitable for sprint-specific context or experimental patterns. The .cursorrules file should contain stable, project-wide conventions that rarely change.

Configuring GitHub Copilot Custom Instructions

Repository-Level Instructions with copilot-instructions.md

The file lives at .github/copilot-instructions.md within the repository. Copilot automatically detects and applies it. The instructions influence Copilot's inline completions, Copilot Chat responses, and code review suggestions. A single file shapes behavior across all three interaction surfaces.

Writing Instructions That Copilot Actually Follows

Effective Copilot instructions name exact framework versions, reference specific libraries, and define concrete patterns rather than abstract principles.

"Use proper error handling" is too vague to change output. "Wrap all database queries in try/except blocks, catch specific exceptions from django.db, and return DRF Response objects with appropriate HTTP status codes" is specific enough to produce correct suggestions.

Common pitfalls include files that exceed useful length (GitHub recommends keeping instructions concise; in practice, keep files under 200 lines and test whether later rules still influence output), contradictory rules (specifying both REST and GraphQL conventions without scoping), and vague qualitative guidance ("write clean code").

<!-- .github/copilot-instructions.md — Django REST Framework -->

## Tech Stack
- Python 3.12, Django 5.x, Django REST Framework 3.15
- Database: PostgreSQL with Django ORM
- Testing: pytest with pytest-django, factory_boy for fixtures
- Type hints: use Python type hints on all function signatures

## API Conventions
- All views must use DRF class-based views (APIView or ViewSets).
- Never use function-based views or Django's generic views.
- Use ModelSerializer for standard CRUD. Use plain Serializer for custom input validation.
- URL routing uses DRF DefaultRouter for ViewSets.

## Serializer Patterns
- Define serializers in a dedicated serializers.py per app.
- Use SerializerMethodField sparingly. Prefer annotated querysets.
- Always set read_only_fields explicitly on ModelSerializer Meta.
- Validate at the serializer level using validate_<field> methods.

## Error Handling
- Use DRF's built-in exception handling. Do not catch generic Exception.
- Return Response objects with explicit status codes from rest_framework.status.
- Log unexpected errors with structlog before re-raising.
  **Required dependency**: `pip install structlog`

## Testing Conventions
- Use pytest fixtures, not setUp/tearDown methods.
- Use factory_boy factories for all model instances. Never call `ModelClass.objects.create()` directly in tests.
- Name test files as test_<module>.py. Name test functions as test_<behavior>.
- Assert on response.status_code and response.data explicitly.

## Code Style
- Use pathlib for all file path operations. No os.path.
- Use f-strings for string formatting. No .format() or % formatting.
- Imports: standard library, then third-party, then local. Use isort ordering.

Organization-Level vs. Repository-Level Settings

GitHub allows organization administrators to configure Copilot defaults that apply across all repositories in the organization. These settings live in the organization's GitHub settings under the Copilot section.

Copilot merges repository-level instructions from copilot-instructions.md with organization-level defaults. Repository rules supplement rather than override org-level configuration. Verify combined behavior in your organization before relying on this layering.

Setting Up CLAUDE.md for Claude Code

CLAUDE.md Structure and Behavior

Claude Code loads CLAUDE.md from the user home directory (~/.claude/CLAUDE.md; on Windows: %USERPROFILE%\.claude\CLAUDE.md), all intermediate directories from root to the current path, the project root, and subdirectories. Files can also import others using @path/to/file.md syntax. Each level adds context, with more specific files supplementing (not replacing) broader ones.

This hierarchical loading distinguishes Claude Code from Cursor's flat .cursorrules approach. A monorepo with a Python backend and TypeScript frontend can have a root CLAUDE.md with shared conventions and subdirectory files with language-specific rules. Intermediate-directory CLAUDE.md files (e.g., at the packages/ level in a monorepo) are also loaded, allowing fine-grained layering.

Note: CLAUDE.md files can contain internal architecture details. Consider whether the contents are appropriate for your repository's visibility level before committing them.

Using Claude Code's Multi-File Awareness

Claude Code operates as an agentic tool that reads, writes, and modifies multiple files in a single session. This means CLAUDE.md rules can define cross-file architectural constraints that single-file completion tools cannot enforce: module dependency rules, testing requirements for every new function, commit message formats, and PR description templates.

<!-- CLAUDE.md — Rust/Axum API Project -->

# Project Overview
This is a REST API built with Rust, Axum 0.7, and SQLx for database access.
Target: PostgreSQL 16. Async runtime: Tokio.

# Module Organization
- Route handlers: src/routes/ (one file per resource)
- Business logic: src/services/ (no database access in route handlers)
- Database queries: src/repositories/ (all SQLx queries live here)
- Shared types: src/models/ (derive Serialize, Deserialize on all API types)
- Error types: src/errors.rs (unified AppError enum implementing IntoResponse)

# Error Handling
- All fallible functions return Result<T, AppError>.
- Never use .unwrap() or .expect() in production code. Use ? operator.
- Map external errors (SQLx, reqwest) into AppError variants.
- Return structured JSON error responses with appropriate HTTP status codes.

# Coding Patterns
- Use extractors for request parsing: Json<T>, Path<T>, Query<T>.
- Use tower middleware for cross-cutting concerns. No ad-hoc middleware.
- Prefer compile-time checked queries with sqlx::query_as!().
- All public functions must have doc comments.

# Testing
- Every route handler must have at least one integration test.
- Use the `testcontainers` crate for database tests (add with `cargo add testcontainers`). No mocking the database layer.
- Unit tests live in a `#[cfg(test)] mod tests {}` block within the source file. Integration tests live in the `tests/` directory at the crate root.
- Run database integration tests single-threaded to avoid conflicts:
  `cargo test --test '*' -- --test-threads=1`
  (the `--` separator passes flags to the test binary; scoping with `--test` avoids serializing unit tests).

# Forbidden Patterns
- No blocking I/O on the Tokio runtime. Use spawn_blocking if unavoidable.
- No .clone() to satisfy the borrow checker without a comment explaining why.
- No pub fields on structs. Use constructor functions and getter methods.

# Git Conventions
- Commit messages: type(scope): description (e.g., feat(auth): add JWT refresh)
- Squash-merge feature branches. No merge commits on main.

Using CLAUDE.md with /init and Project Bootstrapping

Claude Code's /init command (Claude Code CLI only) auto-generates a starting CLAUDE.md by analyzing the project's file structure, dependencies, and existing patterns. The generated file typically lists detected languages, a dependency summary, and directory conventions, giving you a starting point rather than a finished config.

The effective workflow: run /init to generate the initial file, review and correct any inaccurate assumptions, add team-specific conventions the tool could not infer, and then refine rules over subsequent sessions based on cases where Claude Code's output required manual correction. Each correction suggests a missing or insufficiently specific rule.

Stack-Specific Configuration Library

The following snippets are formatted as tool-agnostic rules. Adapt the syntax to match .cursorrules, copilot-instructions.md, or CLAUDE.md as shown in the format comparison earlier.

Frontend Frameworks

React 19 changed several patterns around data fetching and the use() hook. These rules prevent the most common misgenerated patterns:

React 19 + TypeScript + Vite

- React 19 with functional components only. No class components.
- The `use()` hook requires a stable promise reference.
  Do not create promises inline inside the component render body (e.g., `use(fetch('/api'))`):
  this creates a new Promise on every render, causing Suspense to remount repeatedly.
  Memoize promises outside the component or use TanStack Query / SWR for client-side fetching.
- Define props with TypeScript interfaces. Use React.FC sparingly.
- Vite for bundling. Import assets with Vite's ?url and ?raw suffixes.
- Use React.lazy() and Suspense for code splitting.
- CSS: CSS Modules with .module.css files. No inline styles.

Vue 3's Composition API has two reactive side-effect primitives that models frequently confuse. These rules disambiguate them:

Vue 3 Composition API + Pinia

- Vue 3 with <script setup lang="ts"> syntax exclusively.
- Use Composition API. No Options API.
- State management: Pinia stores with setup store syntax (function-based).
- Use defineProps<T>() and defineEmits<T>() with TypeScript generics.
- Use computed() for derived reactive values.
- Use watchEffect() for side effects that depend on multiple reactive sources
  and do not require access to previous values.
- Use watch() when you need: the previous value, lazy execution (not on mount),
  or a single explicit source. Do not replace watch() with watchEffect()
  when old/new value comparison is needed.
- Router: vue-router 4 with typed route params.

Svelte 5's runes syntax is new enough that models default to Svelte 4 patterns without explicit rules:

Svelte 5 with Runes

- Svelte 5 with runes syntax. Use $state(), $derived(), $effect().
- No legacy reactive statements ($: label syntax).
- Use TypeScript in <script lang="ts"> blocks.
- Component props: use $props() rune with TypeScript interface.
- Prefer $derived() over $effect() for computed values.
- Use snippet blocks for reusable template fragments.

Backend Frameworks

Node.js + Fastify + Prisma

Fastify's validation model differs from Express middleware patterns. These rules prevent Express-isms from creeping in:

- Fastify 4.x with TypeScript. No Express patterns.
- Use Fastify's schema-based validation with JSON Schema. If using `@fastify/type-provider-zod`,
  Zod schemas are acceptable at the route level as the type provider. Otherwise, use JSON Schema
  with Fastify's built-in AJV validation. No Joi.
- Prisma ORM for all database access. Never write raw SQL unless justified.
- Define route handlers with full Fastify type generics (Request, Reply).
- Use Fastify plugins for modular route registration.

Laravel 11 + Eloquent

- Laravel 11 with strict Eloquent conventions. Requires PHP 8.3+.
- Use Eloquent scopes for reusable query constraints. No raw DB::queries.
- Form requests for validation. No inline validation in controllers.
- API resources for response transformation. No toArray() in controllers.
- Use PHP 8.3 typed properties, enums, and readonly classes where appropriate.

Rails 7 + Hotwire

- Rails 7 with Hotwire (Turbo + Stimulus). No heavy JavaScript frameworks.
- Use Turbo Frames for partial page updates. Turbo Streams for real-time.
- Stimulus controllers for client-side behavior. Keep controllers small.
- Follow Rails naming conventions strictly. No custom route names without reason.
- Use ActiveRecord scopes. No raw SQL in controllers.

Spring Boot 3 + Kotlin

The reactive vs. coroutine choice in Spring Boot 3 trips up AI tools that assume one or the other. Spell it out:

- Spring Boot 3.x with Kotlin. No Java-style patterns.
- Use Kotlin data classes for DTOs. No Lombok.
- Prefer constructor injection (Kotlin primary constructors). No @Autowired.
- For reactive endpoints, choose either Spring WebFlux with Reactor (Mono/Flux)
  or Spring MVC with Kotlin suspend functions.
  If mixing both in one project:
  1. Ensure `spring-boot-starter-webflux` is declared (provides Reactor on classpath).
  2. Add `kotlinx-coroutines-reactor` for the coroutine/Reactor bridge.
  Do not assume either is available transitively.
- Define exceptions as sealed classes. Use @ControllerAdvice for handling.

Full-Stack and Mobile

Next.js 14 App Router (Full Config)

Refer to the complete .cursorrules example in the Cursor section above. Adapt the format for Copilot or Claude Code as needed, preserving the rules for server components, data fetching, and file organization.

Flutter/Dart

- Flutter 3.x with Dart 3. Use strict null safety.
- State management: Riverpod 2. No setState() in production widgets.
- Use freezed for immutable data classes. Use json_serializable for JSON.
- Widget structure: small, composable widgets. No build() methods over 80 lines.
- Follow flutter_lints rule set. Prefer const constructors where possible.

React Native + Expo

- React Native with the current stable Expo SDK (check expo.dev/changelog for the latest version).
  Use Expo Router for file-based navigation.
- Functional components with TypeScript. Same interface conventions as React.
- Use expo-image over Image. Use expo-av for media.
- Platform-specific code: use .ios.tsx/.android.tsx file suffixes.
- No bare React Native APIs when Expo equivalents exist.

Measuring and Iterating on Config Effectiveness

Signals That Your Config Is Working

Watch for these changes after adding a config file:

  • Fewer post-suggestion edits. Developers edit fewer suggestions before accepting them, particularly for framework-specific patterns like imports and component structure.
  • Correct imports on first generation. The right modules and patterns appear on the first suggestion, not after regeneration.
  • CI passes without style fixes. Generated code passes existing linters and formatters without manual adjustment.

Track these through your IDE's Copilot acceptance-rate dashboard (VS Code shows this under the Copilot status menu) or by monitoring how often generated code passes CI on the first push. Neither metric is perfect, but both trend in visible directions within a week of adding a config file.

Iterating on Rules

Start with 5 to 10 rules covering the highest-frequency pain points: component patterns, import conventions, and error handling. This range is enough to cover the top correction patterns without exceeding the context window the tool reliably follows. Expand the file based on patterns of repeated correction. If a developer finds themselves making the same edit to AI suggestions multiple times per day, that edit should become a rule.

Version-control all config files alongside the codebase. Review rule changes in pull requests just like code changes. This ensures the team collectively agrees on conventions and catches rules that conflict or become stale.

When upgrading frameworks or changing architectural decisions, update the config files in the same pull request. A Next.js 14 to Next.js 15 migration should include corresponding .cursorrules or CLAUDE.md changes reflecting new APIs, deprecated patterns, and updated conventions.

Key Takeaways

Start with one tool and one config file targeting your most-used framework. Use the stack-specific library above as a starting point. Version-control and team-review your config files alongside the code they describe. The highest-value rules eliminate your most frequent manual corrections.

SitePoint TeamSitePoint Team

Sharing our passion for building incredible internet things.

Comments

Please sign in to comment.
Capitolioxa Market Intelligence