Skip to content

Rob Pike's 5 Programming Rules From Today Still Work

Rob Pike wrote 5 rules of programming in 1989 at Bell Labs. Why they still apply to TypeScript, React, and modern frontend in 2026.

· · 9 min read

Updated: July 29, 2026

Dark screen filled with colorized source code representing timeless programming principles

Quick Take

Rob Pike wrote five programming rules at Bell Labs in 1989: measure before optimizing, don't guess at bottlenecks, write simple algorithms first. They remain the most practical debugging advice in existence and apply unchanged to TypeScript and React in 2026.

Rob Pike's Rules of Programming are five practical guidelines for writing fast, simple, correct code, written by Rob Pike, a co-creator of Unix, Plan 9, UTF-8, and the Go programming language, while he worked at Bell Labs. He typed them out sometime around 1989, while building operating systems alongside Ken Thompson. The rules showed up in Notes on Programming in C and later spread across Usenet.

That was 37 years ago. I still think about these rules weekly.

Why? Because they address programmer psychology, not technology. Languages change. Frameworks die. But the human urge to write clever code before checking if it's even slow? That's forever.

Quick take: Rob Pike's 5 rules of programming, written at Bell Labs around 1989, boil down to one principle: don't be clever until the numbers force you to. Measure before optimizing, keep algorithms simple when N is small, and let your data structures define your code. Every rule applies unchanged to TypeScript and React in 2026.

Rule 1: Why Can't You Tell Where a Program Spends Its Time?

Premature optimization is the act of speeding up code before measurement proves it's actually slow, and it is exactly what Pike's first rule warns against. Bottlenecks happen in surprising places, almost never where a developer assumes, so the rule is simple: don't guess, measure.

This one hits hard if you've ever spent three hours optimizing a React component that renders twice per page load while a 340KB JSON blob downloads on every route change. I did exactly that on a project last year, rewrote a table component with virtualization, felt great about it, then ran Chrome DevTools and realized the actual bottleneck was an uncompressed API response.

In TypeScript and React apps, the performance problems are almost never where you think. It's not the Array.map() in your JSX. It's the waterfall of useEffect calls, the unoptimized images, or the 14 re-renders triggered by a context provider sitting too high in the tree.

Use React.Profiler. Use performance.mark(). Use Lighthouse. Measure first, then fix what the numbers tell you to fix.

There's a new wrinkle worth naming here. React Compiler went stable in October 2025 and landed in Next.js 16 this year, which means it now auto-inserts the equivalent of useMemo and useCallback for you at build time. That's Rule 1 showing up in tooling form: instead of guessing which components need manual memoization, the compiler measures your dependency graph and handles it. I ripped out a dozen hand-written useMemo calls from an older dashboard after upgrading, and render counts didn't budge. Turns out most of them were guesses that happened to be harmless, not fixes for anything real.

Rule 2: Why Measure Before Tuning?

Rule 2 extends Rule 1 directly: don't optimize until you've profiled, and don't even profile until you know there's a problem worth solving in the first place. Skipping straight to profiling without evidence that something is slow wastes time just as surely as skipping straight to optimizing does.

I've reviewed pull requests where someone replaced a for...of loop with a hand-rolled while loop "for performance." The function ran 6 times total during the entire page lifecycle. Six. The optimization saved maybe 0.002ms and made the code harder to read for every developer after them.

Here's an uncomfortable opinion: most performance work in frontend codebases is theater. People optimize what's visible in the code rather than what's slow in the browser.

Want to actually measure things? Start with npx webpack-bundle-analyzer or your framework's equivalent. When I ran it on a Next.js 14 project, I found moment.js (327KB gzipped) still bundled alongside date-fns (12KB). Removing the duplicate saved more than any code-level optimization could.

A hand holding a digital stopwatch reading zero against a grey background
Photo by Curated Lifestyle on Unsplash

Rule 3: Why Are Fancy Algorithms Slow When N Is Small?

Pike argues that fancy algorithms have big constants. Until your dataset is large, a simple O(n) scan beats a clever O(log n) structure because the overhead of the clever approach dominates.

This rule maps directly to modern frontend work. How many items does your dropdown filter? 50? 200? You don't need a trie or a fuzzy search library weighing 48KB. This is the entire implementation I shipped for a 200-item product filter, and it still runs in under a millisecond on a five-year-old phone:

function filterProducts(products: Product[], query: string): Product[] {
  const q = query.trim().toLowerCase();
  if (!q) return products;
  return products.filter((p) => p.name.toLowerCase().includes(q));
}

No trie, no fuzzy scoring, no dependency. When the dataset stayed under a few hundred rows, that one-liner beat every clever alternative I tried.

The same applies to state management. Do you actually need a full state management library with normalized stores and selectors for an app with three pages? Probably not. useState and useContext carry you further than most people expect.

Most web apps deal with small N, a few hundred DOM nodes, a few dozen API responses, maybe a thousand table rows. At those sizes, readability wins over algorithmic sophistication every time.

