Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Not Invented Here

Antipattern

A recurring trap that causes harm — learn to recognize and escape it.

Rejecting an external solution and building your own, not because the homegrown version is better but because it’s yours.

Also known as: Reinventing the Wheel, NIH

The name comes from the dismissal that gives the trap its shape: a solution gets waved off because it was “not invented here.” The phrase is older than software. Hardware teams, research labs, and standards bodies have all rejected outside work for the same reason: pride of authorship, distrust of code they didn’t write, or the quiet belief that their problem is special. In software it shows up as a team writing its own date library, its own job queue, its own auth flow, when a maintained one already exists. Reinventing the Wheel is the same trap named from the other side: you’re rebuilding something the rest of the world solved years ago.

Symptoms

  • A standard problem — parsing dates, retrying with backoff, hashing passwords, validating email — has a hand-rolled implementation in your codebase.
  • The justification for building it is a feeling (“I didn’t trust the library”) rather than a named requirement the library failed to meet.
  • The homegrown version covers the happy path and a few cases the team hit, but not the edge cases a mature library has accumulated over years.
  • Nobody can name what the external option actually lacked, only that it “wasn’t quite right.”
  • An agent, asked for a small utility, writes its own implementation of something already in the standard library or an installed package.
  • The team spends review time on bugs that a well-known library fixed long ago.

Why It Happens

The honest version of Not Invented Here is caution. You don’t want a dependency you can’t inspect, can’t patch, and can’t trust with a security-sensitive job. That instinct is sound. The trap is when caution hardens into a reflex that rejects every outside option without doing the comparison.

Several forces feed it. Building is more fun than reading someone else’s docs. Authorship feels like control: code you wrote, you understand, and you can change. Estimating your own work low is a near-universal bias, so the rebuild always looks cheaper than it turns out to be. And a homegrown component scoped to exactly your needs really is simpler on day one, before the edge cases arrive.

Agents amplify the reflex in a specific way. When you ask a model for a function, the cheapest path it has is to generate one. It doesn’t feel the cost of a new dependency, doesn’t weigh community support, and doesn’t get pride or caution out of the decision. It just writes code. So an agent will happily produce a bespoke CSV parser, a custom retry loop, or a hand-rolled slug generator when one import would have done the job better. The model isn’t rejecting the library out of pride. It’s reinventing the wheel because writing fresh code is the default, and nobody told it to look first.

The Harm

The cost is rarely visible on the day you ship the homegrown version. It arrives later, as maintenance you now own alone.

A maintained library carries years of accumulated fixes for cases you haven’t hit yet: leap seconds, malformed input, the timezone that changed its rules in 2016, the encoding that breaks on one customer’s data. Your rebuilt version starts at zero and reacquires those fixes one production incident at a time. You’re not avoiding a dependency. You’re becoming the maintainer of one, without the community, the test suite, or the bug reports that made the original trustworthy.

The bill compounds. Every rebuilt component is code the team must understand, test, secure, and carry forward. Security-sensitive rebuilds (your own crypto, your own auth, your own input sanitizer) are the most dangerous, because the gap between “passes our tests” and “withstands a determined attacker” is exactly the gap a mature library spent years closing. Rolling your own here doesn’t reduce risk. It moves the risk somewhere nobody is watching.

With agents in the loop, the harm multiplies by volume. An agent that defaults to reinventing produces many small custom utilities across a codebase, each plausible, each subtly different from the standard, each now yours to maintain. No single one is a disaster. Together they are a codebase that solved the same solved problems a dozen incompatible ways.

The Way Out

Make the choice deliberate. Not Invented Here is a decision you fell into; the cure is to make it a decision you actually make, out loud, against the real alternatives.

Run the build-versus-buy call explicitly. Before building something a library already does, apply Build-vs-Don’t-Build Judgment: name what the external option lacks, name what building costs over its whole life, and decide on the comparison rather than the reflex. If you can’t name a concrete gap the library fails to fill, that’s your answer.

Name the real tradeoff. A homegrown component isn’t free; it’s a tradeoff you’re choosing to own. Write down what you give up by depending on the library (control, inspectability, one more thing to update) against what you give up by rebuilding it (every edge case the library already handles, plus the maintenance forever). The trade is sometimes worth it. Make it visible so it’s a choice and not a habit.

Let “boring” win for solved problems. Date math, retries, hashing, parsing, and serialization are solved. Reach for the maintained option first. Reserve your building energy for the part of the system that is actually yours: the part no library could have anticipated because it’s specific to your product.

Tell the agent to look before it builds. Agents reinvent by default, so change the default in your instructions. Ask the agent to check the standard library and installed dependencies first, and to justify any new implementation of a common utility. A short standing instruction like “prefer existing libraries for common functionality; explain why if you write your own” removes most machine-speed wheel-reinvention before it reaches review.

Tip

When an agent hands you a custom implementation of something familiar (a slugifier, a deep-clone, an email validator), treat it as a prompt to ask “what library does this already?” The agent had no reason to reach for one; you do.

How It Plays Out

A backend team needs to schedule retries for a flaky payment API. A well-tested library offers exponential backoff with jitter, circuit breaking, and configurable limits. The lead has been burned by an abandoned dependency once, so the team writes its own retry loop instead. It works in testing. Three months later it retries a non-idempotent charge during an outage and double-bills a batch of customers, because the homegrown loop never learned the distinction the library encodes in its defaults. The fix is to adopt the library they rejected, now with a production incident attached to the decision.

An agent is asked to add a function that turns article titles into URL slugs. Instead of using the slug utility already in the project’s dependencies, it generates a fresh one with a regex. The new version handles ASCII fine but mangles accented characters and emoji, producing two different slugs for titles the existing utility would have matched. A reviewer notices the codebase now has two slug functions and asks why. There’s no reason — the agent reinvented the wheel because writing a regex was its shortest path, and nobody told it to check what already existed. The team deletes the new function and points the call at the one that was already there.

A platform team distrusts an open-source feature-flag service and builds its own. For a year it’s fine. Then they need percentage rollouts, then per-tenant overrides, then an audit log of flag changes — each one a feature the rejected service shipped on day one. The team has slowly rebuilt the product they declined to adopt, except theirs has fewer tests and one maintainer. When that maintainer leaves, the homegrown flag system becomes organizational debt nobody chose to take on.

Sources

The phrase Not Invented Here predates software; it described the resistance of research and engineering organizations to adopting ideas developed elsewhere, and was studied in management literature on organizational behavior and innovation, notably Ralph Katz and Thomas J. Allen’s work on R&D team performance in the early 1980s.

William J. Brown, Raphael C. Malveau, Thomas J. Mowbray, and Hays W. “Skip” McCormick III’s AntiPatterns (Wiley, 1998) catalogs Reinventing the Wheel among the recurring development antipatterns, framing it as the cost of teams solving already-solved problems in isolation.