The caret in package.json is so common that most people read it the same way every time: "accept new minors and patches, not majors".
That's right for versions 1.0 and up. Below 1.0, it isn't.
The rule
npm's caret allows changes that don't modify the leftmost non-zero number:
^1.4.2 → >=1.4.2 <2.0.0 (minors and patches) ^0.2.3 → >=0.2.3 <0.3.0 (patches only) ^0.0.3 → >=0.0.3 <0.0.4 (that exact version) You can check any range yourself:
npx semver -r "^0.2.3" 0.2.9 0.3.0 # 0.2.9 Why it works this way
Semantic versioning says that before 1.0, anything may change at any time. npm's compromise is to treat the first non-zero number as the "major". So 0.2 → 0.3 is a breaking release as far as your range is concerned, and it should be.
What it means day to day
0.x packages stay put. npm update won't move ^0.2.3 to 0.3.0, so a pre-1.0 dependency can fall far behind without anything in your workflow noticing.
The bump that "looks minor" isn't. When you finally go from 0.2.x to 0.3.0, read the changelog like it's a major. It often is.
The tilde is stricter still. ~1.4.2 allows only patches (<1.5.0). For 0.x, ^ and ~ end up close to the same thing.
A quick audit
List your direct dependencies that are still pre-1.0:
node -e 'const p=require("./package.json");for(const [k,v] of Object.entries({...p.dependencies,...p.devDependencies}))if(/^[\^~]?0\./.test(v))console.log(k,v)' Then compare each against its latest release. Versyka's free upgrade check does that comparison for a whole package.json in one paste, no sign-up.
How many of your dependencies start with ^0?

