AI for Bulk Changes
Christina Chan |
I'm a developer on a platform team, which means I spend a lot of time in YAML and shell scripts updating every repository in my organization at once. Docker image migrations, GitHub workflow updates, dependency bumps with breaking changes — the kind of work that touches 30 repos on a quiet day and 300+ when things get interesting.
I've been using AI coding tools for this work, and the question I keep coming back to is: should I have the AI make changes directly, or should I have it write me a script?
The honest answer is: it depends.
When to Script It
For bulk migrations, I almost always have the AI write me a script rather than making changes directly.
- Side effects: My scripts open pull requests, request specific reviewers, and sometimes send Slack messages. If I let the agent iterate on all that every time, I lose control.
- Testability: I need to test on one repo before running on a hundred.
- Resumability: If something fails halfway through, I want to pick up where I left off.
- Repeatability: The script becomes documentation. The next person who needs to do a similar migration can look at mine as an example.
When to Let the AI Edit Directly
For single-repo changes, direct edits are often faster. I'll let the AI loose when I'm exploring a codebase and want to understand how things work, when the change is isolated — one file, one feature, no cascading effects — or when I'm prototyping something I might throw away. Good test coverage helps too, since I can verify the changes quickly.
The key question I ask myself: do I need to run this more than once? If yes, script it. If no, let the AI edit directly.
There's also a middle ground, bulk changes where each instance is different enough that a script would be more complex than the task itself. That's when I spin up an agent with a guide instead.
Example: Folding Gems into a Monorepo
We migrated dozens of Ruby gems from separate repositories into one monorepo. The steps were conceptually similar for every gem: clone the repo, preserve git history, update the gem config for the monorepo structure, run the tests, push it up, and open a PR.
But the details varied. Some gems had different CI setups. Some had unusual directory structures. Some had dependencies on other gems we were also migrating. Writing a script that handled every edge case would have taken longer than the migrations themselves.
So instead, we wrote a migration guide — a document explaining the general process, the conventions to follow, and the gotchas to watch for. Then we'd spin up an agent, give it the guide as instructions, point it at a gem, and let it go. The agent could handle the variation because it could read and adapt. A script can't.
This approach works when the operations are too varied to script cleanly, but the general approach is consistent enough to document. You also need to be able to verify the results — tests pass, PR looks right — and the cost of an agent making a mistake needs to be low. It's a PR, not a production deploy.
Example: Migrating CODEOWNERS
Last May, I needed to update CODEOWNERS files across our repositories after a
team reorganisation. I was using Cursor at the time, but
the workflow applies to any AI coding tool.
Front-load the Context
I started my Cursor chat by explaining what the repository does and where to look for examples:
This is the scripts repo. It contains migration scripts for
bulk changes across our GitHub organization.
The script should:
- Find all repos with CODEOWNERS files
- Parse the current owners
- Output a CSV with suggestions for new owners
I also attached 4-5 files as context, previous migration scripts, the repo structure, and documentation about our conventions, etc.
Then I used a separate AI chat (we have an internal tool called Glean that has access to our Notion) to summarise the reorg details: which teams own what functionality, and some general guidelines for who the new code owners should be. I copied that output directly into my prompt.
This is the key insight someone shared with me: if you're inputting more text than you get out, you're probably doing something effective. My initial prompt was longer than the script it generated. That's fine, that's the point!
Let It Generate
The script it wrote was simple – one main function, clear logging – exactly what I needed:
async function findPlatformCodeOwners() {
console.log('🔍 Scanning repositories...');
// iterate through repos, parse CODEOWNERS, match against team mappings
// output CSV with old owner -> suggested new owner
}
I don't care if this code is beautiful. It's a script. I'll run it once, maybe twice, and delete it. What matters is that it works.
Iterate (Minimally)
The main corrections I had to make:
- Stop using
yarn. I prefer to write scripts with Deno, not Node. It kept trying to addpackage.jsonfiles and install dependencies. I eventually added rules about how to determine the TypeScript/JavaScript runtime to my global rules so I'd stop having to correct the AI. - Don't create new branches. I'm already on a feature branch. Stop trying to be helpful.
- Pre-commit hooks. It got confused by a
lefthook.ymlfile triggering linters. I asked it where the hook was coming from, it told me, and I ran the formatter manually to unblock it.
That was it. Maybe 10% of my interaction was corrections. The rest was just "yes, run it" and "now do the next step."
Rules Files
Most AI coding tools support some kind of rules or instructions file, Cursor has
.cursorrules, Claude Code has CLAUDE.md. I have a global rules file that
applies to every project. I structure it like Markdown because that makes it
readable for me and (I assume) easier for the model to parse.
I built this iteratively. Every time the AI did something wrong, like adding
yarn to a Deno project, I'd correct it in chat and then add a rule so it
wouldn't happen again.
The full rules are in my dotfiles if you want to steal them.
Writing Good Prompts
The biggest factor in whether AI coding tools work well is the quality of your prompt.
I front-load the context. I tell the AI what the repo is, what conventions we follow, and where to find examples. I point to specific files. The AI can't read my mind about how my team does things.
I try to be specific about what I want. "Write a script that updates CODEOWNERS" isn't as useful as "Write a script that finds all repos with CODEOWNERS files, parses the current owners, and outputs a CSV with suggestions for new owners based on the team mappings I'll provide."
I attach files instead of describing them. If there's an existing script I want the AI to follow as a pattern, I attach it. If there's documentation about our conventions, I attach it. The AI is better at learning from examples than from descriptions.
I also use multiple AI tools for different purposes. The AI that gathers context doesn't have to be the same AI that writes the code. I'll use our internal search tool to summarise a Notion document, then paste that summary into my coding prompt. Each tool is good at different things.
And like I mentioned earlier — if your prompt is shorter than the output you're expecting, you're probably not giving enough context!
When I Don't Use AI
Here's the thing: I don't actually use AI agents for most of my coding.
If it's a small change in a repo I'm already familiar with, it's faster for me to make the change than to write out all the context. The cognitive overhead of writing a good prompt isn't worth it for a five-line fix.
I reach for AI when I'm in an unfamiliar codebase and don't know where things are, when I don't care about code quality (scripts, one-off tools, throwaway code), when I'm writing something from scratch, or when I'm writing documentation (Jira tickets, PR descriptions, status updates).
The rule of thumb I use: is the cognitive overhead of writing a detailed prompt less than just doing the task manually? If yes, use AI. If no, just do it yourself.
Tools
I use Claude Code now. I used Cursor before that, and Cline before Cursor. Cursor and Cline are basically interchangeable — both VS Code-based, same settings, same workflow. These days, I interact with Claude Code in the terminal and use Zed as my editor.
For gathering context, I use whatever tool has access to the information I need. Our internal search tool, Glean, can read Notion, so I'll ask it to summarise a document and then paste that summary into my coding prompt.
The Results
That CODEOWNERS migration? The script generated a CSV of suggestions. I
reviewed it, made a few manual adjustments for edge cases, then ran the second
half of the script to open PRs across all the repos.
Total time: maybe 2 hours, including the PR reviews trickling in over the next few days. Without the script, I'd have been clicking through repos for a week.
The tooling will keep changing — I've already swapped editors three times — but the workflow stays the same. Front-load the context, be specific about what you want, and know when to script it versus when to let the AI loose.