AI Pair Programming: 10 Workflows That Save Hours

ai-pair-programmingworkflowcursorproductivitybest-practicesaider

Here’s the thing nobody tells you after your first week with an AI coding tool: the model isn’t the bottleneck. The bottleneck is knowing what to ask and when. A developer who’s figured out three well-structured workflows will consistently outperform someone who’s been using the tool for months but still just pastes code and asks “what’s wrong.”

What follows are ten workflows that show up on real codebases in 2026. They’re specific enough to copy-paste, and each one covers where the generic advice falls apart.


1. Refactor Without Fear: Scope First, Then Run

Before you let Cursor touch a 1,000-line service class, make it show you a map first.

Open Cursor’s chat panel, switch to Plan Mode (not Agent Mode), and run:

I need to extract the `PaymentProcessor` class from `services/billing.ts`.
It currently handles payment intent creation, webhook parsing, and idempotency
key management. Describe step-by-step what files will change, what I'll need to
update in the callers, and where the most likely test breakage will be. Do NOT
write any code yet.

Read the plan in full, then verify the file list against your actual imports with a quick grep. If it looks right, switch to Agent Mode and paste the plan back with “Now implement this plan.”

Why this saves you 30–60 minutes per major refactor: Cursor Agent in unrestricted mode tends to start writing before it’s identified the full blast radius. Plan Mode forces the model to enumerate affected files first—and forces you to read the map before the territory changes. The classic failure mode is “the refactor ran but now three callsites are broken and I don’t know why.” This stops that.

One caveat: Plan Mode won’t catch dynamic imports or runtime-resolved paths. Still run grep -r "BillingService" after the plan output to spot anything static analysis missed.

See the Cursor custom workflow deep dive for how to pair this with project rules so the refactor respects your architecture conventions automatically.


2. Test Generation Cascade: Tests That Find Bugs You Didn’t Know Existed

This one works on any tool—Cursor, Cline, Aider—because it’s a prompt structure, not a feature.

Paste the function and run step one:

Write a complete test suite for this function. Cover: happy path,
boundary values, null/undefined inputs, and any state mutation side effects.
Use Jest/Vitest [or your test framework]. Write the tests before I tell you
whether they pass.

Run the tests. Some will fail. Now step two—paste the failures back:

These three tests are failing:
[paste the test output]

Do not look at my implementation. Based only on the test name and the assertion,
what edge case does each failing test imply I haven't handled?

Step two is where the real value is. The model, reasoning from the assertion alone, will name the behavioral gap directly—something like “the function doesn’t handle negative values of offset, which the test expects to clamp to zero.” You now have a precise bug description before you’ve read the error message yourself. The first step saves 30–40 minutes of test-writing; the second surfaces edge cases that would otherwise slip into code review or production.

AI-generated tests skew optimistic about input shapes. Always write at least one test yourself with a completely unexpected type—pass a string where the function expects a number—that the model wouldn’t think to add.


3. PR Description Auto-Write: Pipe the Diff

Ten to fifteen minutes per PR. If you ship daily, that’s well over an hour a week spent translating a diff into words. Aider eliminates this cleanly from the terminal:

git diff main...HEAD > /tmp/pr_diff.txt
aider --message "Read the attached diff. Write a GitHub pull request description
with: (1) a one-line summary, (2) a 'What changed' bullet list (implementation-level,
not business-level), (3) a 'How to test' section with specific steps.
Be specific about file names and function names. Do not mention AI.
$(cat /tmp/pr_diff.txt)" --no-auto-commits

If you prefer a local model via Ollama:

git diff main...HEAD | ollama run qwen2.5-coder:14b \
  "Write a GitHub PR description for this diff. Include: summary line,
   bullet list of changes by file, and testing instructions."

Two things to know: large diffs (500+ lines) will overflow context on smaller models. Pipe just the file-level summary with git diff --stat main...HEAD first, then add the full diff for the two or three most interesting files. And the output will occasionally be verbose—tell the model “keep it under 200 words” if your team’s culture leans brief.

For Aider’s full capabilities beyond this, see the Aider review.


4. Legacy Code Archaeology: Line-by-Line with Side Effects Called Out

You’ve inherited a function from someone who left. It works, and you need to change it, but you don’t actually know what it’s doing at a line-by-line level. Any tool handles this—Cursor, Cline, Claude.ai, a local LLM—because this is pure comprehension, not generation.

Paste the function with this prompt:

