MADEBYARIS

Cursor Chat vs Composer: what I open first

6 min read
By Aris Setiawan
Cursor Chat vs Composer: what I open first

I used to treat Cursor Chat vs Composer like one tool with two tabs. Then a six-file “small” change landed in the sidebar, I hit apply, and the PR looked like three juniors had a fight.

Chat is for questions. Composer (the multi-file edit surface, Agent in some builds) is for changing files. I mix them up when I am tired. The PR pays for it.

I’m Aris Setiawan. Senior full-stack, first Cursor Ambassador in Indonesia. I ship client work in Cursor every week. This is not a UI tour. It is what I open first on a real repo.

The full production loop is How to use Cursor AI to ship faster without wrecking the codebase. The messy-repo version is How to use Cursor AI effectively on a real repo. This post is just the fork: Chat or Composer.

They are not the same tool

Pick the surface: Chat vs Composer

Chat lives in the sidebar. You ask. It reads what you @, plus whatever it needs. It writes an answer. Sometimes it offers an apply. I treat that apply as a suggestion, not a commit.

Composer (or Agent) is a job. You give it a scoped change. It opens files, edits several of them, and comes back with a diff you are supposed to read like a junior’s PR.

If I ask Chat to “add CSV export on invoices,” it will explain a plan, maybe dump a snippet that does not match this repo’s helper, and I will paste it into the wrong folder. If I give Composer the same sentence with no fence, it will touch billing, rename a shared type, and open a 30-file diff.

Same model. Different job. The surface is the contract.

When I use Chat

I use Chat when I do not know the shape yet.

Trace. Where does this invoice status actually get set? Which package owns the CSV helper? I @ one or two files and ask. I want a map, not a patch.

Plan. “We need export on the list. Propose the smallest change. Do not write files. Name the paths.” I make it argue with me in Chat until the plan fits on a sticky note.

Explain a weird error. Stack traces, a failing test, a type that only explodes in CI. Chat is faster than me grepping with one eye on Slack.

Review a small hunk. I paste a function and ask what looks off. I do not ask Chat to rewrite the module while I am still reading it.

A Chat prompt I actually use:

In apps/web/app/invoices, where does list filtering live? Do not suggest new files. Point me at the existing helper if there is one.

If the answer names a folder I did not know about, I open that folder. Then I decide if Composer is allowed to touch it.

I do not use Chat to “just apply this across the app.” Apply-from-chat on a client repo is how a helper lands in utils2.ts next to the one we already have.

When I use Composer

I use Composer when I can name the files.

The change is clear. The blast radius is a sentence. I already looked at a sibling file so it can match the local pattern.

Multi-file, still small. A list page plus the export helper. A form plus the action. Not “refactor the billing package.”

PR-sized. A teammate should finish the diff in one sitting. If Composer wants a second package I did not name, I stop it.

A Composer prompt I actually use:

Add CSV export on the invoices list. Touch apps/web/app/invoices and packages/export only. Do not touch packages/billing or webhooks. Reuse the existing CSV helper. Match the error shape in the nearby list route.

Open the files first. @ those paths. Name what is off-limits. Same habit as the effective-on-a-real-repo post. Composer without a fence is Chat with write access. That is worse, not faster.

If I cannot name the folders, I am not ready for Composer. Back to Chat.

My loop

My loop: Chat, Composer, then review

Unclear: Chat. Clear change: Composer. Then I still read it.

I do not skip the last box. Composer is confident. Confident is not correct. I read the diff like a senior reading a junior. Types, names, the extra file I did not ask for, the test it skipped because “the pattern was obvious.”

If the diff is bigger than the sticky note, I revert the extra and split the work. Two Composer jobs beat one “while we are here” job.

A loop I trust:

  1. Ask Chat where this kind of change lives.
  2. Open those files. Write the fence.
  3. Run Composer once.
  4. Read every file it touched. If it opened a stranger, stop.

That is Cursor Chat vs Composer without letting Chat quietly become an unscoped agent.

Mistakes I still see

Pasting a spec into Chat and hitting apply. The spec was a wish. Apply is a commit-shaped object. Keep the wish in Chat. Keep the commit in Composer, with a fence.

Composer with the whole repo in context. It will “help” the neighboring app. Helpful without a map is how Friday’s PR becomes Monday’s revert.

Skipping review because the summary sounded right. Summaries are marketing. The diff is the work. I read the diff.

Using Composer to ask questions. You can. I do not. Questions are cheaper in Chat. Composer will start editing while it explains.

Switching models to fix a bad surface pick. A better model in the wrong surface still writes the wrong size of change. Pick the surface first.

What I tell people who ship

If you are still installing Cursor, start with the beginner tutorial. If you already ship, remember this:

Chat is the senior in the room when you are lost. Composer is the junior you hired for a scoped ticket. You are still the one who merges.

The fix for huge diffs is almost never “use a smarter model.” It is stop asking Chat to ship, and stop asking Composer to think. Practice that loop on your actual repo once.

If you want a second pair of eyes while you install the habit, that is Level up.

Aris Setiawan

Aris Setiawan

Senior Full Stack Developer specializing in Next.js, React, and WordPress. I write about web development, performance optimization, and best practices.

Related Articles