Rule 4: Why Are Fancy Algorithms Buggier Than Simple Ones?

Even when N justifies complexity, Pike warns that complicated algorithms are harder to implement correctly. They have more edge cases, more off-by-one errors, more subtle failure modes.

I've watched this play out with TypeScript generics. Someone builds a type-level computation so complex that only they understand it. The TypeScript compiler takes 8 seconds to check the file. A new team member opens it and immediately closes it. Meanwhile, a simpler type with one or two overloads would have covered 95% of the same cases.

The same pattern shows up with CSS Grid layouts. I've debugged grid templates with 6 named areas and minmax() expressions nested three deep that could've been a straightforward flexbox column. Was it worth the 45 minutes the next developer spent trying to add a sidebar? No.

Simple code has a compounding advantage: it's easier to test, review, modify, and delete when requirements change.

Rule 5: Why Does Data Dominate?

Rule 5 is my favorite of the five, and Pike states it plainly: "If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident."

Think about how much React code exists just to reshape data that arrived in the wrong format. You get an API response structured one way, your components need it another way, so you write transformers and derived state hooks, all because the data shape was wrong from the start.

When I restructured a dashboard's API to return data already grouped by category instead of as a flat array, I deleted about 180 lines of frontend transformation code. The types got simpler. Two derived state hooks just disappeared. The data was doing the work.

This maps to TypeScript's utility types too. Pick, Omit, Record, these exist because data shape matters. Getting your interfaces right early saves you from writing code that compensates for bad structure later.

Fred Brooks said something similar in The Mythical Man-Month: "Show me your tables, and I won't usually need your flowchart." Pike and Brooks are pointing at the same truth from different decades.

A carefully balanced cairn of smooth stones on a pebble beach
Photo by Jeremy Thomas on Unsplash

What Do Pike's 5 Rules Look Like at a Glance?

Each of the five rules maps to a concrete frontend scenario, and seeing all five side by side makes the pattern obvious: every rule pushes toward measuring first and adding complexity only when evidence demands it. Rule 1 and Rule 2 are really the same idea stated twice, measure before you touch anything, while Rules 3 and 4 both argue that simple code wins until your data size proves otherwise. Rule 5 ties the other four together: get the data structures right, and the first four rules mostly take care of themselves because there's less code left to optimize or complicate. The table below pairs each rule with a real example pulled from the sections above.

RuleSaysFrontend example
1You can't tell where a program spends its time; measureReal bottleneck was an uncompressed API response, not the JSX map
2Measure before you tuneRemoved duplicate moment.js (327KB) instead of micro-optimizing a loop
3Fancy algorithms are slow when N is smallOne-line .filter() beat a trie for a 200-item dropdown
4Fancy algorithms are buggier than simple onesA 3-deep nested CSS Grid vs. a simple flexbox column
5Data dominates; get the structure right and the code followsRegrouping an API response deleted 180 lines of frontend transforms

How Do You Apply Pike's Rules Today?

Applying Pike's rules boils down to one meta-principle: fight the urge to be clever, write the obvious thing first, and measure whether it's actually fast enough before touching anything else. Three checks turn that principle into a repeatable habit:

  1. Profile the actual bottleneck with React.Profiler, performance.mark(), or a bundle analyzer before changing any code.
  2. Check your N. If the dataset is a few hundred rows or fewer, a simple loop almost always beats a clever algorithm.
  3. Look at your data structures before your algorithms. Reshaping the data often deletes more code than any optimization would.

None of that means you should never optimize, it means you should earn complexity through evidence rather than assume it up front.

If you're working in TypeScript 7 or React 19 or whatever comes next, the tools will keep changing. Pike's rules won't. The temptation to prematurely optimize has been constant since at least 1989, and probably since Knuth first warned about it in 1974.

Write boring code. Measure it. Fix what's actually slow. Trust your data structures. That's the whole playbook, and it hasn't needed an update in 37 years.

Frequently Asked Questions

What are Rob Pike's 5 Rules of Programming?
They cover measurement before optimization, avoiding fancy algorithms unless data proves you need them, using simple data structures, letting data shape your code, and writing straightforward programs first before trying to be clever.
Are Rob Pike's rules still relevant for modern languages like TypeScript?
Yes. The rules address human tendencies like premature optimization and over-engineering, which haven't changed since 1989. TypeScript, React, and Go codebases all benefit from the same principles.
How do Rob Pike's rules relate to Donald Knuth's optimization quote?
Pike's Rule 1 and Rule 2 echo Knuth's famous line that premature optimization is the root of all evil. Both argue you should measure before optimizing and keep code simple until profiling tells you otherwise.
Who is Rob Pike and why should programmers listen to him?
Rob Pike co-created Unix, Plan 9, UTF-8, and the Go programming language at Bell Labs and later Google. His decades of systems programming experience shaped these rules.