How I Replaced user_id in 73 Files in 5 Minutes While My Mate Watched in Disbelief
How I Replaced `user_id` in 73 Files in 5 Minutes While My Mate Watched in Disbelief
Last week I was refactoring a legacy codebase—the kind where you open a file and immediately want to close it again. The task: rename userid to accountid across 73 files. My colleague said, "Mate, you'll be here till midnight." I did it in five minutes. His jaw dropped so far I could've fitted an AirPods case in there.
But here's the thing. Six months ago, I'd have been the one pulling an all-nighter. Opening files one by one. Ctrl+F. Replace. Save. By file 40, I'd be questioning every life choice that led me to software development.
Today I'm going to talk about Cursor's multi-file editing—not the sanitised "click here, click there" nonsense from the docs, but the actual tricks, gotchas, and facepalm moments I've collected over months of real use.
Your Idea of "Batch Editing" Is Probably Just Find-and-Replace with Extra Steps
Let me embarrass myself first.
When I started with Cursor, I thought Cmd+Shift+F followed by "Replace All" was batch editing. Revolutionary, right? Until I accidentally replaced every instance of type with category across an entire project.
typeof became cateof. PropTypes became—wait, let me get this right—PropTypes became PropCates because the type inside it got swapped. Actually, no. I'm second-guessing myself now. I think it was PropTypes where the type substring matched, so it turned into PropCates. Whatever. The point is, the console lit up like a Christmas tree. Two full screens of red. The only thought in my head: Thank god I committed this morning.
Here's the truth: real batch editing isn't string replacement. It's context-aware modification.
Cursor's actual superpower is its Agent mode (that little wand icon—it was called Composer until mid-2024, then they rebranded). You give it natural language instructions, it understands your code structure across files, and it makes surgical changes.
Here's what I actually said:
"In the src directory, rename the `user_id` field to `account_id` in all API files. Keep the assignment logic intact. Don't touch `user_id` in comments. Don't modify interface names in type definition files."
Cursor scanned the project, identified 23 files that actually needed changing, skipped type definitions, comments, and documentation. Thirty seconds. Zero errors.
That's batch editing. Not Ctrl+H with a fancy UI.
Real-World Scenario #1: Refactoring an API Call Chain
Last year I inherited an e-commerce project where error handling was scattered across components like confetti. My job: extract it all into errorHandler.ts and update 40+ component files to use the unified approach.
The old way: Open each file, find the try-catch blocks, replace with handleError() calls, pray you didn't miss one. By file 20, you're genuinely considering whether rewriting the entire project from scratch would be faster.
The Cursor way:
- I wrote
errorHandler.tsfirst with the standard logic:
export const handleError = (error: unknown, context?: string) => {
// Unified reporting + user notification
console.error(`[${context}]`, error);
message.error(getErrorMessage(error));
};
- Opened Agent mode and gave it this:
"Analyse all .tsx files under src/components. Find every try-catch block that directly calls `console.error` or `message.error`. Replace with `import { handleError } from '@/utils/errorHandler'` and call `handleError(error)`. Keep existing business logic. Don't touch other try-catch blocks."
- Cursor generated a modification plan—42 files with specific changes listed—and asked me to confirm.
- I scanned the plan and noticed it included test files. Added: "Exclude the tests directory."
- Confirmed. One minute. Ran the test suite. All green.
The gotcha I learned the hard way: The first time I used this feature, I didn't review the plan. Just hit confirm. It modified files in node_modules. Yes, that black box you never look at but your entire project collapses without. Thank god for Git. I nearly performed an impromptu keyboard destruction ritual.
Lesson: Always specify file scope in your instructions. It's muscle memory for me now—every command includes "only look in src", "exclude node_modules and dist".
Real-World Scenario #2: Migrating Component Library Usage
We moved from Ant Design 4 to 5. API changes everywhere. Eighty-seven files used these components. My instruction: "In all .tsx files under src, replace ` It generated a list. I reviewed each one. Some Small hiccup afterwards: it changed Critical technique: Always make Cursor list matches first before modifying. It gives you a review window. I never skip this step, no matter how confident I am in my prompt. This one's for anyone working on internationalised apps. Our i18n setup had all Chinese strings in one massive This involved: I tackled it in two steps: Step one: Split the JSON "Split `locales/zh-CN.json` by key prefix: `user_*` keys go into `locales/user.json`, `order_*` into `locales/order.json`, and so on. Preserve the original structure. Don't modify values." Step two: Update all references "In the src directory, change all `t('user_xxx')` to `t('user:xxx')`, `t('order_xxx')` to `t('order:xxx')`. Note: `t()` parameters may include template literals like `` t(`user_${type}`) ``—these should become `` t(`user:${type}`) ``." It actually handled the template literal cases correctly. I stared at the screen for a solid few seconds. Genuinely impressed. Minor facepalm: it changed After eight months of heavy use, here's what I've learned—often painfully: 1. Git backups aren't optional Sounds obvious. But efficiency tools make you cocky. I now commit before every batch edit. Sometimes I even create a separate branch. Run tests after. Merge only when clean. Don't ask why I'm this paranoid. Let's just say I once reverted 300 lines of code. Three. Hundred. Lines. Sit with that. 2. Be specific, but give examples Early on, I wrote vague instructions like "help me refactor error handling." Cursor would interpret that however it fancied. Results were... creative. My current prompt template: "In [file scope], change [match condition] to [target form]. Here's an example: [example]. Don't touch [exceptions]." I've used this template for about three months. Success rate went from roughly 60% to over 90%. 3. Split complex tasks Don't expect one instruction to handle everything. The i18n migration above? Splitting it into two steps was actually faster because each step was independently verifiable. Try to do too much at once and you'll spend longer debugging than you saved. Same principle as writing functions—single responsibility. 4. AI isn't infallible. Review everything. Cursor is impressive. It's not magic. It misjudges context. It changes things it shouldn't when you're not looking. Always review the modification plan, even if it's just a quick scan. Thirty seconds of review versus hours of rollback. You do the maths. 5. Standardise rules across your team If your whole team uses Cursor, create a Some numbers from my own work: But honestly? The biggest gain isn't time. It's psychology. Before, the thought of touching dozens of files made me procrastinate. I'd put off refactoring indefinitely. Now, knowing there's a tool that's got my back, I'm actually willing to improve code. The quality goes up as a side effect. Our project's ESLint warnings dropped from over 200 to under 30 in about two sprints. Without Cursor, I'd probably still be arguing with the product manager about whether refactoring has "business value." What are you using Cursor's batch editing for? Any spectacular failures you'd care to share? Or clever tricks the official docs don't mention? Drop a comment. I'll pick the interesting ones and turn them into a follow-up tutorial. Someone once told me they used Cursor to batch-rename CSS variables and accidentally mangled template literals inside CSS-in-JS. I laughed for a full day. Related reads: Full-stack developer with 8+ years of experience. Currently building AI-powered developer tools. I've tested 20+ AI API providers and coding assistants.'s
pagination prop went from object to boolean-or-object. 's visible became open. Ant Design 5 officially launched late 2023, I think—we didn't migrate until March 2024. Don't ask. Scheduling conflicts. You know how it is.
visible props were on custom components—we had a that shouldn't change. I marked those as exceptions. Cursor skipped them automatically. The rest were done in one go.false was a variable name, not a boolean literal. Manual fix. Two minutes.Real-World Scenario #3: Bulk Migration of i18n Strings
zh-CN.json. We needed to split it into module-based files—user.json, order.json, product.json—and update all t('xxx') calls in code to use module prefixes like t('user:xxx').
t() calls across 23 component filest('userinfo') to t('user:info'), but userinfo and user:info weren't the same key in our mapping. Caught three of these by running our i18n lint script. As far as I know, no AI handles business-logic-level mappings perfectly yet. Human review is still non-negotiable.The Landmines I've Already Stepped On for You
.cursorrules file. Put your naming conventions, directory structure, and forbidden actions in there. Our team started this in June 2024. Now everyone's batch edits are consistent—no more situations where Alice changes something and Bob changes it back. It looks something like:
// .cursorrules
- Never modify node_modules or dist directories
- Component naming: PascalCase
- API function naming: camelCase, prefixed with fetch
- Ban the `any` type
So How Much Faster Is It, Actually?
What's Your Experience?
#cursor #ai-coding #developer-tools #refactoring #productivity #frontend #webdevCael Lee