Skip to content
kobiakov.dev
Back to blog

AI-Assisted Development Workflow: Claude, Cursor, and the Limits of Automation

How to integrate Claude and Cursor into a real development workflow without losing architectural judgment — patterns that work and traps to avoid.

Vladyslav Kobiakov4 min read
AI-Assisted Development Workflow: Claude, Cursor, and the Limits of Automation

Every developer now has an AI pair programmer available on demand. The question is no longer whether to use it, but how to integrate it without accumulating technical debt faster than the AI generates code. This post is about the workflow I've settled on after a year of using Claude and Cursor on production projects.

The Fundamental Tension

AI models are excellent at generating plausible-looking code. They're poor at understanding your specific constraints: your team's conventions, the footgun you shipped three months ago, the third-party library with the undocumented breaking change. The developer's job has shifted from writing boilerplate to providing context and evaluating output.

This isn't a warning to use AI less. It's a prompt to use it differently.

Tool Roles

I treat the two tools as serving different purposes:

Claude (in Claude Code / API) is for reasoning tasks — architecture decisions, debugging a subtle race condition, reviewing a PR for correctness, explaining why a piece of code does what it does. It operates on conversation context and handles ambiguity well.

Cursor is for code generation tasks within a specific file or module — implementing a function whose interface is already defined, converting a component to use a new API, writing tests for an existing function. It works best when the surrounding code is visible in context.

Neither tool replaces design thinking. Both tools replace typing.

Context Is the Bottleneck

The single most important skill with AI-assisted development is writing effective context. A vague prompt produces vague code:

❌ "Add pagination to the blog"

✅ "Add cursor-based pagination to getMdxPosts() in lib/mdx.ts.
   The function currently returns all posts sorted by date desc.
   Params: { cursor?: string; limit: number }.
   Return: { posts: BlogPost[]; nextCursor: string | null }.
   The cursor is the post slug of the last item in the previous page."

The second prompt specifies the file, the current behavior, the interface, and the return contract. The model generates something that fits the existing codebase instead of a generic pagination implementation.

CLAUDE.md as Persistent Context

One of Claude Code's most useful features is the CLAUDE.md file — project-level instructions that are injected into every session. I use it to document things the model can't infer from code:

## Testing Strategy Unit tests mock Prisma via vi.mock() in __tests__/setup.ts — do not change this pattern. Never mock the database adapter; use the real Prisma client against a test database. ## API Routes All API routes require rate limiting via lib/rate-limit.ts. Use logger.ts (Pino), not console.log. ## Component Patterns UI components follow shadcn/ui patterns with CVA for variants.
markdown

This prevents the model from "helpfully" restructuring your test setup or introducing console.log into production code.

Workflow for a Feature

Here's how a typical feature flows through the toolchain:

1. Design in conversation

Before touching code, describe the feature to Claude and discuss the approach:

"I need to add a newsletter subscription endpoint. We use Resend for email.
The endpoint should: validate email format, check for duplicates via Prisma,
send a welcome email, and return a rate-limit-aware response.
What's the simplest implementation that fits the existing api/ pattern?"

This surfaces architectural decisions — where rate limiting goes, what error responses look like — before they're embedded in code.

2. Generate with Cursor

Once the interface is clear, use Cursor's inline generation to implement it. The surrounding file context (existing routes) guides the style.

3. Review output critically

AI-generated code has predictable failure modes:

  • Over-engineering: Abstractions that aren't justified by current requirements
  • Missing error handling at boundaries: Good internal logic, weak validation at the edge
  • Optimistic concurrency: No consideration of race conditions (two users subscribing simultaneously)
  • Hallucinated APIs: Functions that look right but don't exist in the installed version

Read the output as you would a junior developer's PR. Approve the correct parts, rewrite the rest.

4. Write the tests yourself

Tests are the specification. Writing them yourself forces you to think through edge cases the AI didn't consider. Use AI to generate test scaffolding (describe blocks, mock setup), but write the assertions manually.

When Not to Use AI

Some tasks are actively slower with AI assistance:

Debugging production issues — The model doesn't have your logs, your actual data, or your infra. It will propose plausible-sounding fixes that don't address the root cause. Use observability tools first.

Security-sensitive code — Auth logic, input sanitization, cryptography. AI gets these wrong in subtle ways that pass code review. Write these from first principles and have them audited.

Cross-file refactoring — The model's context window limits its view of dependencies. Large refactors are better planned in conversation, then executed manually or with a purpose-built script.

Measuring the Actual Speedup

Anecdotally: routine implementation tasks (CRUD endpoints, form components, utility functions) are 3-4× faster. Architecture and debugging are roughly the same speed — the planning time dominates.

The bigger gain is psychological. The cost of exploring an approach drops to near zero. You can prototype three different implementations in the time it used to take to write one, compare them, and discard the weaker two. This changes how you make design decisions.

Conclusion

The most effective AI-assisted developers I've seen share a pattern: they spend more time on context and review, not less. The model handles synthesis; the developer handles judgment. The workflow that extracts the most value is one where you are very clear about which of those two jobs you're doing at any given moment.

Share: