Skip to content

Breaking Change Resolution

Deterministic AST rewrites for all 25 update categories. Not string replacement — real syntax tree transforms.

Deep Dive

25 categories, zero left behind

Ovvoc classifies every npm update into one of 25 categories:

  • 1-6No code change \u2014 version bumps, lockfile regeneration, type stubs, security patches, devDeps
  • 7-13Simple transforms \u2014 renames, import paths, parameter changes, config keys, deprecated API replacements
  • 14-25Complex transforms \u2014 paradigm shifts, architecture changes, hidden behavior changes, coordinated multi-package updates

Every transform uses the OXC parser \u2014 the fastest JavaScript/TypeScript AST parser available. Code is rewritten at the syntax tree level, not with fragile string replacement.

Express 4 (before)
const express = require('express');
const app = express();
- app.get('*', catchAll);
- app.get('/user/:id?', getUser);
- app.del('/item/:id', removeItem);
Express 5 (after)
const express = require('express');
const app = express();
+ app.get('{*path}', catchAll);
+ app.get('/user{/:id}', getUser);
+ app.delete('/item/:id', removeItem);

How It Works

Four stages, fully automated

1

Detect

Scanner identifies the update category from 25 predefined types — version bump, import rename, paradigm shift, and more.

2

Plan

Migration engine selects transforms from 150+ YAML rules covering 29 packages across the npm ecosystem.

3

Transform

OXC AST parser rewrites your code at the syntax tree level. No regex. No string replacement. Real structural transforms.

4

Verify

Build and test run inside an isolated container. If anything breaks, you get a report instead of a broken PR.

Technical Demo

Express 4 \u2192 5 migration in action

Four transforms applied to a real Express app. All AST-level, all verified.

app.js \u2014 Express 4
const express = require('express');
const app = express();
// Wildcard catch-all
- app.get('*', (req, res) => {
res.sendFile('index.html');
});
// Optional parameter
- app.get('/api/users/:id?', getUsers);
// Deprecated method name
- app.del('/api/sessions/:id', logout);
// path-to-regexp v8 breaking change
- app.get('/files/:path+', serveFile);
app.js \u2014 Express 5
const express = require('express');
const app = express();
// Wildcard catch-all
+ app.get('{*path}', (req, res) => {
res.sendFile('index.html');
});
// Optional parameter
+ app.get('/api/users{/:id}', getUsers);
// Deprecated method name
+ app.delete('/api/sessions/:id', logout);
// path-to-regexp v8 breaking change
+ app.get('/files/:path+', serveFile);

Use Cases

Real migrations, handled automatically

Express 4 → 5

Wildcard routes rewritten from * to {*path}, optional params from :param? to {/:param}, middleware patterns updated.

React Router 5 → 6 → 7

Switch replaced with Routes, useHistory migrated to useNavigate, component prop converted to element with JSX.

ESLint 8 → 9

.eslintrc.json migrated to flat config format (eslint.config.js). Plugin imports restructured, extends converted.

The Hidden Cost

What unhandled breaking changes really cost

4–8h

Average developer hours spent per breaking change, including research, code fixes, testing, and code review.

Compounding effect: one skipped major version becomes 3 breaking changes within 6 months as APIs evolve further.

$5–50k

Estimated cost of production incidents caused by outdated dependencies, from minor downtime to full outages.

Breaking changes don't just cost time — they cost compound time. When a team skips a major version update because it's too complex to handle manually, every subsequent release from that package diverges further from what's running in production. Import paths change, APIs are removed, default behaviors shift. What could have been a 2-hour migration becomes a multi-day rewrite.

Security vulnerabilities in outdated packages create a separate category of risk. A CVE in a package you can't easily update — because updating means handling 3 major versions of breaking changes — leaves your application exposed for weeks or months.

Ovvoc eliminates this cycle. By handling breaking changes as they happen, you stay current. No backlog. No compounding debt. No emergency weekend migrations.

Honest Limitations

What Ovvoc can't handle automatically

We believe in transparency. Ovvoc handles the vast majority of breaking changes, but there are patterns that no automated system can reliably transform. For these cases, Ovvoc generates a detailed report instead of attempting a potentially incorrect transform.

Dynamic Code Patterns

Code using eval(), dynamic require() with variables, or computed property access cannot be statically analyzed. Ovvoc flags these for manual review.

Custom Build Systems

Highly customized build pipelines with proprietary tooling, non-standard compilation steps, or unusual build scripts may not run correctly inside Ovvoc's container environment.

Proprietary Internal Packages

Private packages without published changelogs or migration guides have no source for Ovvoc to derive transform rules. These are reported as manual-review items.

Runtime Behavior Changes

Subtle changes in timing, ordering, or runtime semantics that don't affect the API surface but alter program behavior. These are only caught if your test suite covers the affected paths.

Comparison

How Ovvoc compares

Dependabot and Renovate handle version bumps. Ovvoc handles everything — including the hard problems they can't touch.

CapabilityOvvocDependabotRenovate
Version bumps
Simple renames (API, imports)
Paradigm shift migrations
Build verification
Test verification
Failure reports with diagnostics
Multi-package atomic PRs
AI-assisted complex transforms

Dependabot and Renovate are great at what they do. Ovvoc picks up where they stop.

Ready to automate your dependency updates?

Start with one repo. See the difference in your first PR.