Explain this function line by line. For each non-trivial line:
1. What it does
2. What it assumes (implicit preconditions)
3. Whether it has any side effects that aren't obvious from the function signature

After the line-by-line, list any hidden state dependencies—anything this function
reads from or writes to outside its own arguments and return value.

The “hidden side effects” request is the part most developers skip, and it’s also the most valuable piece. A good model will surface things like: “line 14 reads from this._cache which is set in the constructor and never reset—if you call this function twice with the same key, the second call returns stale data.” You won’t see that by reading the function yourself, not without a few careful re-reads. This cuts 20–40 minutes per unfamiliar function, sometimes much more if it would have triggered a production incident.

One thing to keep in mind: LLMs can be overconfident about what code “does” versus what it “intends to do.” Cross-check any claim about external dependencies—database calls, env vars, module-level state—against the actual code. The AI reads statically; it can’t know if a variable was mutated by a goroutine.


5. Multi-File Rename Impact Analysis: Use @ Before You Move

This one is Cursor-specific because it relies on the @ file reference feature in chat.

You want to rename UserSession to AuthSession. It lives in one file but shows up across twenty. Before running anything, tag the files you think are relevant in Cursor chat and ask:

@services/auth.ts @components/LoginForm.tsx @api/routes/auth.ts
@middleware/session.ts @tests/auth.test.ts

I'm renaming `UserSession` to `AuthSession` in `services/auth.ts`.
Which of these files will need changes? For each one that needs changes,
list the specific lines or patterns that reference `UserSession`.

This is faster than grep -r not just in time, but in quality of output: the response tells you not just where the name appears but how it’s used—type annotation, variable instantiation, or string in a test description. That distinction matters before you run a global replace.

Cursor’s @ tagging has a context limit. Tag more than 8–10 large files at once and performance degrades; the model may hallucinate about files it didn’t fully read. Keep scope tight—tag only files you have a real reason to suspect—and run a grep -r pass for anything outside that set.


6. SQL Migration Safety Check: Paste Before You Run

This is one of the highest-ROI prompts in the list, and it requires no special tool.

Before running rails db:migrate or flyway migrate in production, paste both your migration file and the relevant portion of your current schema, then:

Here is a database migration I'm about to run on production:

[MIGRATION FILE CONTENTS]

Here is the current production schema for the affected tables:

[SCHEMA SNIPPET]

List the risks of running this migration, specifically:
1. Will any part of this lock tables and for how long?
2. Does it drop or rename anything irreversibly?
3. Are there indexes being created synchronously that will block reads/writes?
4. Is there a data backfill step that could fail partway through and leave the
   schema in an inconsistent state?
5. Is there a down migration, and is it reversible in practice?

Even a junior developer running this on a migration they wrote themselves will catch issues they’d have missed on a solo gut-check. The ROI is asymmetric: a few minutes of review against a potential 5–30 minute production outage.

The one gap to keep in mind: the model doesn’t know your database load patterns. “This adds a non-null column to users” gets flagged as risky (it is, at scale), but the model has no idea whether your users table has 500 rows or 50 million. Read the output with that context applied.


7. Debugging with Context Injection: The 3-Cause Prompt

Most developers paste just the error message into chat. That leaves half the value on the table.

Structure the debugging prompt like this instead:

I have a runtime error. Here's the context:

**Error message:**
[paste full stack trace]

**File where the error originates:**
[paste the relevant file or function, 50–100 lines]

**What I was doing when it happened:**
[describe the user action or the test that triggered it]

Given all of the above, what are the 3 most likely root causes, ranked by
probability? For each cause, tell me the specific line I should check and
what I'd expect to find there if that cause is correct.

The “ranked by probability” constraint stops the model from producing a kitchen-sink list of twelve possibilities. The “what to expect to find” part converts a vague diagnosis into a testable hypothesis you can verify in 30 seconds. In practice, the right cause is in the top two roughly 80% of the time. The biggest time saving—15–40 minutes per session—comes from not spending 20 minutes chasing a red herring.

If the stack trace includes vendor library internals, add “focus only on code I likely wrote—ignore vendor library frames” or the model will anchor on lines you can’t change.


8. Boilerplate Generation with Constraints: Rules + Generate

Your team generates a lot of similar components—form components, API handlers, database models—and they all follow the same pattern. Without a constraints file, AI will generate something that compiles but uses the wrong form library, puts the interface in the wrong place, or defaults to export default when your codebase uses named exports.

Cursor solves this with project rules in .cursor/rules/. Create the rule file once:

