Treating the AI as a pair partner works best when you decide what it should do at each step: explain, suggest, or implement. That keeps you in the driver's seat and makes the output easier to trust and maintain.
What is pair programming with AI?
Traditional pair programming involves two developers: one writes code (the "driver") while the other reviews and guides (the "navigator"). With AI, you are always the driver, and the AI is a capable but fallible navigator that can:
- Explain unfamiliar code or patterns
- Suggest implementations or approaches
- Generate code based on your specifications
- Review your code and suggest improvements
- Help debug issues by analyzing errors
The key difference: AI does not have context about your project's goals, constraints, or history. You provide that context and make the decisions.
When to ask for explanation
Use explanation mode when you need to understand something before changing it:
Good times to ask for explanation:
- Reading unfamiliar code: "Explain what this function does step by step"
- Understanding algorithms: "What is the time complexity of this? How does it work?"
- Learning patterns: "How does this React hook pattern work?"
- Debugging: "Why might this code throw a null reference error?"
Explanations help you build mental models. Use them before asking for changes so you know what you are modifying.
Example conversation
You: Explain what this useEffect does and why it has these dependencies.
AI: This useEffect runs whenever `userId` changes. It fetches user data
from the API and stores it in state. The dependency array [userId]
ensures the effect only re-runs when the user ID changes, not on
every render. The cleanup function cancels the fetch if the component
unmounts or userId changes before the fetch completes.
You: Got it. Now I can safely modify the loading state handling.
When to ask for implementation
Use implementation mode when the task is well-defined and you know what you want:
Good times to ask for implementation:
- Clear, bounded tasks: "Add error handling to this function"
- Syntax conversions: "Convert this to async/await"
- Boilerplate: "Create a test file for this component"
- Repetitive code: "Add similar validation for these other fields"
Give the AI the file, relevant snippet, and exact change you want. After you get a suggestion, read it and run it; do not accept blindly.
Example conversation
You: Add try/catch error handling to this async function.
Log errors to console and return null on failure.
AI: [provides updated function with try/catch]
You: [reviews code, runs tests]
This looks good, but use our logger instead of console.error.
AI: [provides updated version using your logger]
Keep architecture and design decisions yours
AI can suggest implementations, but you choose:
- Module structure: Which folders and files exist
- Data flow: How information moves through the system
- Dependencies: Which libraries to use
- Patterns: Component structure, state management, API design
- Naming: Conventions that fit your codebase
If AI proposes a big refactor or a new dependency, treat it as a proposal. Ask yourself:
- Does this fit our architecture?
- Is this dependency worth adding?
- Will this be maintainable?
- Does this match our team's conventions?
You decide. AI helps execute your decisions.
How to iterate in small steps
Large requests lead to large, hard-to-review outputs. Break work into small steps:
| Instead of | Try |
|---|---|
| "Rewrite this whole module" | "Extract this validation into a separate function" |
| "Add comprehensive test coverage" | "Add a test for the empty input case" |
| "Refactor this to be cleaner" | "Split this function into two: one for fetch, one for transform" |
| "Fix all the TypeScript errors" | "Fix the type error on line 42" |
Small steps are easier to:
- Verify: You can review a 10-line change quickly
- Roll back: If something breaks, you know which change caused it
- Correct: You can fix issues before they compound
Chain several small requests, but ensure each step is something you can review in one go.
How to correct the AI
When output does not match your intent, say so clearly:
- Be specific: "This doesn't handle null" not "this is wrong"
- Reference the code: "Line 12 should use optional chaining"
- State the fix: "Use our logger, not console.error"
- Provide context: "We never throw errors in this layer; return a Result type instead"
Correcting early avoids a long chain of wrong assumptions. The AI adjusts based on your feedback.
Example corrections
You: This implementation logs to console, but we use a custom logger.
Replace console.error with logger.error from @/lib/logger.
You: This function doesn't handle the case where data is undefined.
Add an early return for that case.
You: The naming doesn't match our conventions. Rename `getData` to
`fetchUserProfile` to match the other fetchers in this file.
Common mistakes to avoid
| Mistake | Why it is a problem | Better approach |
|---|---|---|
| Accepting code without reading | Bugs and style issues slip through | Review every line before committing |
| Asking for huge changes | Hard to review, hard to roll back | Break into small, focused requests |
| Letting AI decide architecture | AI lacks project context | Keep design decisions yours |
| Not providing context | AI makes wrong assumptions | Paste relevant code, types, constraints |
| Ignoring AI suggestions | May miss good ideas | Evaluate suggestions, then decide |
Practical workflow
Here is a workflow for effective AI pair programming:
Understand first. If you do not understand the code, ask for an explanation before changing it.
Specify clearly. When asking for implementation, provide language, framework, constraints, and relevant code context.
Review every output. Read the generated code. Check for correctness, style, and edge cases.
Test immediately. Run tests or the application after each change. Catch issues early.
Correct and iterate. If something is wrong, provide specific feedback. Small iterations beat big rewrites.
Commit clean states. After a working change, commit. This gives you rollback points.
Summary
Pair programming with AI works best when you:
- Stay in the driver's seat
- Decide when to ask for explanation vs implementation
- Keep architecture and design decisions yours
- Iterate in small, reviewable steps
- Correct the AI clearly when output is wrong
For more on getting value from AI without losing control, see AI-assisted coding: practical tips and When to use AI vs write it yourself.
