In April 2020, it was being downloaded around 12 million times a week. GitHub counted about 3.5 million projects depending on it, mostly without knowing it existed.
Within hours, people trying to start a new project with create-react-app, angular-cli or firebase-tools were staring at an error instead.
Semantic Versioning gives every release three numbers: MAJOR.MINOR.PATCH. PATCH means a bug fix. Nothing new, nothing removed. MINOR means something new was added, and everything that worked before still works. MAJOR means something changed in a way that can break you. Read the notes before upgrading.
That promise is what makes automatic upgrades possible. When a project lists a dependency as "is-promise": "^2.1.0", the caret says: give me any 2.x version from 2.1.0 upward. Patches and minors flow in on their own, because SemVer says they're safe.
Python works the same way. requests~=2.31 or django>=5.0, "Can anyone using this observe the difference?"
Not "did I mean to break something." Not "is this technically a feature." Just whether someone on the other side could notice.
You don't need to publish npm packages to fall into this. Every backend API has consumers, and the same gap between intent and impact shows up in ordinary tickets: Tightening validation. You add a minimum length on a username field. It feels like an addition. But a request that returned 200 yesterday returns 400 today. You removed something. Fixing a value. Your API returned status labels in the wrong language, and you fix it. Any client that matched on the old strings now breaks. With enough users, someone depends on every observable behaviour, bugs included. That idea even has a name: Hyrum's Law. Correcting a calculation. Same field, same type, different number. If clients make decisions on it, their decisions just changed without warning. Adding a field. Usually safe. Not for clients with strict schema validation that rejects unknown fields.
The is-promise change sits on the third branch. It changed how the package was loaded, which is exactly why it needed a new MAJOR.
A slow query made faster is a patch. An internal log nobody else can see is a patch. A crash fixed in a code path that never returned anything useful is a patch.
And inflating versions has its own cost. If every release is "breaking," consumers stop reading the number and pin everything forever. Then they miss your security fixes too.
"It's only a small change" isn't evidence. "It works on my machine" definitely isn't.
Forbes Lindesay shipped a fix within hours and wrote a public post-mortem. He added end-to-end tests that install the package the way a real user would, then check it actually loads. And the ES Module support came back later as a proper major version instead of a minor.
It was an honest mistake that could have happened to almost anyone. What's worth copying is the response: test from the consumer's side, and let the version number tell the truth.
Semantic Versioning looks like it's about code. It's really a promise to the people who depend on you, written in three numbers.
is-promise didn't break because it was small, or because one-line packages are silly. It broke because the version described what the author meant to do, while millions of installs depended on what the release actually did.
What's the smallest release you've seen break something big? I'd like to hear the gray-zone stories other engineers have run into. Share yours in the comments.
If this was useful, follow me here for more stories from the backend trenches, and subscribe to The Backend Tech Lead on Substack for weekly blueprints.
