The problem
Every new feature on our team started with the same 20 minutes of copy-pasting a route, a component, a test file, and a Storybook story from an existing feature, then renaming everything by hand. I wanted a CLI that generated all of it correctly in one command.
Role and impact
- Individual project, developer tooling.
- Cut new-feature scaffolding time from ~20 minutes to under 30 seconds.
- Adopted by the whole frontend team, used to generate 40+ feature modules.
- Eliminated an entire class of copy-paste bugs from stale renamed files.
Technical decisions and challenges
Hardcoded string templates drift out of sync the moment the starter's conventions change, since nobody remembers to update the generator alongside the code. I compared maintaining static template strings versus generating templates directly from an existing "reference" feature module in the repo.
I chose to generate templates from a living reference module with placeholder tokens, because it means updating the starter's conventions once, in real code, automatically keeps the generator correct instead of relying on someone remembering to update a separate template file.
A generator that blindly overwrites files is dangerous the moment someone reruns it against a folder they've already started editing. I evaluated always overwriting with a warning versus a dry-run diff preview before any write.
I ended up requiring a dry-run diff preview by default, with an explicit --write
flag to apply changes, since silent overwrites were the single scariest failure
mode for a tool meant to save time, not create incidents.
Detailed stack
| Layer | Technology | Why |
|---|---|---|
| CLI | Node.js, Commander.js | Simple, well-documented command parsing and subcommands |
| Templating | Handlebars | Readable templates with helpers for casing and pluralization |
| Testing | Vitest | Snapshot tests over generated output for every template |
Gallery
What I would do differently
I would add an interactive prompt mode for picking which pieces to generate (route, tests, stories) instead of always generating the full set. Right now, a feature that doesn't need a Storybook story still gets one, which someone then has to remember to delete.