You’ve probably used code autocompletion or copied snippets from Stack Overflow. But with LLMs, coding moves to a new level. Now you can:
- Generate a function from a task description
- Write tests automatically
- Review a pull request on the fly
- Explain complex code as if to a junior
In this lesson, you’ll learn to give clear technical prompts and to use an LLM inside your editor — with GitHub Copilot, Continue, or the ChatGPT API.
📘 What You’ll Learn
- How to correctly ask an LLM to generate or refactor code
- How to write prompts for automated tests
- How to conduct reviews via a prompt
- How to explain someone else’s code in plain language
- How to build a CLI interface based on prompt chains
📚 Key Terms
- Refactoring — changing code structure without altering behavior. Improves readability and maintainability.
- Prompt-chain — a chain of requests where the result of one step is used as input for the next (example: first generate code, then tests).
- Pull request (PR) — a proposal to make changes to a GitHub repository, usually including code, a description, and discussion.
- Continue.dev — a VS Code extension that integrates a GPT model locally or via API, letting you converse with code in the editor.
- Copilot — a GitHub tool powered by OpenAI that offers code completion and generation right as you type.
🧱 Code Generation and Refactoring
Working with code via prompts is one of the most obvious and useful LLM use cases. The model can generate a function scaffold, help structure code, add checks, and then rewrite it to be more readable.
🧪 Prompt for generation:
Write a Python function that checks whether a string is a palindrome. Add a docstring and type annotations.
📌 Tips for code generation:
- Specify the language:
Python,JavaScript,Go— syntax and style depend on it. - Clarify the paradigm: “OOP style,” “using functional patterns.”
- Add constraints:
- “no loops,”
- “O(n) complexity,”
- “do not use third-party libraries.”
- Output format:
returnorprint?- Answer as JSON?
- Do you need logging?
📎 Analogy: You’re the architect describing what to build and from what, and the LLM is the experienced builder creating a turnkey draft. The more precise the spec, the fewer reworks.
🧹 Prompt for refactoring:
Rewrite the code below to improve readability and avoid duplication. Use modern ES6 practices.
📌 Refactoring tips:
- Specify the goal: readability, performance, modularity.
- Specify the style: “in the spirit of the Airbnb guide,” “as declarative as possible.”
- Clarify whether to split code into functions or extract constants.
- Ask the model to explain the changes — useful for learning.
📎 Analogy: You write a draft and hand it to an editor. They don’t change the meaning, but remove repetition, improve style, and format the text. Same with code — behavior stays, structure gets clearer.
📌 Before and After Examples
Before (duplication and poor readability):
function greet(user) {
if (user === 'admin') {
console.log('Welcome, admin!');
} else if (user === 'guest') {
console.log('Welcome, guest!');
} else {
console.log('Welcome, user!');
}
}
After (ES6 refactor):
function greet(user) {
const roles = {
admin: 'Welcome, admin!',
guest: 'Welcome, guest!',
};
console.log(roles[user] || 'Welcome, user!');
}
💡 Why this matters
- Generation — quickly creates templates, especially for routine tasks.
- Refactoring — saves time “cleaning up” someone else’s or legacy code.
- Combination — generate first, then refactor — in 2 steps you can reach excellent code.
🧠 Combo prompt:
Generate a JavaScript function to sort an array of numbers (without using .sort()), then rewrite it in ES6 functional style with comments.
That’s a full scenario for a mid+ developer, checking generation, refactoring, and explanation.
🧪 Auto-Generating Tests
An LLM can significantly speed up writing tests, especially when:
- you’ve just written a function and want to ensure it works,
- you need to cover edge cases but fear missing something,
- you want a quick test skeleton to refine manually later.
📌 The key idea: you describe what to test and which framework — and the LLM writes the tests.
🧾 Sample prompt:
Write unit tests for the function get_user_age(user_dict) that retrieves age from a dict. Use PyTest. Consider edge cases and erroneous input.
✅ What an LLM outputs:
import pytest
from my_module import get_user_age
def test_valid_age():
assert get_user_age({"name": "Alice", "age": 30}) == 30
def test_missing_age():
with pytest.raises(KeyError):
get_user_age({"name": "Bob"})
def test_invalid_type():
with pytest.raises(TypeError):
get_user_age("not a dict")
📌 Prompt recommendations:
- Specify the framework:
PyTest,Jest,JUnit,Mocha— template differs. - Use phrasing like “cover edge cases” — the LLM will propose:
- empty inputs,
- incorrect types,
- boundary values.
- Clarify test format:
- AAA: Arrange → Act → Assert
- table-driven: tests via iterating arrays of inputs/outputs.
- Add: “comment each test” — for learning or review.
🤖 Pros of auto-generation:
- Speed — 10 tests in 10 seconds.
- Typical coverage — standard checks (null, type errors, invalid keys).
- Convenience — run immediately to see which cases you missed.
⚠️ Cons:
-
Poor awareness of side effects.
The LLM won’t always check whether state changes (e.g., database).
-
No performance or security checks.
-
Redundancy: sometimes 5 near-duplicate tests add little value.
🧠 Using It Together with Code Generation
A powerful approach is to generate code and tests together. This lets you:
- see how the model interprets your task,
- automatically verify logic correctness,
- find bugs faster and refine requirements.
💡 Combo prompt:
Write a function get_user_age(dict), then a set of PyTest tests covering edge cases.
📌 Why useful:
- You get two artifacts at once: function and tests.
- You can run tests immediately to verify behavior.
- Easier to catch overlooked cases — especially for MVPs or learning.
🔍 Example model output:
# Function
def get_user_age(user_data):
if not isinstance(user_data, dict):
raise TypeError("Input must be a dictionary.")
return user_data["age"]
# PyTest tests
import pytest
def test_valid_age():
assert get_user_age({"name": "Alice", "age": 25}) == 25
def test_missing_age():
with pytest.raises(KeyError):
get_user_age({"name": "Bob"})
def test_invalid_input_type():
with pytest.raises(TypeError):
get_user_age("not a dict")
📎 Analogy: chef and taster in one
A chef cooks and immediately tastes. They instantly spot where it’s too salty, where a side is missing, and fix it on the spot.
The combo prompt does the same: first create, then test. It’s a more mature way to work with LLMs.
📌 Where it’s especially useful:
- 👶 Education — students see one prompt influences both logic and verification.
- 🚀 Fast MVPs — speed matters, production errors are unacceptable.
- 🧪 Testing new features — result + check at once.
- 🤖 Automated code generation — generate a function, save it, and run the test pipeline.
🔍 Code Review via LLM
One of the most useful, underrated cases: using an LLM as a helper in code review. Especially relevant when:
- you work solo and lack a second pair of eyes,
- the team is large and reviews take long,
- you want a self-check before sending a PR.
📌 Principles of a good review prompt:
- Provide full context:
- code (diff or full file),
- PR description (what changed and why),
- links to docs, task, or bug report.
- Split review into stages:
- 1️⃣ Describe what this PR does
- 2️⃣ Find possible problems or improvements
- 3️⃣ Suggest alternatives
- Clarify focus:
- style checks (
PEP8,ESLint,Prettier), - performance (nested loops, complexity),
- security (injections, validation, XSS).
- style checks (
🧪 Sample request:
Here’s a pull request:
Changes:
- Added PDF generation function
- Connected new library reportlab
- Updated report rendering logic
Here’s the code diff: [paste diff]
Analyze:
1. Does the code follow Python style?
2. Any performance or security issues?
3. What improvements can be made?
🧰 Helpful tools:
- Continue.dev — embed review right in VS Code; ask the LLM “analyze this file.”
- GitHub Copilot Chat — in-editor question: “what’s wrong with this piece of code?”
✅ Especially useful when:
- 🧪 Before team review — catch obvious bugs early.
- 🏗 With legacy code — get refactoring hints.
- 🧑🏫 In education — students get explanations of why this is better.
🔄 Tips:
- Use a “mentor + engineer” tone — ask not just for a list of errors, but for reasoned solutions.
- Don’t stop at one prompt — add follow-ups: “Can you simplify this function?”, “Is there a way to extract this into a utility?”
💡 Explaining Someone Else’s Code
Explain this code to a junior developer. Avoid jargon, use analogies.
📌 Tips:
- Specify the explanation level: “for a beginner,” “as for a student,” “for a manager.”
- You can add “Provide a real-life analogy.”
📎 Example:
def is_even(n):
return n % 2 == 0
Explanation:
The function checks whether a number divides by 2 with no remainder — then it’s even. It’s like splitting apples evenly between two people — if none is left over, the number is even.
🧨 Common Mistakes
- 📉 “Make me a bot” — too broad a request
- 🔄 Inconsistent chain: one step doesn’t pass data to the next
- 🧩 Mixing tasks: generation and tests in one prompt — harder to debug
❓ FAQs
Q: How is Copilot different from ChatGPT?
A: Copilot is in-flow autocompletion; ChatGPT is prompt-based generation. Together — powerful.
Q: How do I avoid “hallucinations” in code?
A: Clarify the task, constrain context, ask for examples.
Q: Can I trust an LLM’s review?
A: As a first iteration — yes. Final decisions should be human.
📌 Summary
Today you learned:
- How to use LLMs for generation, tests, and code review
- How to formulate technical prompts correctly
- How to explain code even if it isn’t yours
- How to create a CLI assistant based on a prompt-chain
👉 These skills save hours of routine, improve code quality, and make working with LLMs practical, not magical.