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.
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.
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.
| 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.