My first experience with codemods
Back when React deprecated the componentWillMount lifecycle method, the React team introduced codemods to help move existing usages to the new name, UNSAFE_componentWillMount. I remember being shocked that there was a way to update code that wasn’t effectively a fancy find-and-replace, but that you can surgically update the structure of your code.
At the time, I assumed it was all far more smart and clever than anything I was capable of, so I never really looked into it. I’ve interacted with codemods a few times since then (notably, the MobX codemod for migrating from 5 to 6), and again I’ve always had the assumption of it being some vague magic. I understood at a high level that they made use of abstract syntax tree (AST) analysis to change code rather than a pure text manipulation but anything beyond that was out of my knowledge.
Using codemods to update props
Our design system, Kaleidoscope, has a subsystem to support layering elements over the top of each other without needing to manually manage z-indexes. When I introduced this subsystem, we added the ability to opt-in to this behaviour for supported components by using the boolean `autoStack` prop, with an emphasis on opt-in. If you didn’t provide the prop, autostacking would be off. At the time this made sense since we had lots of usages of overlay components from Kaleidoscope littered around the codebase. We wanted to reduce risk of breaking existing experiences and wait until it was a stable and proven pattern.
Three years on, and the layering subsystem is embedded in the way we build UI, and most of the time you actually do want autoStack enabled. There’ve been a number of bugs where we just forgot to add autoStack to a component, and we’ve discussed the idea of changing the default behaviour 😅 so one day last week, after fixing another one of them, I decided to do something about it.
I wanted to make progress on this quickly and decided that the best path forward would be to set `autoStack` to false for all existing usages which don’t have it set. My primary aim is to stem the bleeding for new usages, as most existing usages that aren’t already opted-in probably don’t need autostacking enabled anyway (otherwise they would already have done so).
I audited and found hundreds of autostack-using components across the apps. Yikes. There was no way I was doing that by hand. So I googled “codemod change component props”, hoping I’d find a CLI tool to help, and I found a really good article outlining the process of building your own codemod 🫨
I followed the guide, tweaking bits to make sense for my context, and eventually it worked! You can check out my codemod script here, with a stepper that I wrote with ChatGPT 😁
Caveats
There were two main issues with this approach that I need to note, that might be a problem for you:
- You still need to run Prettier over the code, if you use a code formatter
- Sometimes the formatting resolutions don’t quite make sense (I think this is partially due to the first point 😅)
For example:
return (
// comment
expr1 &&
// another comment
expr2
);
becomes:
return (
(expr1 && // comment
expr2) // another comment
);
which can cause code compilation issues, especially with // @ts-expect-error
or other pragmas. Luckily this didn’t cause issues with any of the files I actually had to run the codemod over, but otherwise we’ll have to manually intervene. Not super ideal for something that modify hundreds of files, but it’s easy to scan over the diff and double check. Way better than manually editing everything!
The compounding benefits of making things easier (or possible)
It was super satisfying to solve that problem, which we’ve been thinking about for three years or so. I’m excited to have another tool in my belt for when I need to make progress on a large change. This reminded me of how important it is to have tools that make things easier to change.
I remember at my last company, something I learnt from a great engineer was the impact of speeding up build times on our CI servers. He said that it’s not just one 5 minute saving. It’s that 5 minute saving, for every single build ever, and how that changes how you work.
Little things that you can quickly validate in CI, or maybe it’ll allow you to break up a task into smaller releasable chunks. Or being able to quickly get a fix out for something that you broke (mandatory mention of “Taming Complexity with Reversibility”).
Over time, by chipping away at these, you can change the fabric of where you work and what you work on. It’s so worth it.