Shipping re-shell cli to 0.29 - Lessons From Fast Iteration
The CLI for my Re-Shell platform crossed version 0.29 recently, and it got there through a long string of small, rapid releases rather than a few big planned ones. I am the only maintainer, so the whole release cadence lives or dies on habits rather than process. Looking back at the version history is a little like reading a diary of decisions, some good and some I quietly walked back a release later. A few lessons stand out, and most are about discipline rather than cleverness.
Pre-1.0 Is a License, Not an Excuse
Being under 1.0 is freeing. Semantic versioning gives you explicit permission to break things in the zero range, and for a tool that is still finding its shape, that permission is the difference between shipping and stalling. I have reworked command names, changed default behaviors, and restructured config more than once. Pre-1.0 is exactly the time to do that, while the user base is small and the cost of a breaking change is a paragraph in release notes rather than a migration guide and a wave of support requests.
But the freedom has a sharp edge. It is tempting to treat the zero range as a place where nothing counts, where you churn the surface area endlessly because technically you are allowed to. That habit trains your users to expect instability, and it trains you to stop thinking hard about interface decisions. So I hold a rule for myself: I can break things, but I have to mean it. A breaking change should buy a clearly better design, not just relieve me of the discomfort of living with an earlier choice.
Honest Breaking Changes
When I do break something, the version number has to tell the truth. In the zero range that means a minor bump, and the release notes have to say plainly what changed and how to adapt. The failure mode I work hardest to avoid is the quiet breaking change, the kind that slips out in a release labeled as a patch and silently changes behavior someone depended on. That is the thing that actually erodes trust, far more than a clearly announced rework.
The discipline is unglamorous. Before tagging a release I ask whether anyone running the previous version would be surprised by the new one, and if the answer is yes, that surprise goes in the notes with a clear before and after. It costs a few minutes per release. It saves the kind of debugging session where someone is convinced their setup is broken when really the tool changed under them without saying so.
Small Releases Beat Big Ones
The single practice that has helped most is shipping small and often. Many of those releases between early versions and 0.29 changed one thing each. A small release is easy to reason about, easy to describe, and easy to roll back if it turns out wrong. When something breaks after a release, the diff to investigate is tiny, and the blast radius is contained to one change rather than tangled up with twenty others.
Large releases feel productive because they bundle a lot of work into one moment, but they concentrate risk in exactly the wrong way. Frequent small releases keep that risk spread thin. They also keep momentum honest. There is always something shippable, which means there is always a reason to finish a piece of work rather than let it sprawl into a six-week branch that never quite lands.
Dogfooding Is the Real Test Suite
I use this CLI on my own projects, constantly. That is not a nice-to-have, it is the primary way rough edges surface before anyone else hits them. Automated tests catch the things I thought to test. Dogfooding catches the things I did not, the awkward flag combination, the confusing error message, the default that seemed reasonable in the abstract and is annoying in daily use. A tool you do not use yourself drifts toward what is easy to build rather than what is good to use.
The feedback loop is immediate and unforgiving in a useful way. If a workflow irritates me three times in a week, that irritation goes straight onto the list, and it tends to get fixed fast because I am the one feeling it. It is the cheapest, highest-signal feedback I have, available every single day without my having to ask anyone.
Velocity Without Dropping Quality
Keeping this pace as a solo maintainer is where AI agents genuinely earn their place. I lean on them heavily for the surrounding work that would otherwise eat the time I do not have: drafting tests, sketching an implementation for a new command, sweeping for inconsistencies across the codebase, writing the first pass of release notes. The agents do the legwork; I make the calls. That division keeps velocity high without letting me pretend the machine is making the design decisions.
The risk is obvious and worth naming. Moving fast with a lot of generated code makes it easy to ship things you did not fully think through. So the human judgment cannot be the part that gets automated away. I still read the diffs, I still decide what is worth a breaking change, and I still ask whether a thing is actually good rather than merely working. Which brings me to the question underneath all of this: what does done enough to publish even mean for a CLI? My answer, refined over many releases, is that it means the feature works, the interface is one I would defend, the breaking changes are honest, and I would happily run it on my own machine tomorrow. That bar is lower than perfect and much higher than it compiles. Holding it consistently is most of what got the tool to 0.29 without it falling apart.