TechLead
Lesson 18 of 25
5 min read
AI-Native Engineering

AI Pair Programming Best Practices

Master the human-AI pair programming dynamic — when to lead, when to follow, how to maintain flow state, and the daily rhythm of an AI-native engineer

The Human-AI Pair Dynamic

Traditional pair programming has a driver (typing) and a navigator (guiding). AI pair programming has a similar dynamic, but the roles are fluid. Sometimes you drive and AI navigates ("What should I consider before refactoring this?"). Sometimes AI drives and you navigate ("Implement this feature, I will review your approach"). The best AI-native engineers switch between these modes instinctively.

When to Lead vs When to Follow

You Lead (AI Assists) AI Leads (You Review)
Defining requirements and acceptance criteriaImplementing well-defined features
Making architecture decisions with business contextWriting boilerplate code and tests
Designing user experiences and workflowsRefactoring and migrating code patterns
Evaluating tradeoffs that involve team/org contextGenerating documentation from code
Reviewing and approving AI-generated changesFinding and fixing mechanical bugs

Establishing Context Efficiently

The most common frustration with AI pair programming is "it does not understand my project." This is almost always a context problem, not an AI capability problem. Here is how to establish context efficiently:

# Layer 1: Project context (CLAUDE.md — written once)
# Covers: tech stack, architecture, conventions
# See the CLAUDE.md lesson for details

# Layer 2: Session context (first message in a session)
> I am working on the billing module today. The key files are
  app/lib/billing.ts, app/api/billing/, and app/components/billing/.
  We use Stripe for payments and our own invoice generation.

# Layer 3: Task context (each specific request)
> Add a "retry failed payment" feature. When a subscription payment
  fails, we should show a banner on the dashboard with a "Retry
  Payment" button. Clicking it calls Stripe's retry endpoint.
  Reference the existing PaymentFailedEmail handler in
  app/lib/email-handlers.ts for the failure detection logic.

Working in Flow with AI

The biggest productivity gains come when you achieve a flow state with AI — a rhythm where you direct, AI implements, you review, and the cycle continues without interruption.

Tips for Maintaining Flow

  • Batch your requests: Instead of one tiny change at a time, describe 3-4 related changes and let Claude do them all at once.
  • Review diffs, not files: Focus on what changed, not re-reading the whole file. Use git diff to see only the changes.
  • Use /compact between tasks: Keep context fresh. Summarize after each major task to avoid context pollution.
  • Trust but verify: Run tests after AI changes, not just reading code. If tests pass, trust the implementation.
  • Have a clear task list: Know your next 3 tasks before starting. Switch to the next immediately after completing one.

The "Yes, And" Pattern

Borrowed from improv comedy: instead of rejecting AI output and re-prompting from scratch, build on what AI gave you. Say "yes, and adjust X" instead of "no, do it differently."

# BAD: Rejection and re-prompt (breaks flow)
> [AI generates a component]
> No, that is not what I wanted. Start over. Make it...

# GOOD: Build on what was generated (maintains flow)
> [AI generates a component]
> Good start. Three adjustments:
  1. Use our existing Button component instead of a raw button element
  2. Add loading state for the async action
  3. Move the inline styles to Tailwind classes
  Keep everything else as is.

# Why this works:
# - AI retains context of what it already built
# - You only change what needs changing
# - Much faster than regenerating from scratch
# - The AI learns your preferences within the session

Rubber-Ducking with AI

# Use AI to think through problems before coding
> I need to design a notification system. Before writing any code,
  help me think through these questions:
  1. What notification channels do we need? (in-app, email, push?)
  2. Should notifications be real-time or polled?
  3. How do we handle notification preferences per user?
  4. What is the data model for notifications?
  5. How do we handle notification grouping (e.g., "5 new comments"
     instead of 5 separate notifications)?
  Give me your thoughts on each question with pros/cons.

# This is 10x better than rubber-ducking with an actual rubber duck
# because the AI actually responds with useful analysis

A Day in the Life of an AI-Native Engineer

Time Activity Tool Used
9:00Review overnight PR comments, triage bugsGitHub + Claude Code (/review-pr)
9:30Architecture discussion for new featureClaude.ai for brainstorming
10:00Implement feature — plan, then executeClaude Code (plan mode + execution)
10:45Review AI output, request adjustmentsClaude Code (iterative refinement)
11:00Write tests for new featureClaude Code (generate + run tests)
11:30Quick bug fixes (2-3 small bugs)Cursor (Cmd+K for inline fixes)
12:00Lunch
13:00Code review for 2 teammate PRsClaude Code (AI first pass + human review)
14:00Refactoring: extract shared componentsClaude Code (large-scale refactor)
15:00Technical writing: update API docsClaude Code (generate from code)
15:30Explore solution for tomorrow's featureClaude.ai (design exploration)
16:00Clean up commits, write PR descriptionsClaude Code (git workflows)

The Key to Great AI Pairing

The best AI pair programmers share three traits: (1) they communicate clearly — they know how to describe what they want in a way AI can act on, (2) they review critically — they read every diff and think about whether it is correct, not just whether it compiles, (3) they maintain momentum — they build on AI output rather than rejecting and restarting. These are the same skills that make great human pair programmers, just applied to a different partner.

Summary

AI pair programming is a skill that improves with practice. Learn when to lead and when to follow. Establish context efficiently with layered information. Maintain flow by building on AI output rather than rejecting it. Use AI for rubber-ducking and solution exploration before committing to an approach. The daily rhythm of an AI-native engineer alternates between directing AI and reviewing output, with different tools for different tasks throughout the day.

Continue Learning