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, and I'd argue the rules have aged better than any framework written since.
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.
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. The classic version goes like this: you rewrite the table component with virtualization, feel great about it, then open Chrome DevTools and find that 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 reached 1.0 in October 2025, and per the React team's announcement it memoizes your code at build time based on its own analysis, with useMemo and useCallback kept as escape hatches. That's Rule 1 showing up in tooling form: instead of guessing which components need manual memoization, the compiler analyzes your code and handles it. My bet is that most hand-written useMemo calls in older codebases were guesses that happened to be harmless, not fixes for anything anyone measured.
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.
You know the pull request where someone replaces a for...of loop with a hand-rolled while loop "for performance." Say the function runs 6 times during the entire page lifecycle. Six. The saving is a rounding error, and the code gets harder to read for every developer after that.
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.
Most performance work in frontend codebases is theater: people optimize what's visible in the code, a loop, a map call, rather than what's actually slow in the browser, a bloated bundle, an uncompressed API response, or a re-render triggered three layers up the component tree.
Want to actually measure things? Start with npx webpack-bundle-analyzer or your framework's equivalent. The typical finding is boring and large: a date library or utility belt pulled in whole for one function, sometimes twice under two names. Removing the duplicate saves more than any loop rewrite could. I'd run the analyzer before touching a single line of application logic.
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. This is the entire implementation of a product filter, and when I timed it in Node 22 on Apple silicon against 200 generated product names, it took about 0.01ms per call:
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. At a few hundred rows, I'd take that one-liner over any clever alternative, because there's nothing left for the clever version to win back.
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.
It plays out with TypeScript generics. Someone builds a type-level computation so complex that only they understand it. The compiler slows down on that file, and a new team member opens it and immediately closes it. Meanwhile, a simpler type with one or two overloads would often have covered most of the same cases.
The same pattern shows up with CSS Grid layouts. A grid template with six named areas and minmax() expressions nested three deep, where a straightforward flexbox column would do, is the same mistake in CSS. I'd ask what it costs the next developer who has to add a sidebar. Was the cleverness worth it? 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."
Rob Pike's fifth rule argues that data dominates: choose the right data structures and organize them well, and the algorithms operating on that data become almost self-evident, needing little cleverness to write correctly.
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.
Picture a dashboard whose API returns a flat array while the UI needs items grouped by category. Change the API to return them already grouped and the frontend transformation code mostly disappears. The types get simpler. The derived state hooks that held the transformation go with it. The data is 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.
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.
Pike's five rules reduce to two ideas repeated across five statements: measure before you optimize, and keep both your algorithms and your data structures as simple as the problem allows until evidence says otherwise.
| Rule | Says | Frontend example |
|---|---|---|
| 1 | You can't tell where a program spends its time; measure | Real bottleneck was an uncompressed API response, not the JSX map |
| 2 | Measure before you tune | Removed duplicate moment.js (327KB) instead of micro-optimizing a loop |
| 3 | Fancy algorithms are slow when N is small | One-line .filter() beat a trie for a 200-item dropdown |
| 4 | Fancy algorithms are buggier than simple ones | A 3-deep nested CSS Grid vs. a simple flexbox column |
| 5 | Data dominates; get the structure right and the code follows | Regrouping 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:
- Profile the actual bottleneck with
React.Profiler,performance.mark(), or a bundle analyzer before changing any code. - Check your N. If the dataset is a few hundred rows or fewer, a simple loop almost always beats a clever algorithm.
- 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.
Which Tutorials Should You Read Next?
- Exit cost - a stack criterion in the spirit of Pike's rules: what leaving costs, measured before you commit
- AI code review TypeScript
- AI coding tools 2026