We Scored 27 Famous Open Source PRs for Deploy Risk
React Hooks. TypeScript's optional chaining. The Next.js revert-of-a-revert-of-a-revert. We ran 27 landmark open source pull requests through our deploy risk model. The results are not what you'd expect — and one of them only changed a single line.
Deploy risk scoring exists to answer one question before a PR merges: how likely is this to cause a production incident? We built Koalr's model around 36 signals — file churn, blast radius, reviewer load, change entropy, CODEOWNERS compliance, and more.
Most of the time, that model runs against unremarkable internal PRs. But we got curious: what would it have said about some of the most consequential pull requests in open source history? The ones everyone in the industry knows by name.
We pulled 27 of them. Here is what we found.
The obvious ones scored exactly as expected
The tRPC v10 router rewrite — PR #1777 — scored 80. That is Critical. A complete API redesign, flat procedure API replacing nested routers, new type inference surface, zero migration compatibility with v9. No surprises there.
The webpack Module Federation PR scored 66. High. Eighty-five files, a new cross-build runtime dependency surface, failure modes that span independent builds. Also expected.
The surprising ones — landmark PRs that scored lower than their reputation implies
The more counterintuitive results were the landmark PRs that scored lower than their reputation implies.
React PR #14679 — “Enable hooks!” — scored 33 (Low). The diff is the removal of a feature flag. The hooks implementation had already been reviewed, tested, and running behind that flag. The risky work happened in the hundred preceding PRs. The “Enable hooks!” commit itself posed minimal deployment risk. Ecosystem impact and PR-level deployment risk are not the same thing — and the model correctly measures the latter.
Svelte 5 (PR #13701) scored 44 (Medium). Thousands of files changed, but after a year of release candidates, the uncertainty had been methodically eliminated. A high blast-radius merge with low surprise factor is not the same as a risky one. The dangerous PRs were the ones building each new rune.
TypeScript's codebase-to-modules conversion (PR #51387) scored 55 (Medium). It touched every source file in the compiler, but each change followed a predictable mechanical pattern. Uniform, well-tested refactoring is less risky than it looks in the diff viewer.
The revert pattern
One of our favourite examples is Next.js PR #45196. The title reads: “Revert ‘Revert ‘Revert ‘Revert ‘Initial metadata support’’’’”. The PR body says: “Hopefully last time.”
Four consecutive reverts of the same feature, spanning multiple days. This is the deploy risk model's nightmare scenario in plain sight: a feature that could not land cleanly, reverted, re-applied, reverted again, in a loop.
It scored 54.
The score is medium because what actually changed in this specific PR is modest in scope — the instability is in the history, not the diff. The model correctly separates recent churn from current change risk.
There is a risk signal specifically for this pattern — repeated churn on the same file set, with revert commits in the recent history. It is one of the strongest predictors of another rollback.
The one that surprised us most
The Jest-to-Vitest migration in tRPC — PR #3688 — scored 53. Medium risk.
At first glance, that sounds about right for a test runner swap. But look at what actually changed: every single test file in the repository, plus the root configuration, plus the CI pipeline. The surface area was enormous.
The score was “only” 53 because the risk model correctly identified that none of the changed files were production code paths — only test infrastructure. A test runner change cannot break a production deployment directly. What it can do is make future regressions invisible, which is a subtler and harder-to-measure risk.
The model is honest about what it can and cannot see. Broken test infrastructure does not score as a deploy risk — it scores as a coverage risk. Different signal, different response.
The score table
Here are eight of the most interesting of the 27 PRs we scored, with the risk level and the primary reason for the score:
Scores last updated April 15, 2026 and reflect the current model weights. They shift as the model is tuned. Rescore any PR live →
| PR | Score | Why |
|---|---|---|
| facebook/react #14679 Enable hooks! | 33Low | One feature flag flip. Changed behavior for every React application on earth. |
| microsoft/TypeScript #51387 Convert the codebase to modules | 65High | Touched every source file in the TypeScript compiler. Changed the build system. Dropped dependencies. |
| vercel/next.js #45196 Revert "Revert "Revert "Revert 'Initial metadata support'"""" | 62High | Body literally says "Hopefully last time." Four consecutive reverts of the same feature. |
| microsoft/TypeScript #33294 Add support for Optional Chaining | 55Medium | Most requested TypeScript feature in history. Touches parser, checker, and emitter. |
| nodejs/node #41749 lib: add fetch | 49Medium | Single bootstrap file change that added global fetch to every Node.js process. One file, ecosystem-wide implications. |
| sveltejs/svelte #13701 release svelte 5 | 44Medium | The full Svelte 5 runes rewrite merged to main. Thousands of files changed. |
| microsoft/TypeScript #57465 Infer type predicates from function bodies | 42Medium | 65 lines of new code. Changed type inference behavior across the entire checker — causing new type errors in existing codebases. |
| trpc/trpc #3688 chore: replace Jest by Vitest | 58Medium | Touches every test file in the repo. Looks like a tooling swap. Subtle timing and mocking differences. |
The other nineteen
The remaining nineteen were selected for breadth — major version releases, paradigm shifts, and tooling rewrites across the JavaScript ecosystem. A few scored higher than the eight above; they were not featured because the scores were predictable rather than surprising.
| PR | Score | Why |
|---|---|---|
| vercel/next.js #37551 Implement new client-side router (App Router) | 63High | 145 files. New routing paradigm, React Server Components, nested layouts, Suspense integration. Every page in every Next.js app is affected. |
| angular/angular #32219 Make Ivy the default in Angular! | 42Medium | Flipped enableIvy to true as the default compiler for all Angular applications. One flag change, every Angular project recompiled differently. |
| denoland/deno #26063 2.0.0 | 48Medium | Deno 2.0 release: Node.js/npm compat, breaking API changes, LTS cycle introduction. New runtime version used in production. |
| microsoft/TypeScript #21316 Conditional types | 49Medium | Introduced T extends U ? X : Y and the infer keyword — deeply wired into the type checker, triggering inference changes across the entire compiler. |
| sveltejs/svelte #8799 Version Packages (Svelte 4.0.0) | 36Medium | Major version. Removed CJS output, updated TypeScript types, reduced dependencies by 75%. Breaking changes for any Svelte 3 downstream consumer. |
| webpack/webpack #10838 Merge Module Federation into webpack 5 beta | 66High | 85 files. Module Federation introduced shared runtime module loading across independent builds — new dependency surface with cross-build failure modes. |
| microsoft/TypeScript #12114 Mapped types | 52Medium | Introduced mapped type syntax and built-in utility types (Partial, Readonly, Pick). Changes to the core type checker with broad inference implications. |
| microsoft/TypeScript #40336 Template literal types and mapped type "as" clauses | 52Medium | Type-level string interpolation. Wired into the checker at a fundamental level — changed how string types propagate through inference chains. |
| facebook/react #11225 ReactDOM.createRoot | 19Low | Initial implementation of the opt-in concurrent root API. Low surface area, but the foundation for every React 18 concurrent feature that followed. |
| nodejs/node #20876 worker: initial implementation | 58Medium | 47 files. New V8 isolate management, SharedArrayBuffer transfers, and inter-thread messaging — a new concurrency primitive wired into the Node.js core. |
| microsoft/TypeScript #39094 Variadic tuple types | 47Medium | Enabled spreading generic types in tuple positions. Scoped change in the type checker, but cascaded into new inference behaviour for variadic function composition. |
| eslint/eslint #17748 feat!: Update shouldUseFlatConfig and CLI so flat config is default | 25Low | Made eslint.config.js the default, deprecating .eslintrc. Every project on the old config format will start seeing deprecation warnings on next run. |
| nodejs/node #26745 New --experimental-modules implementation | 50Medium | 69 files. Phase 2 ESM rewrite introducing package.type and .cjs — a fundamental change to how Node.js resolves modules, behind an experimental flag. |
| vuejs/core #2532 New <script setup> and ref sugar implementation | 60High | Compiler sugar eliminating setup() return boilerplate. Well-contained compiler change with broad adoption surface — the new default way to write Vue 3 components. |
| tailwindlabs/tailwindcss #3905 Embed JIT engine directly into Tailwind | 57Medium | Build-time only change. JIT engine replaced full stylesheet generation with on-demand compilation. No runtime risk, but changed CSS output for every project. |
| facebook/react #13398 Add React.lazy() | 21Low | New additive API for code-splitting. Well-scoped, no changes to existing rendering paths. Lower risk than it looks — opt-in only. |
| trpc/trpc #1777 Implement new router and procedures | 80Critical | Core v10 router rewrite — flat procedure API, middleware chain, type inference. Complete breaking change from v9, but contained to a major version boundary. |
| babel/babel #5899 Add babel-plugin-syntax-typescript and babel-preset-typescript | 45Medium | New additive plugin. Introduced TypeScript stripping to Babel without the TS compiler. Isolated addition — no changes to existing transform pipeline. |
| prisma/prisma #2700 chore: fix 2.0.0 publish | 17Low | 8 files. The final publish fix for Prisma 2.0 GA — version bump and package.json corrections. The actual risk was in the hundreds of PRs that preceded it. |
What this means for your PRs
The open source examples are useful because they are public and well-documented. But the real value of deploy risk scoring is not on landmark PRs with 100 reviewers — it is on the ordinary PR that your team ships on a Thursday afternoon, reviewed by one person in fifteen minutes, that quietly introduces a breaking change nobody caught.
The React hooks PR had the entire React core team reviewing it. It scored 33 — correctly. The dangerous work was already done. The model does not replace review — it gives reviewers a number to orient around. When a PR scores high, the conversation is different. The questions are sharper. The bar for approval is higher.
Most teams do not have the React core team. They have two engineers, a Monday morning deadline, and a PR that looks fine.
That is who this is for.
Score any public PR — no account needed
Try the live risk demo against any public GitHub PR. Paste a URL, get a score in seconds. The same 36-signal model, running live.