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 add package.json files 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.yml file 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.


Recent Posts

Reading List (2023)