We've been using AI-assisted coding tools since they were glorified autocomplete. Somewhere between GitHub Copilot's early days and today's agentic Claude Code sessions, something actually changed — these tools got useful in ways that aren't hype. But the workflow that gets the best out of them? It took a lot of bad sessions and wasted hours to figure out.
This isn't a feature comparison article. Cursor has a sidebar chat. Claude Code runs in your terminal. Both can write code. That's not the interesting part. The interesting part is *when* to reach for each one and how to structure your work so the AI is actually helping instead of confidently generating garbage you then have to clean up.
The fundamental difference nobody talks about
Cursor lives inside your editor. It sees your file, your cursor position, your open tabs. It's good at local, scoped changes — refactoring a function, adding types to a component, explaining why your Zod schema is broken. The feedback loop is tight and visual.
Claude Code lives in your terminal and operates on your entire repo. It can read multiple files, run commands, check git history, and make coordinated changes across the codebase. It's closer to a junior dev you've given shell access to than a smart autocomplete.
Use Cursor when you know what you want and need help writing it. Use Claude Code when you're not sure what files even need to change.
We landed on this distinction after a painful week where we used Claude Code to rename a component across the codebase — great use case — and then tried to use it for a single function rewrite. It went and read 14 files, made changes in 6 of them, and introduced two bugs. Cursor would have done that rewrite in 10 seconds.
Cursor: the stuff that actually works
Cursor's best mode is Cmd+K inline editing. Select some code, describe the change, done. No context switching, no chat history to manage. The agent mode is powerful but has a habit of getting ambitious and touching things you didn't ask it to touch.
Here's where Cursor genuinely saves time for us:
- Writing boilerplate — server actions, route handlers, Zod schemas from a TypeScript type
- Translating designs into Tailwind components when the structure is clear
- Adding error handling to existing async functions
- Writing tests for functions that already exist
- Explaining what a piece of code does when you're reading someone else's work
The trick with Cursor inline edits is being annoyingly specific. 'Make this better' produces noise. 'Add Zod validation to this server action, return a typed error object instead of throwing, keep the existing function signature' produces something you can actually use.
// What you have:
async function createProject(data: unknown) {
const project = await db.insert(projects).values(data as any);
return project;
}
// Cursor prompt: "Add Zod validation, return { success, data, error } union type,
// don't throw, validate against this schema: name string min 2, slug string regex /^[a-z0-9-]+$/"
// What you get back:
const createProjectSchema = z.object({
name: z.string().min(2),
slug: z.string().regex(/^[a-z0-9-]+$/),
});
type CreateProjectResult =
| { success: true; data: typeof projects.$inferInsert }
| { success: false; error: string };
async function createProject(data: unknown): Promise<CreateProjectResult> {
const parsed = createProjectSchema.safeParse(data);
if (!parsed.success) {
return { success: false, error: parsed.error.message };
}
const [project] = await db.insert(projects).values(parsed.data).returning();
return { success: true, data: project };
}That took about 8 seconds. You still need to review it — Cursor occasionally hallucinates method names or gets the Drizzle API slightly wrong — but the structure is right and you're editing, not writing from scratch.
Claude Code: treating it like a dev, not a search engine
The biggest mistake people make with Claude Code is using it the same way they'd use ChatGPT — throwing a question at it and expecting an answer. Claude Code is a tool that can *do things*, not just describe things. That mental model shift matters a lot.
When we started treating Claude Code sessions like onboarding a new developer — giving it context upfront, being clear about constraints, letting it ask clarifying questions — the output got dramatically better.
# Bad: vague, no constraints
> Add a feature to export user data
# Better: context + constraints + expected output
> I need to add a CSV export endpoint for user data.
> - Route: GET /api/users/export (existing route handler pattern is in src/app/api/users/route.ts)
> - Auth: use the getServerSession pattern you'll see in that file
> - Include fields: id, email, createdAt, plan — nothing sensitive
> - Stream the response, don't buffer everything in memory
> - Add rate limiting: one export per user per 10 minutes, use our Redis client at lib/redis.ts
> Look at the existing patterns first, then make the changes.That second prompt gives Claude Code enough to produce something that actually fits your codebase rather than a generic implementation it grabbed from its training data. The 'look at existing patterns first' instruction is one we added after Claude Code kept ignoring our auth setup and writing its own.
Claude Code is especially good at multi-file refactors that you've been putting off because they're tedious. Last month we needed to migrate from one email provider's SDK to another across about 15 files. It took Claude Code a single session to map all the usages, rewrite them consistently, and update the types. That would have taken us two hours of careful, boring work.
Managing context so the AI doesn't go off the rails
Both tools get worse as context grows. Cursor's chat gets confused after a long conversation. Claude Code starts making assumptions and skipping steps when the session is long. We've learned to be ruthless about starting fresh sessions.
For Cursor, this means: one task per chat thread. Finished the server action? Start a new chat for the component. Keeping threads focused keeps the suggestions sharp.
For Claude Code, we keep a CLAUDE.md file at the root of every project. It's a one-page summary of the architecture, the conventions we use, and the things Claude Code should never do (e.g. 'don't install new packages without asking', 'use our existing auth helpers, don't write new ones'). Claude Code reads this at the start of every session automatically.
# CLAUDE.md — Project conventions
## Stack
- Next.js 15 App Router, TypeScript strict mode
- Drizzle ORM with Postgres (Neon)
- Auth: custom session-based (see lib/auth.ts)
- Styling: Tailwind + shadcn/ui components
## Hard rules
- Never use `any`. If you're stuck, use `unknown` and narrow.
- All DB access goes through server actions or route handlers. No direct DB calls from components.
- Don't install new packages without checking first.
- Error handling: return `{ success, data, error }` objects. Don't throw from user-facing functions.
## File structure
- Server actions: `src/actions/[feature].ts`
- DB schema: `src/db/schema.ts` (single file for now)
- Shared types: `src/types/index.ts`
## Current focus
Building out the billing flow. Stripe is already integrated in lib/stripe.ts.This file has saved us from Claude Code doing something creative with our auth setup more times than we can count. It sounds basic but most people don't do it.
The review step you can't skip
There's a temptation when AI code looks right to just accept it and move on. This is how you end up debugging something at 2am that was subtly broken from day one. We've shipped AI-generated bugs. Everyone has. The solution isn't less AI — it's building review into your workflow instead of treating it as optional.
Our actual process for AI-generated code:
- Read the diff completely before accepting. Not skimming. Reading.
- Check that it's using your patterns — auth, error handling, types — not something it invented
- Run the TypeScript compiler. `tsc --noEmit` catches a lot of AI mistakes before you do
- For anything touching auth or payments, manually trace the happy path and one error path
- If the function is longer than ~30 lines, ask Cursor to explain what it does. If the explanation has gaps, the code probably does too
The TypeScript compiler is your friend here. Claude Code in particular will occasionally write code that looks correct but uses a method that doesn't exist or gets the generic arguments wrong. `tsc --noEmit` before committing has caught this for us multiple times.
When to not use AI at all
This is the part everyone skips. AI pair programming is genuinely useful, but there are categories of work where it makes things worse, not better.
**Security-sensitive code.** Auth flows, permission checks, token validation — write these yourself. Not because AI always gets them wrong, but because you need to fully understand this code. If something goes wrong in production, you need to debug it at 2am without needing to reconstruct what the AI was thinking.
**Early architecture decisions.** If you're still figuring out the shape of a feature, AI will confidently build the wrong thing in detail. Sketch the structure yourself first, then use AI to fill in the implementation.
**Complex business logic with lots of edge cases.** AI handles the happy path well and the edge cases inconsistently. If you have a billing calculation with seven different proration scenarios, write it yourself with tests. AI will get six of seven right and the one it misses will be the one that matters.
The best use of AI is on code that's tedious to write but easy to verify. If it's hard to verify, slow down.
Our actual workflow day-to-day
When we're building out a new feature in a peal.dev template, the workflow roughly goes like this: we design the data model and API surface ourselves (no AI at this stage, it takes maybe 30 minutes), then we use Claude Code to scaffold the database schema and migrations based on our description, use Cursor to write the server actions and components, and then review everything together in a PR diff. The AI handles maybe 60-70% of keystrokes. We handle 100% of the thinking.
The speedup is real. A feature that would take two days takes one. But it's not because we're writing less code — it's because we're spending less time on the parts that don't require thinking, which frees up time for the parts that do.
For anyone starting a new Next.js project, our templates at peal.dev already have CLAUDE.md files set up with the project conventions, so Claude Code onboards itself correctly from day one. Saves you the awkward first session where the AI confidently writes its own auth system.
One last thing: don't chase the newest model or the newest tool. Cursor and Claude Code are both good enough that your workflow and your prompts matter more than which version you're on. We've seen people spend more time tweaking their AI setup than actually building. That's the trap. Pick a setup, get good at it, ship things.
