The Pareto Principle in Software: The Vital Few and the Long Tail
By Sergey Nosov
16 October 2026
On 2 October 2002, Steve Ballmer, then Microsoft’s chief executive, wrote about what the company had learned from the error reports that customers chose to send after crashes. “About 20 percent of the bugs cause 80 percent of all errors,” he reported, “and—this is stunning to me—one percent of bugs cause half of all errors.” With that data, he wrote, Microsoft could now direct its debugging where it would most improve customers’ experience.
That is the Pareto principle, or the 80/20 rule: a small share of the causes produces most of the effect. In software, a few modules collect most of the defects, a few functions take most of the run time, and a few commands account for much of what users do. Find that small share first, and effort goes much further.
The rule also misleads, in two ways. Its numbers are a rule of thumb, not a law, and a team that guesses which 20 percent matters, instead of measuring, can spend its effort on the wrong code. And some of the most expensive failures live in the other 80 percent: in features few people use, and in error handlers that run only after something has gone wrong. In 2014, the Heartbleed bug exposed server memory through OpenSSL’s heartbeat extension, a feature the project later removed altogether.
This article treats the 80/20 rule as a hypothesis to test, not a fact to assume. It checks the data for defects, run time, features, and effort, gives a script that measures your own split, and shows where the long tail is the job. The code was checked on Node.js 24.
Where the Name Comes From
Joseph M. Juran, a pioneer of quality management, named the principle and told the story in an essay titled “The Non-Pareto Principle; Mea Culpa.” The principle, he wrote, is shorthand for “the phenomenon that in any population which contributes to a common effect, a relative few of the contributors account for the bulk of the effect.” As a young engineer in the mid-1920s, he noticed the pattern, as had many others before him, in a long list of defects sorted by frequency.
In the late 1940s, while writing the first edition of his Quality Control Handbook (1951), Juran needed a short name for the idea. The handbook noted that the Italian economist Vilfredo Pareto had found wealth to be unequally distributed, and Juran gave the principle Pareto’s name. He also coined the phrase “vital few and trivial many.”
Then critics, among them Dorian Shainin, challenged the attribution, and Juran finally looked into what Pareto had done: work in economics, with models not meant for other fields. “I was forced to confess that I had mistakenly applied the wrong name to the principle,” Juran wrote. “Had I been structured along different lines,” he added, “assuredly I would have called it the Juran principle.” Other writers, he complained, had “fabricated some embellishments” and credited Pareto with things he did not do.
Pareto’s own subject was the distribution of income and wealth. The second volume of his Cours d’économie politique (1897) has a chapter called “La courbe des revenus,” the income curve. It works through tax statistics from England, Ireland, Prussia, Saxony, and several Italian towns, among others. On logarithmic scales, Pareto plotted each income level against the number of taxpayers above it. The points fell close to a straight line, with similar slopes across countries. Such a line marks what is now called a Pareto distribution, a kind of power law.
Many retellings, including a 2018 research paper on software defects, add that Pareto noticed 20 percent of Italians owned 80 percent of Italy’s land. His income chapter gives no such figure; its Italian data come from a family tax levied in several towns. Treat the tidy origin story the way this article treats every 80/20 claim: check the source.
Reading the Numbers
The 80 and the 20 measure different things: a share of the effects and a share of the causes. Nothing makes them add up to 100 percent. Ten percent of the files can hold 80 percent of the defects, or 20 percent can hold 60 percent. The pair is a convenient label for an uneven distribution, not a constant of nature.
The label does carry one useful piece of arithmetic. In a Pareto distribution, the split repeats inside the top group. If 20 percent of the causes produce 80 percent of the effects, then the top 20 percent of that 20 percent, or 4 percent of the causes, produce 64 percent. A few lines of JavaScript give the share for any fraction:
// Share of the effect from the
// top fraction p of causes, if
// every top group splits 80/20
const k =
Math.log(0.8) / Math.log(0.2);
const share = (p) => p ** k;
const tops = [0.2, 0.04, 0.01];
for (const p of tops) {
const s = share(p).toFixed(2);
console.log(p, s);
}
// Prints:
// 0.2 0.80
// 0.04 0.64
// 0.01 0.53
By that arithmetic, the top 1 percent of causes produces about 53 percent of the effects, close to the half that Microsoft reported for its bugs. Donald Knuth found something similar in a 1971 study of FORTRAN programs: “less than 4 per cent of a program generally accounts for more than half of its running time.”
Real data is not always that tidy. When Neil Walkinshaw and Leandro Minku tested defect data from one hundred GitHub projects in 2018, only 66 percent of the projects passed a test for a power law. The law, they concluded, “is far from universal.” Use the arithmetic to sanity-check a claim, not to predict your own numbers.
What the Data Shows in Software
Defects
A widely cited summary comes from Barry Boehm and Victor Basili, whose “Software Defect Reduction Top 10 List” appeared in IEEE’s Computer in January 2001. Item four reads: “About 80 percent of the defects come from 20 percent of the modules, and about half the modules are defect free.” In the studies behind it, 20 percent of the modules held between 60 and 90 percent of the defects, with a median of about 80 percent.
Item five concerns failures in operation: “About 90 percent of the downtime comes from, at most, 10 percent of the defects.” In one analysis of nine large IBM products, about 0.3 percent of the defects accounted for about 90 percent of the downtime.
Walkinshaw and Minku tested the claim on far more systems than the studies they replicated, which had covered one to three telecommunications systems each. They identified fixes by searching the commit messages of their one hundred projects, then counted the fixes each file took part in. The top 20 percent of files were involved in about 80 percent of the fixes: a mean of 80.53 percent and a median of 78.49 percent.
But the result depends on how a fix is counted. The principle holds, they found, “only if defects that incur fixes to multiple files count as multiple defects.” Most fixes touch more than one file, and a multi-file fix that touches a frequently fixed file usually touches rarely fixed ones too. Across the projects, the fixes that touched the most-fixed 20 percent of files involved a median of 32 percent of all files, not 20.
The authors offered a conjecture they planned to test. Many of the most-fixed files, they suggested, “are not especially defective, but are highly connected with the rest of the system,” so they change along with fixes made elsewhere. Simple metrics did not pick those files out reliably: churn, the count of all changes to a file, did better than size, but only for the files that changed most. And even if the most-fixed 20 percent could be predicted, they concluded, focusing on those files alone “would probably be misguided.”
Run Time
Knuth’s 1971 study, “An Empirical Study of FORTRAN Programs,” also proposed a name for a table of how often each statement runs: the program’s profile. “Programs typically have a very jagged profile, with a few sharp peaks,” he wrote. When his team ran its profiling program, FORDAP, on itself, they found it “was spending about half of its time in two loops that could be greatly simplified.” Simplifying them doubled its speed “in less than an hour’s work, without even looking at the rest of the program.”
Three years later, in “Structured Programming with go to Statements,” Knuth drew the lesson in a passage best known for one clause. “We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil,” he wrote. “Yet we should not pass up our opportunities in that critical 3%.” A good programmer, he went on, looks at the critical code only after it is identified, because programmers who use measurement tools find, without exception, that “their intuitive guesses fail.”
Jon Bentley reported the same pattern in the second edition of Programming Pearls (2000). “Profiling a program usually shows that most of the time is going to a few hot spots and that the rest of the code is rarely executed,” he wrote.
Features
Usage data shows a familiar head and a long tail. In April 2006, Jensen Harris published the five most-used commands in Word 2003 on his Office user interface blog: Paste, Save, Copy, Undo, and Bold. In opt-in usage data, those five made up about 32 percent of all command use, and Paste alone more than 11 percent. Beyond the top ten or so, though, the curve flattened out, and that made the redesign hard: “people really do use a lot of the breadth of Office,” Harris wrote.
Measuring the head paid off. The team had considered leaving the Cut, Copy, and Paste buttons off the Office 2007 Ribbon, because most people used keyboard shortcuts or the context menu. The data showed that people still clicked the Paste toolbar button more than any other button, so Paste became the first big button on Word’s first tab. “The data kept us from making a crucial, stupid mistake,” Harris wrote.
The tail is the warning. The common claim that 20 percent of the features deliver 80 percent of the value is harder to measure than it sounds. Usage data counts how often a feature is used, not how much it matters, and each user’s indispensable feature may sit in the flat part of the curve.
Effort
The fourth familiar 80/20 claim is about schedules: 80 percent of a product takes 20 percent of the time, and the last 20 percent takes the rest. A sharper version is the ninety-ninety rule. The Jargon File credits it to Tom Cargill of Bell Labs and says Jon Bentley popularized it in his column in Communications of the ACM in September 1985.
In the rule’s arithmetic, the first 90 percent of the code and the last 10 percent each take 90 percent of the development time. The percentages add up to 180 percent, which is the point: the end of a project takes far longer than its schedule allowed.
A version of the ninety-ninety rule has surfaced with AI coding tools. In December 2024, Addy Osmani described a “70% problem.” People who are not engineers, he wrote, echoing a tweet, can “get 70% of the way there surprisingly quickly, but that final 30% becomes an exercise in diminishing returns.”
Measure Your Own Split
Every number so far came from someone else’s software. The script below checks your own. It reads a repository’s history with git log and treats commits whose subject mentions a fix, a bug, or a defect as bug fixes. For each file that still exists, it counts the fixes that touched it. Then it finds the smallest set of files that holds 80 percent of those touches and asks how many fixes stayed entirely inside that set. It needs only Node.js and git.
// Usage: node pareto-fixes.mjs
// [repo] [since] [skip-regex]
import { execFileSync } from "node:child_process";
const [repo = ".", since = "2 years ago", skip] =
process.argv.slice(2);
const git = (...args) =>
execFileSync("git", ["-C", repo, ...args],
{ encoding: "utf8", maxBuffer: 2 ** 30 });
// Files in the current tree,
// minus any the regex skips
const skipped = skip ? new RegExp(skip) : null;
const tree = new Set(
git("ls-tree", "-r", "--full-tree", "--name-only", "HEAD")
.split("\n")
.filter((f) => f && !skipped?.test(f)));
// Commits whose subject looks
// like a bug fix, with the files
// they touched that still exist
const fixWord = /\b(fix(es|ed|ing)?|bugs?|bugfix(es)?|defects?)\b/i;
const fixes = git("log", "--no-merges",
"--no-renames", "--name-only",
`--since=${since}`, "--format=%x00%s")
.split("\0").slice(1)
.map((entry) => entry.split("\n"))
.filter(([subject]) => fixWord.test(subject))
.map(([, ...files]) => files.filter((f) => tree.has(f)))
.filter((files) => files.length > 0);
if (fixes.length === 0) {
console.log("No fix commits found.");
process.exit(0);
}
// Fix count per file, most first
const count = new Map();
for (const f of fixes.flat()) {
count.set(f, (count.get(f) ?? 0) + 1);
}
const ranked = [...count].sort((a, b) => b[1] - a[1]);
const total = ranked.reduce((s, [, n]) => s + n, 0);
// Smallest set of files holding
// 80% of all fix touches
const hot = new Set();
let held = 0;
for (const [file, n] of ranked) {
if (held >= 0.8 * total) break;
hot.add(file);
held += n;
}
const inside = fixes
.filter((files) => files.every((f) => hot.has(f))).length;
const pct = (part, whole) =>
`${Math.round((100 * part) / whole)}%`;
console.log("Fix commits:", fixes.length);
console.log("Files with no fix:",
pct(tree.size - count.size, tree.size));
console.log("80% of touches in:",
pct(hot.size, tree.size), "of files");
console.log("Fixes entirely inside:",
pct(inside, fixes.length));
Run it from inside a clone with its full history. All three arguments are optional: the repository path, a time window in quotes, and a regular expression for files to skip. Pointed at the last two years of vuejs/core, the core repository of the Vue.js framework, on 15 October 2026, it printed:
Fix commits: 376
Files with no fix: 60%
80% of touches in: 20% of files
Fixes entirely inside: 69%
The classic rule holds almost to the decimal: 20 percent of the files took 80 percent of the fix touches, and 60 percent took none in those two years. Yet 31 percent of the fixes reached outside the hot files. Many files tie at the 80 percent line with two touches each, and a different choice among them can move that share by a few points. A review or test policy aimed only at the hot files would have missed part of about one fix in three.
Skip the folders named __tests__ by passing that name as the third argument, and the hot set shrinks to 17 percent of the remaining files. About one fix in five still reaches outside it.
Over the last ten years, Express, Flask, and Requests put 80 percent of the fix touches in 15 to 18 percent of their files. Between 18 and 25 percent of their fixes reached outside those files. The script does not follow renames, though. When Requests moved its code into a src folder in August 2023, earlier fixes to the moved files dropped out of every count.
Read the output as a lead, not a verdict. A keyword match can miss fixes that do not say so and catch commits that fix no bug, as Walkinshaw and Minku note of their own method. A touch is not a defect, either: a file can change in a fix only to keep up with an interface that changed elsewhere. Nor is a commit a fix: one fix can span several commits, and one commit can mix a fix with other changes.
Hindsight flatters the result, too. The hot set above was picked from the same fixes it was scored on. A hot set picked in advance did worse. The files that were hot in vuejs/core from October 2022 to October 2024 took only about 60 percent of the fix touches in the two years since. Of today’s hot files, only 56 percent were hot then.
Where the Long Tail Is the Job
Juran later renamed the trivial many the useful many. Applied to software, the 80/20 rule usually ranks causes by their share of a count: fixes, crashes, profile samples, or uses. Some of the most important work concerns things that happen rarely, and frequency is the wrong scale for them. My Software Development Principles series puts it in one sentence: “The principle guides proportional allocation, not absolute neglect.” Four places need their own rules.
Security
An attacker does not care how often your users take a code path; one reachable flaw is enough. Heartbleed (CVE-2014-0160) was a missing bounds check in OpenSSL’s implementation of the heartbeat extension, a keep-alive mechanism that RFC 6520 defined in February 2012.
The bug shipped in OpenSSL 1.0.1 in March 2012. Anyone who could connect to a vulnerable server could read its memory, private keys included. A malicious server could do the same to a vulnerable client. The extension was on by default. In “The Matter of Heartbleed” (2014), Zakir Durumeric and colleagues called it “the largely unused extension” and noted that turning it off meant recompiling OpenSSL. OpenSSL fixed the bug in April 2014 and later removed heartbeats in stages, the last of them in 2021.
Log4Shell (CVE-2021-44228), disclosed in December 2021, also lived in a feature that was on by default. Log4j 2 expanded lookups written inside log messages, including JNDI lookups that could load code from a remote LDAP server. An attacker who could control a logged string, such as a request header the application wrote to its log, could run code on the server. Apache’s fixes turned message lookups off by default in Log4j 2.15.0 and removed them in 2.16.0, as its security page and release notes record.
Usage tells you what to optimize; it does not tell you what an attacker can reach. A threat model does. Its data-flow diagram marks every place where data crosses a trust boundary, popular or not; the article on STRIDE on this site shows how to draw one. Then remove or disable what nobody needs; the article on deleting code covers how.
The rule does work for the patch queue, because exploitation is concentrated too. Of 192,035 published vulnerabilities, 6.4 percent were observed being exploited between July 2016 and December 2022, according to the researchers behind FIRST’s Exploit Prediction Scoring System (EPSS). Every day, EPSS publishes the probability that each published vulnerability will be exploited in the wild in the next thirty days, so teams can patch the likeliest first. FIRST calls EPSS “not a complete risk score,” because the model knows neither your environment nor the damage an exploit would do.
Error Handling
Error handlers run only after something has gone wrong, so most of them sit far out in the tail of a profile. In a 2014 OSDI paper, Ding Yuan and colleagues at the University of Toronto studied 198 randomly sampled, user-reported failures in Cassandra, HBase, HDFS, Hadoop MapReduce, and Redis.
Forty-eight of the failures were catastrophic, affecting all or most users. “Almost all (92%) of the catastrophic system failures are the result of incorrect handling of non-fatal errors explicitly signaled in software,” they found. In 58 percent of the catastrophic failures, simple tests of the error-handling code could easily have exposed the fault. In 35 percent, the fault was one of three trivial patterns:
- a handler that was empty or only logged the error;
- a handler that aborted the cluster on an overly general exception;
- a handler with “FIXME” or “TODO” in its comments.
A profile that counts every statement, as Knuth’s did, also finds the code that never ran. “Statements with zero frequency indicate untested sections of the program,” Knuth noted. A coverage report is the modern version. Test error paths on purpose, with injected faults where you can, and in review, ask why any catch block is empty.
Rare but Essential Features
Some features are rarely used by design: account recovery, data export, a restore from backup. A usage chart puts them in the tail, yet on the day someone needs one, it is the only feature that matters. Weight them by the cost of failure, not by frequency. A minority use case is not a trivial one, either: for someone who uses a screen reader, a button it cannot reach is a feature that does not exist.
The Last 20 Percent
The ninety-ninety rule describes a trap as well as a schedule. When the common cases work early, it is tempting to call the product done and the rest polish. Some of the rest is polish. Much of it is the security, error handling, and edge cases above. Files outside the hot set still ship, too, and debt there grows out of sight until a change or an attacker reaches it. Decide what “done” means before the schedule tightens, and never use the 80/20 rule to justify cutting corners.
Five Steps for Using the Rule
None of this argues against the rule, only for using it with data and with its limits in view. The five steps below are a lens for decisions, not a process to follow: whenever you decide where to spend time, ask whether you are spending it on the vital few.
- Identify the vital few with data. Count fixes, crashes, profile samples, usage events, or support tickets before you decide what matters. Start from the measurement, not from what the team remembers.
- Prioritize by impact. Rank the work by its share of the effect, and ask of each item whether it falls in the high-impact 20 percent. Weigh cost as well as count, as the IBM downtime figure above shows. Rank security, error handling, and rare but essential features separately, by risk.
- Execute with most of the capacity on the top items. Most, not all: reserve a fixed share for the long tail.
- Validate the effect. Measure again after the work, and check whether the expected share of crashes, run time, or defects went away. By Ballmer’s account, error reporting helped Microsoft eliminate more than half of all Office XP errors with Office XP Service Pack 2.
- Iterate. The vital few move as the system changes: in
vuejs/core, a hot set picked two years earlier caught only about 60 percent of the next two years’ fix touches. Measure again every release or every quarter.
A Checklist for the 80/20 Rule
- Is your 20 percent measured or assumed? Measure before you rank.
- Do fixes stay inside the hot files? Check, then weight reviews and tests toward them, never only at them.
- Did you profile before you optimized? Work on the hot spots the profile shows, then measure again.
- Are security and error handling ranked by risk? Not by how often the code runs. Test the error handlers on purpose.
- Is anything enabled that nobody needs? Remove it or turn it off; it is still attack surface.
- Is the patch queue ranked by exploitation? Fix first the vulnerabilities that attackers use, on systems they can reach.
- When did you last measure? The vital few move.
Further Reading
- The Non-Pareto Principle; Mea Culpa (Joseph M. Juran, Juran Institute)
- Software Defect Reduction Top 10 List (Barry Boehm and Victor R. Basili, Computer, January 2001)
- Are 20% of Files Responsible for 80% of Defects? (Neil Walkinshaw and Leandro Minku, ESEM, October 2018)
- Simple Testing Can Prevent Most Critical Failures (Ding Yuan and others, OSDI, October 2014)