Chapter 6

Cursor Composer in Practice — Multi-File Editing and Complete Workflows

Chapter 6: Cursor Composer in Practice — Multi-File Editing and Complete Workflows

Composer is the biggest difference between Cursor and other AI editors: you describe a feature, it simultaneously edits 5–10 files, shows you a complete diff, and you apply everything with one click after review. This is not completion — it's collaborative development. This chapter covers how Composer works internally, three techniques you must master, and a complete walkthrough of adding Stripe payments from scratch.

Chat vs Composer: When to Use Which

Dimension Cursor Chat Cursor Composer
Primary use Understanding, discussion, getting suggestions Directly generating and modifying code
Code changes Requires manual copy-paste Automatic diff, one-click apply
File scope Mainly single-file discussion Coordinated changes across multiple files
Best for Asking "why", learning principles Doing "what", implementing features
Context control Explicit @ references Automatic project structure analysis

Decision rule: if you need to modify code, use Composer; if you only need to understand code, use Chat.

Composer's Internal Workflow

When you submit a Prompt in Composer, these steps happen behind the scenes:

  1. Project analysis: Cursor scans the project structure and builds a file dependency graph
  2. Semantic search: Finds files in the vector index relevant to your description
  3. Context assembly: Combines relevant file contents, .cursorrules rules, and your Prompt into a long context
  4. Generate a plan: AI first plans which files to modify and what to add
  5. Generate code: Generates changes file-by-file according to the plan
  6. Display diff: Shows all changes in a git-diff-like interface for per-file review

Step 6 is critical: you see all changes before applying them. This is the most important difference between Composer and "just write it for me" — AI is the proposer, you are the decision-maker.

Three Techniques You Must Master

Technique 1: Use Checkpoints to Submit in Phases

Don't ask Composer to implement an entire feature in one Prompt. Break it into logical phases:

# Round 1 Prompt (data layer)
Add a Payment model to the Prisma schema with fields:
- id: UUID primary key
- userId: foreign key to User
- amount: Integer (store in cents, not dollars)
- currency: String (default "USD")
- status: Enum (pending / completed / failed)
- stripePaymentIntentId: String (nullable)
- createdAt: DateTime

Generate the corresponding migration file. Do not touch any other files.

After applying round 1, test that the database migration succeeds, then start round 2:

# Round 2 Prompt (service layer)
Based on the Payment model just created, create PaymentService:
- createPaymentIntent(userId, amount, currency): calls Stripe API to create a payment intent
- confirmPayment(paymentIntentId): confirms successful payment in Webhook, updates DB status
- getUserPayments(userId): queries a user's payment history

Use the existing Stripe client instance in @src/lib/stripe.ts. Do not reinitialize.

Why phases matter: each step can be tested and verified, problems are easy to isolate, and you avoid "changed 50 files but have no idea what broke."

Technique 2: Use Agent Mode for Large Uncertain Tasks

Click the "Agent" button in Composer to enter autonomous execution mode: Cursor reads files, runs commands, and decides the next step on its own. Best for tasks like "complete this large refactor."

Typical Agent mode Prompt:

Analyze the project's error handling approach, then standardize it to:
1. All API errors return { success: false, error: { code, message } } format
2. Service layer throws a custom AppError class (already defined in src/errors.ts)
3. Route layer uniformly catches and formats the response

List the plan before executing. After each module, describe what you changed.

Agent mode warning: It executes real commands, including potential file deletions. Make sure git is in a clean state before starting, so you can roll back if something goes wrong.

Technique 3: The Right Way to Review Diffs

When Composer shows you a diff, don't just click "Accept All." The correct review flow:

  1. Look at the file list first: Are any files being modified that shouldn't be?
  2. Focus on new external calls: What new libraries or APIs are being introduced? Do they comply with project standards?
  3. Check edge cases: Is error handling complete? How are null values, zeros, and overlong inputs handled?
  4. Verify constraints: Did the AI honor the constraints you specified in your Prompt (don't change the interface, keep tests passing)?

Real Case: Adding Stripe Payments in Two Rounds

Goal: Add Stripe one-time payment to an existing Next.js + Prisma project.

Round 1: Data model and service layer

Add Stripe payment to the project, Phase 1: infrastructure

1. Add Payment model to prisma/schema.prisma:
   - id, userId(FK), amount(Int,cents), currency(String),
     status(Enum:pending/completed/failed),
     stripePaymentIntentId(String?), createdAt

2. Create src/lib/stripe.ts: initialize Stripe client (use STRIPE_SECRET_KEY env var)

3. Create src/services/PaymentService.ts:
   - createPaymentIntent(userId: string, amount: number): Promise<{clientSecret: string, paymentId: string}>
   - confirmPayment(stripePaymentIntentId: string): Promise<void>

Do only these three things. Do not touch routes or frontend.

Round 2: API routes and Webhook

Based on the PaymentService from the previous step, complete Phase 2:

1. POST /api/payments/create-intent:
   - Verify user is logged in (use getCurrentUser from src/lib/auth.ts)
   - Call PaymentService.createPaymentIntent
   - Return clientSecret to frontend

2. POST /api/webhooks/stripe:
   - Verify Stripe Webhook signature (use stripe.webhooks.constructEvent)
   - Handle payment_intent.succeeded event, call PaymentService.confirmPayment
   - IMPORTANT: this route must use req.text() not req.json()
     (Stripe signature verification requires the raw body)

Do not create frontend components. Backend API only.

After two rounds the payment backend is complete, fully auditable, and easy to test.

5 Common Failure Patterns

Failure pattern Symptom Fix
Prompt too vague Generated code looks reasonable but doesn't match project style Add specific constraints: which library, which existing file to follow
Too much in one request Dozens of files changed; unknown what broke Split into small phases; one concern per round
No .cursorrules AI doesn't know project conventions; style drifts between rounds Write .cursorrules first with tech stack and standards
Accept All without reviewing Bugs hard to trace back to which round caused them Review every diff carefully before accepting
Agent mode out of control Modified files it shouldn't, deleted important content Commit before starting; git checkout to recover

Chapter Key Points

  1. Composer is for "building features," Chat is for "understanding code" — know the boundary and each tool delivers its full value
  2. Phased Checkpoints are essential: Break one large feature into 3–5 Prompts. Validate each round before starting the next. Far more reliable than a single large commit.
  3. Agent mode is a double-edged sword: Powerful but risky. Ensure clean git state before starting; monitor what it does in real time.
  4. Diff review is not ceremonial: Focus on new external calls, edge case handling, and whether constraints were honored.
  5. .cursorrules is an upfront investment for Composer: Without it, Composer's generated code style will drift. Write it once; it pays off on every subsequent use.
Rate this chapter
4.7  / 5  (51 ratings)

💬 Comments