Code Generation
Code generation is one of the most transformative applications of LLMs. Modern code models โ GPT-4, Claude 3.5, Code Llama, DeepSeek Coder โ can generate functional code from natural language descriptions, translate between languages, explain complex codebases, write tests, and even review code for bugs and security issues.
The landscape of code LLMs includes general-purpose models with strong coding capabilities (GPT-4o, Claude 3.5 Sonnet) and specialized code models (Code Llama, DeepSeek Coder, StarCoder) that are trained on code-heavy corpora. Specialized models often match general-purpose models on code tasks while being significantly smaller and faster.
This guide covers effective patterns for code generation, repository-level understanding, test generation, code review, and the practical workflow of integrating AI code generation into development pipelines.
Choosing the right model for code generation depends on your requirements for latency, cost, context length, and code quality. The following table compares leading models across HumanEval (pass@1) and practical metrics.
| Model | HumanEval | Context | Speed | Best For |
|---|---|---|---|---|
| GPT-4o | ~92% | 128K | Fast | General code gen, complex logic |
| Claude 3.5 Sonnet | ~93% | 200K | Fast | Long context, code review |
| DeepSeek Coder V2 | ~90% | 128K | Fast | Self-hosted code gen |
| Code Llama 34B | ~75% | 16K | Moderate | On-device, fine-tuning |
| GitHub Copilot | ~55% | 8K | Very fast | Inline completions |
info
Effective code generation prompts are specific, structured, and include contextual information. The most important pattern is providing the language, framework, existing code context, and constraints upfront rather than relying on the model to infer them.
| 1 | # Bad prompt: vague and underspecified |
| 2 | bad_prompt = "Write a function to sort a list." |
| 3 | |
| 4 | # Good prompt: specific with context and constraints |
| 5 | good_prompt = """Write a Python function that sorts a list of dictionaries |
| 6 | by a specified key field. |
| 7 | |
| 8 | Requirements: |
| 9 | - Language: Python 3.12 |
| 10 | - Input: list[dict], key field name (str), ascending (bool, default True) |
| 11 | - Output: list[dict] (sorted, does NOT modify input) |
| 12 | - Sorting: stable sort |
| 13 | - Handle: missing key (skip those items), empty list |
| 14 | - Edge cases: None values in sort key (sort to end) |
| 15 | - Performance: O(n log n) average case |
| 16 | |
| 17 | Example: |
| 18 | Input: sort_by_key([{"name": "Zoe"}, {"name": "Alice"}], "name") |
| 19 | Output: [{"name": "Alice"}, {"name": "Zoe"}]""" |
| 20 | |
| 21 | response = client.chat.completions.create( |
| 22 | model="gpt-4o", |
| 23 | messages=[{"role": "user", "content": good_prompt}] |
| 24 | ) |
Repository-Level Code Generation
For generating code that integrates into an existing codebase, provide the relevant context: imports, type definitions, existing function signatures, and usage examples. Models with long context windows (Claude 3.5 at 200K, GPT-4o at 128K) can consume entire file contents for understanding.
| 1 | # Repository-level code generation with context |
| 2 | REPO_CONTEXT = """ |
| 3 | We use FastAPI with SQLAlchemy async sessions. Project structure: |
| 4 | - app/models/: SQLAlchemy models |
| 5 | - app/schemas/: Pydantic schemas |
| 6 | - app/routes/: API route handlers |
| 7 | - app/services/: Business logic layer |
| 8 | |
| 9 | Current model (app/models/user.py): |
| 10 | class User(Base): |
| 11 | __tablename__ = "users" |
| 12 | id = Column(Integer, primary_key=True) |
| 13 | email = Column(String, unique=True, nullable=False) |
| 14 | name = Column(String, nullable=False) |
| 15 | is_active = Column(Boolean, default=True) |
| 16 | |
| 17 | Current schema (app/schemas/user.py): |
| 18 | class UserCreate(BaseModel): |
| 19 | email: str |
| 20 | name: str |
| 21 | |
| 22 | Current route (app/routes/users.py): |
| 23 | @router.post("/users", response_model=UserResponse) |
| 24 | async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db)): |
| 25 | service = UserService(db) |
| 26 | return await service.create_user(user) |
| 27 | """ |
| 28 | |
| 29 | def generate_endpoint(context: str, endpoint_desc: str) -> str: |
| 30 | prompt = f"""{context} |
| 31 | |
| 32 | Based on this codebase context, implement the following: |
| 33 | |
| 34 | {endpoint_desc} |
| 35 | |
| 36 | Follow existing patterns. Use async/await. Add error handling. |
| 37 | Return only the code, no explanation.""" |
| 38 | response = client.chat.completions.create( |
| 39 | model="claude-3-5-sonnet-20241022", |
| 40 | messages=[{"role": "user", "content": prompt}], |
| 41 | temperature=0.2 |
| 42 | ) |
| 43 | return response.content |
LLMs excel at generating test cases. They can produce unit tests, integration tests, property-based tests, and even generate test data. The key is providing the function signature, expected behavior, and edge cases to cover.
| 1 | def generate_tests(function_code: str, framework: str = "pytest") -> str: |
| 2 | prompt = f"""Generate comprehensive tests for the following function. |
| 3 | Use {framework} framework. |
| 4 | |
| 5 | Function to test: |
| 6 | {function_code} |
| 7 | |
| 8 | Requirements: |
| 9 | - Cover: normal cases, edge cases, error cases |
| 10 | - Include parameterized tests where appropriate |
| 11 | - Test: empty inputs, None values, boundary conditions |
| 12 | - Use descriptive test names |
| 13 | - Add type hints |
| 14 | |
| 15 | Return only the test code, no explanation.""" |
| 16 | |
| 17 | response = client.chat.completions.create( |
| 18 | model="gpt-4o", |
| 19 | messages=[{"role": "user", "content": prompt}], |
| 20 | temperature=0.2 |
| 21 | ) |
| 22 | return response.choices[0].message.content |
| 23 | |
| 24 | # Example |
| 25 | function = """ |
| 26 | def divide_numbers(a: float, b: float) -> float: |
| 27 | if b == 0: |
| 28 | raise ValueError("Cannot divide by zero") |
| 29 | return a / b |
| 30 | """ |
| 31 | tests = generate_tests(function) |
| 32 | print(tests) |
| 33 | # Expected: tests for normal division, division by zero, |
| 34 | # negative numbers, floating point precision, type errors |
Mutation Testing for Test Quality
Use LLMs to generate mutated versions of code (introduce bugs) and check whether existing tests catch them. This evaluates test suite quality and identifies gaps.
| 1 | def mutation_test(original_code: str, test_code: str) -> list: |
| 2 | prompt = f"""Original function: |
| 3 | {original_code} |
| 4 | |
| 5 | Tests: |
| 6 | {test_code} |
| 7 | |
| 8 | Generate 5 mutated versions of this function, each introducing |
| 9 | a different bug (off-by-one, wrong operator, missing edge case, |
| 10 | incorrect return, logic error). For each mutation: |
| 11 | 1. The mutated code |
| 12 | 2. Whether the existing tests would pass (YES/NO) |
| 13 | 3. What bug was introduced""" |
| 14 | |
| 15 | response = client.chat.completions.create( |
| 16 | model="gpt-4o", |
| 17 | messages=[{"role": "user", "content": prompt}], |
| 18 | temperature=0.7 |
| 19 | ) |
| 20 | return parse_mutations(response.choices[0].message.content) |
| 21 | |
| 22 | # Fix suggested by mutation analysis |
| 23 | suggestions = mutation_test(function, tests) |
LLMs can perform automated code review, identifying bugs, security vulnerabilities, performance issues, style violations, and suggesting improvements. The most effective approach is a focused, criteria-driven review rather than a general "review this code" prompt.
| 1 | REVIEW_CRITERIA = """Focus on these specific areas: |
| 2 | 1. **Security**: SQL injection, XSS, CSRF, auth bypass, insecure crypto |
| 3 | 2. **Performance**: N+1 queries, memory leaks, unnecessary allocations |
| 4 | 3. **Logic**: Off-by-one, race conditions, null pointer, error handling |
| 5 | 4. **Maintainability**: Code duplication, unnecessary complexity, naming |
| 6 | 5. **Testing**: Missing edge cases, untested branches""" |
| 7 | |
| 8 | def review_code(code: str, language: str = "Python") -> dict: |
| 9 | prompt = f"""Review this {language} code: |
| 10 | ```{language} |
| 11 | {code} |
| 12 | ``` |
| 13 | |
| 14 | {REVIEW_CRITERIA} |
| 15 | |
| 16 | For each issue found: |
| 17 | - **Severity**: critical/major/minor |
| 18 | - **Location**: line number |
| 19 | - **Description**: what the issue is |
| 20 | - **Suggestion**: how to fix it |
| 21 | |
| 22 | If no issues found, respond with "No issues found." """ |
| 23 | |
| 24 | response = client.chat.completions.create( |
| 25 | model="claude-3-5-sonnet-20241022", |
| 26 | messages=[{"role": "user", "content": prompt}], |
| 27 | temperature=0.1, |
| 28 | max_tokens=2048 |
| 29 | ) |
| 30 | return parse_review(response.content) |
| 31 | |
| 32 | # Continuous review in CI pipeline |
| 33 | def ci_code_review(diff: str, changed_files: list) -> list: |
| 34 | all_issues = [] |
| 35 | for file_path in changed_files: |
| 36 | with open(file_path) as f: |
| 37 | code = f.read() |
| 38 | issues = review_code(code) |
| 39 | if issues: |
| 40 | all_issues.append({ |
| 41 | "file": file_path, |
| 42 | "issues": issues |
| 43 | }) |
| 44 | return all_issues |
best practice