---
description: React form component conventions
globs: ["src/components/**/*.tsx"]
alwaysApply: false
---

## Form Component Rules
- Props interface goes above the component, named `[ComponentName]Props`
- Use `react-hook-form` + `zod` for validation, never raw state
- Error messages use the `<FieldError>` component from `src/components/ui/`
- Never use inline styles; use Tailwind class names only
- Export the component as a named export (not default)

Then generating a new component is one prompt:

/generate a form component called `AddressForm` that captures:
street, city, state (US only, dropdown), and zip code.
Follow the component rules.

With the rule active, Cursor conforms to your stack choices on the first pass—no manually stripping out the useState form handling, no hunting down where it put the interface. That’s 10–15 minutes per component, plus the code review time you won’t spend correcting it.

Rule files don’t update themselves. When your conventions change, update the rule file in the same PR as the convention change—otherwise the AI keeps generating the old pattern indefinitely. See Cursor’s docs on project rules for the full activation mode reference, and Building Your First Cursor Custom Workflow for a from-scratch walkthrough.


9. Documentation from Tests: Write the Docstring Without Reading the Code

A function with no documentation, and you need a docstring or a wiki entry. You could read the implementation—or you could use the tests, which describe what the function is supposed to do in unambiguous, verifiable language.

Paste only the tests (not the implementation):

Given only these tests, write a docstring for the function being tested.
The docstring should describe:
1. What the function does (from the test descriptions)
2. Its input parameters and types (inferred from the test calls)
3. Its return value and type
4. Any edge cases or error conditions the function explicitly handles

Do not invent behavior the tests don't demonstrate.

Two things fall out of this that don’t come from reading the implementation directly. First, the documentation reflects verified behavior—not hoped-for behavior that may have bugs in the implementation. Second, it highlights gaps: if the docstring would naturally say “raises ValueError when X” but there’s no test covering that, you’ve just found a missing test case worth investigating.

This is 5–15 minutes per function—low per-function, high aggregate when you’re documenting an entire module for the first time. It’s also a quality multiplier on good tests; if a test was written wrongly, the docstring will faithfully reflect the wrong contract.


10. Dependency Audit: Every External Call, Every Failure Mode

Before adding retry logic, configuring timeouts, or deciding whether a function needs a circuit breaker, you need an accurate picture of where it reaches outside the process. Most functions that interact with external systems are more fragile than they look. Cline works particularly well here because it can follow imports—but any tool handles the core prompt.

Tell Cline to read the file (or paste the function directly) and ask:

List every external call this function makes—HTTP requests, database queries,
file system operations, cache reads, message queue publishes, anything that
goes outside the process boundary.

For each one:
1. Name the call (e.g., "Redis GET on key `user:{id}`")
2. Can it fail silently? (returns empty/null vs. throwing)
3. What happens to the caller if this call hangs indefinitely?
4. Is there a timeout configured?

After the list, tell me which calls represent the highest risk if they degrade
in production.

You’ll have an actionable fault map in two minutes. The “can it fail silently” column is consistently the most useful—silent failures produce incorrect behavior without triggering alerts, and they’re the hardest to diagnose after the fact. This audit takes 30–60 minutes off manual call-graph tracing, and more if it prevents an incident.

If the function makes calls through an abstraction layer—a service class, a repository pattern—the AI will only see the interface, not the implementation. Add “also check the implementation of any service classes called here” and either paste them or use Cline’s file-read capability.

For more on Cline’s file-traversal abilities, see the Cline review.


The most-used workflow on this list, day to day, is the 3-Cause Debugging prompt (workflow 7)—almost every debugging session benefits from it and the setup cost is near zero. The most skipped, despite having an outsized payoff, is the SQL migration safety check (workflow 6): developers are usually confident enough in their migrations to skip the review, which is exactly when an index-blocking migration slips through.


Sources

  1. Cursor documentation — Rules and project context: cursor.com/docs/rules
  2. Cursor documentation — Plan Mode and agent workflows: cursor.com/docs/chat/agent
  3. Aider documentation — Usage with git and diffs: aider.chat/docs/usage.html
  4. Cline GitHub — README and configuration reference: github.com/cline/cline
  5. Aider documentation — LLM selection and model configuration: aider.chat/docs/llms.html
  6. Cursor changelog — Multi-file context with @ references (2024–2026): cursor.com/changelog
  7. Postgres documentation — Lock modes and migration risks: postgresql.org/docs/current/explicit-locking.html

Last verified: May 13 2026

Was this article helpful?