> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
Always preferred Perlis' version, that might be slightly over-used in functional programming to justify all kinds of hijinks, but with some nuance works out really well in practice:
> 9. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.
Intermernet•Mar 18, 2026
I believe the actual quote is:
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
bfivyvysj•Mar 18, 2026
This is the biggest issue I see with AI driven development. The data structures are incredibly naive. Yes it's easy to steer them in a different direction but that comes at a long term cost. The further you move from naive the more often you will need to resteer downstream and no amount of context management will help you, it is fighting against the literal mean.
andsoitis•Mar 18, 2026
> This is the biggest issue I see with AI driven development. The data structures are incredibly naive.
Bill Gates, for example, always advocated for thinking through the entire program design and data structures before writing any code, emphasizing that structure is crucial to success.
neocron•Mar 18, 2026
Ah Bill Gates, the epitome of good software
dotancohen•Mar 18, 2026
Yes, actually. Gates wrote great software.
Microsoft is another story.
jll29•Mar 18, 2026
And Paul Allen wrote a whole Altair emulator so that they could use an (academic) Harvard computer for their little (commercial) project and test/run Bill Gates' BASIC interpreter on it.
andsoitis•Mar 18, 2026
> Ah Bill Gates, the epitome of good software
While developing Altair BASIC, his choice of data structures and algorithms enabled him to fit the code into just 4 kilobytes.
PaulDavisThe1st•Mar 18, 2026
I'd like to see Gates or anyone else do that for a project that lasts (at least) a quarter century and sees a many-fold increase in CPU speed, RAM availability, disk capacity etc.
Intermernet•Mar 18, 2026
Naive doesn't mean bad. 99% of software can be written with understood, well documented data structures. One of the problems with ai is that it allows people to create software without understanding the trade offs of certain data structures, algorithms and more fundamental hardware management strategies.
You don't need to be able to pass a leet code interview, but you should know about big O complexity, you should be able to work out if a linked list is better than an array, you should be able to program a trie, and you should be at least aware of concepts like cache coherence / locality. You don't need to be an expert, but these are realities of the way software and hardware work. They're also not super complex to gain a working knowledge of, and various LLMs are probably a really good way to gain that knowledge.
dotancohen•Mar 18, 2026
Then don't let the AI write the data structures. I don't. I usually don't even let the AI write the class or method names. I give it a skeleton application and let it fill in the code. Works great, and I retain knowledge of how the application works.
nostrademons•Mar 18, 2026
The rule may not hold with AI driven development. The rule exists because it's expensive to rewrite code that depends on a given data structure arrangement, and so programmers usually resort to hacks (eg. writing translation layers or views & traversals of the data) so they can work with a more convenient data structure with functionality that's written later. If writing code becomes free, the AI will just rewrite the whole program to fit the new requirements.
This is what I've observed with using AI on relatively small (~1000 line) programs. When I add a requirement that requires a different data structure, Claude will happily move to the new optimal data structure, and rewrite literally everything accordingly.
I've heard that it gets dicier when you have source files that are 30K-40K lines and programs that are in the million+ line range. My reports have reported that Gemini falls down badly in this case, because the source file blows the context window. But even then, they've also reported that you can make progress by asking Gemini to come up with the new design, and then asking it to come up with a list of modules that depend upon the old structure, and then asking it to write a shim layer module-by-module to have the old code use the new data structure, and then have it replace the old data structure with the new one, and then have it remove the shim layer and rewrite the code of each module to natively use the new data structure. Basically, babysit it through the same refactoring that an experienced programmer would use to do a large-scale refactoring in a million+ line codebase, but have the AI rewrite modules in 5 minutes that would take a programmer 5 weeks.
mock-possum•Mar 18, 2026
I’m really going to need to see both. There’s a lot of business logic that simply is not encoded in a data storage model.
mosura•Mar 18, 2026
Perlis is just wrong in that way academics so often are.
Pike is right.
Intermernet•Mar 18, 2026
Hang on, they mostly agree with each other. I've spoken to Rob Pike a few times and I never heard him call out Perlis as being wrong. On this particular point, Perlis and Pike are both extending an existing idea put forward by Fred Brooks.
mosura•Mar 18, 2026
Perlis absolutely is not saying the same thing, and as the commenter notes the functional community interpret it in a particularly extreme way.
I would guess Pike is simply wise enough not to get involved in such arguments.
jacquesm•Mar 18, 2026
Perlis is right in the way that academics so often are and Pike is right in the way that practitioners often are. They also happen to be in rough agreement on this, unsurprisingly so.
hrmtst93837•Mar 18, 2026
Treating either as gospel is lazy, Perlis was pushing back on dogma and Pike on theory, while legacy code makes both look cleaner on paper.
AnimalMuppet•Mar 18, 2026
Could you be more specific?
mosura•Mar 18, 2026
Promoting the idea of one data structure with many functions contradicts:
“If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident.”
And:
“Use simple algorithms as well as simple data structures.”
A data structure general enough to solve enough problems to be meaningful will either be poorly suited to some problems or have complex algorithms for those problems, or both.
There are reasons we don’t all use graph databases or triple stores, and rely on abstractions over our byte arrays.
AnimalMuppet•Mar 18, 2026
I think you are badly misinterpreting the statement.
Let's say you're working for the DMV on a program for driver's licenses. The idea is to use one structure for driver's license data, as opposed to using one structure for new driver's licenses, a different one for renewals, and yet a third for expired ones, and a fourth one for name changes.
It is not saying that you should use byte arrays for driver's license records, so that you can use the same data structure for driver's license data and missile tracks. Generalize within your program, not across all possible programs running on all computers.
mosura•Mar 18, 2026
Your admittedly exaggerated example is arguing against the entire concept of relational databases, which is not a winning proposition.
You do not write programs with one map of id to thing as you are suggesting here.
rsav•Mar 18, 2026
There's also:
>I will, in fact, claim that the difference between a bad programmer
and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
-- Linus Torvalds
mikepurvis•Mar 18, 2026
I think this is sometimes a barrier to getting started for me. I know that I need to explore the data structure design in the context of the code that will interact with it and some of that code will be thrown out as the data structure becomes more clear, but still it can be hard to get off the ground when me gut instinct is that the data design isn't right.
This kind of exploration can be a really positive use case for AI I think, like show me a sketch of this design vs that design and let's compare them together.
ignoramous•Mar 18, 2026
> This kind of exploration can be a really positive use case for AI I think
Not sure if SoTA codegen models are capable of navigating design space and coming up with optimal solutions. Like for cybersecurity, may be specialized models (like DeepMind's Sec-Gemini), if there are any, might?
I reckon, a programmer who already has learnt about / explored the design space, will be able to prompt more pointedly and evaluate the output qualitatively.
Yeah key word is exploration. It's not "hey Claude write the design doc for me" but rather, here's two possible directions for how to structure my solution, help me sketch each out a bit further so that I can get a better sense what roadblocks I may hit 50-100 hours into implementation when the cost of changing course is far greater.
sph•Mar 18, 2026
AI is terrible for this.
My recommendation is to truly learn a functional language and apply it to a real world product. Then you’ll learn how to think about data, in its pure state, and how it is transformed to get from point A to point B. These lessons will make for much cleaner design that will be applicable to imperative languages as well.
Or learn C where you do not have the luxury of using high-level crutches.
sph•Mar 18, 2026
From what I understand from the vibe coders, they tell a machine what the code should do, but not how it should do it. They leave the important decisions (the shape of data) to an LLM and thus run afoul of this, which is gonna bite them in the ass eventually.
Zamicol•Mar 18, 2026
That is excellent. I'm putting that in my notes.
aleph_minus_one•Mar 18, 2026
> >I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
> -- Linus Torvalds
What about programmers
- for whom the code is a data structure?
- who formulate their data structures in a way (e.g. in a very powerful type system) such that all the data structures are code?
- who invent a completely novel way of thinking about computer programs such that in this paradigm both code and data structures are just trivial special cases of some mind-blowing concept ζ of which there exist other special cases that are useful to write powerful programs, but these special cases are completely alien from anything that could be called "code" or "data (structures)", i.e. these programmers don't think/worry about code or data structures, but about ζ?
mpalmer•Mar 18, 2026
Was the "J" short for "Cassandra"?
When someone says "I want a programming language in which I need only say what I wish done," give him a lollipop.
Hendrikto•Mar 18, 2026
I feel like these are far more vague and less actionable than the 5 Pike rules.
DaleBiagio•Mar 18, 2026
" 9. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures."
That's great
mchaver•Mar 18, 2026
I find languages like Haskell, ReScript/OCaml to work really well for CRUD applications because they push you to think about your data and types first. Then you think about the transformations you want to make on the data via functions. When looking at new code I usually look for the types first, specifically what is getting stored and read.
embedding-shape•Mar 18, 2026
Similarly, that approach works really well in Clojure too, albeit with a lot less concern for types, but the "data and data structures first" principle is widespread in the ecosystem.
mchaver•Mar 18, 2026
I've heard good things about Clojure, and it'ss different from what I am used to (bonus points because I like an intellectual challenge), so trying it out is definitely on my todo list.
linhns•Mar 18, 2026
Nice to see Perlis mentioned once in a while. Reading SICP again, still learning new things.
I’m with Rich Hickey on this one, though I generally prefer my data be statically typed.
0xpgm•Mar 18, 2026
Sure, static typing adds some sort of process that provides a coarse interpretation of the data.
johnmaguire•Mar 18, 2026
Hm, not sure. Data on its own (say, a string of numbers) might be meaningless - but structured data? Sure, there may be ambiguity but well-structured data generally ought to have a clear/obvious interpretation. This is the whole idea of nailing your data structures.
0xpgm•Mar 18, 2026
Yeah, structured data implies some processing on raw data to improve its meaning. Alan Kay seems to want to push this idea to encapsulate data with rich behaviour.
gregw2•Mar 18, 2026
Thanks for the pointer to this 2016 dialog!
One part of it has interesting new resonance in the era of agentic LLMs:
alankay on June 21, 2016 | root | parent | next [–]
This is why "the objects of the future" have to be ambassadors that can negotiate with other objects they've never seen.
Think about this as one of the consequences of massive scaling ...
Nowdays rather than the methods associated with data objects, we are dealing with "context" and "prompts".
0xpgm•Mar 18, 2026
Quite a nice insight there!
I should probably be thinking more in this direction.
JanisErdmanis•Mar 18, 2026
With 100 functions and one datastructure it is almost as programming with a global variables where new instance is equivalent to a new process. Doesn’t seem like a good rule to follow.
embedding-shape•Mar 18, 2026
The scope of where that data structure or functions are available is a different concern though, "100 functions + 1 data structure" doesn't require globals or private, it's a separate thing.
JanisErdmanis•Mar 18, 2026
One can always look as global variables equivalent to a context object that’s is passed in every function. It’s just a syntactic difference whether one constructs such data structure or uses it implicitly via globals.
What I am getting at is that when one has such gigantic data structure there is no separation of concerns.
CyberDildonics•Mar 18, 2026
Does one need one's separation of concerns if one's concerns shouldn't be separated in the in the first place?
Anytime one has access to a database one has access to one large global data structure that one can access from anywhere is a program.
This same concept goes for one's global state in one's game if one is making a game.
JanisErdmanis•Mar 18, 2026
Separation of concerns is still a valid paradigm with a single global datastructure like GUI, Microservice, Database and etc. In such situation one can still seperate concerns via composing the global datastructure from a smaller units and define methods with respect to thoose smaller units. In that way one does not need to wonder whether there are some unattended side effects when calling a function that mutates the state.
CyberDildonics•Mar 18, 2026
Seems like one is backpedaling because one was just talking about one's separation of one's concerns and now one is defending one's separation of concerns with respect to one's global data structure.
JanisErdmanis•Mar 18, 2026
I still firmly believe that one ctx object and hundred functions/methods is as bad as programming with plain variables defined in the global scope. If the ctx is composed from smaller data structures with whom the functions are defined, then all is good. This is the opposite of the rule.
CyberDildonics•Mar 18, 2026
But why?
You keep saying you believe it, but that is literally what a database is, game state manipulation, string manipulation, iterator algorithms, list comprehensions, range algorithms, image manipulations, etc. These are all instances where you use the same data structures over and over with as many algorithms and functions and you need.
alberto-m•Mar 18, 2026
This quote from “Dive into Python” when I was a fresh graduate was one of the most impacting lines I ever read in a programming book.
> Busywork code is not important. Data is important. And data is not difficult. It's only data. If you have too much, filter it. If it's not what you want, map it. Focus on the data; leave the busywork behind.
bandrami•Mar 18, 2026
Also basically everything DHH ever said (I stopped using Rails 15 years ago but just defining data relationships in YAML and typing a single command to get a functioning website and database was in fact pretty cool in the oughts).
Pxtl•Mar 18, 2026
As much as relational DBs have held back enterprise software for a very long time by being so conservative in their development, the fact that they force you to put this relationship absolutely front-of-mind is excellent.
embedding-shape•Mar 18, 2026
I'd personally consider "persistence" AKA "how to store shit" to be a very different concern compared to the data structures that you use in the program. Ideally, your design shouldn't care about how things are stores, unless there is a particular concern for how fast things read/writes.
mosura•Mar 18, 2026
Often significant improvements to every aspect of a system that interacts with a database can be made by proper design of the primary keys, instead of the generic id way too many people jump to.
The key difficulty is identifying what these are is far from obvious upfront, and so often an index appears adjacent to a table that represents what the table should have been in the first place.
embedding-shape•Mar 18, 2026
I guess that might be true also, to some extent. I guess most of the times I've seen something "messy" in software design, it's almost always about domain code being made overly complicated compared to what it has to do, and almost never about "how does this domain data gets written/read to/from a database", although it's very common. Although of course storage/persistence isn't non-essential, just less common problem than the typical design/architecture spaghetti I encounter.
Pxtl•Mar 18, 2026
I'm a firm believer in always using an auto-generated surrogate key for the PK because domain PKs always eventually become a pain point. The problem is that doing so does real damage to the ergonomics of the DB.
This is why I fundamentally find SQL too conservative and outdated. There are obvious patterns for cross-cutting concerns that would mitigate things like this but enterprise SQL products like Oracle and MS are awful at providing ways to do these reusable cross-cutting concerns consistently.
Pxtl•Mar 18, 2026
I meant to reply to a different comment originally, specifically the one including this quote from Torvalds:
> Good programmers worry about data structures and their relationships.
> -- Linus Torvalds
I was specifically thinking about the "relationship" issues. The worst messes to fix are the ones where the programmer didn't consider how to relate the objects together - which relationships need to be direct PK bindings, which can be indirect, which things have to be cached vs calculated live, which things are the cache (vs the master copy), what the cardinality of each relationship is, which relationships are semantically ownerships vs peers, which data is part of the system itself vs configuration data vs live, how you handle changes to the data, (event sourcing vs changelogging vs vs append-only vs yolo update), etc.
Not quite "data structures" I admit but absolutely thinking hard about the relationship between all the data you have.
SQL doesn't frame all of these questions out for you but it's good getting you to start thinking about them in a way you might not otherwise.
tangus•Mar 18, 2026
Aren't they basically saying opposite things? Perlis is saying "don't choose the right data structure, shoehorn your data into the most popular one". This advice might have made sense before generic programming was widespread; I think it's obsolete.
Rygian•Mar 18, 2026
Pike: strongly typed logic is great!
Perlin: stringly typed logic is great!
embedding-shape•Mar 18, 2026
> Perlis is saying "don't choose the right data structure, shoehorn your data into the most popular one"
I don't take it like that. A map could be the right data structure for something people typically reach for classes to do, and then you get a whole bunch of functions that can already operate on a map-like thing for free.
If you take a look at the standard library and the data structures of Clojure you'd see this approach taken to a somewhat extreme amount.
TYPE_FASTER•Mar 18, 2026
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
If I have learned one thing in my 30-40 years spent writing code, it is this.
seanalltogether•Mar 18, 2026
I agree. The biggest lesson I try to drive home to newer programmers that join my projects is that its always best to transform the data into the structure you need at the very end of the chain, not at the beginning or middle. Keep the data in it's purest form and then transform it right before displaying it to the user, or right before providing it in the final api for others to consume.
You never know how requirements are going to change over the next 5 years, and pure structures are always the most flexible to work with.
bluGill•Mar 18, 2026
Related: your business logic should work on metric units. It is a UI concern if the user wants to see some other measurement system. Convert to feet, chains, cubits... or whatever obscure measurement system the user wants at display time. (if you do get an embedded device that reports non-metric units convert when it comes in - you will get a different device in the future that reports different units anyway)
You still have to worry about someone using kg when you use g, but you avoid a large class of problems and make your logic easier.
dcuthbertson•Mar 18, 2026
But doesn't No. 2 directly conflict with Pike's 5th rule? It seems to me these are all aphorisms that have to be taken with a grain of salt.
> 2. Functions delay binding; data structures induce binding. Moral: Structure data late in the programming process.
jerf•Mar 18, 2026
As I'm sure more and more people are using AI to document old systems, even just to get a foothold in them personally if they don't intend to share it, here's a hint related to that: By default, if you fire an AI at a programming base, at least in my experience you get the usual documentation you expect from a system: This is the list of "key modules", this module does this, this module does that, this module does the other thing.
This is the worst sort of documentation; technically true but quite unenlightening. It is, in the parlance of the Fred Brooks quote mentioned in a sibling comment, neither the "flowchart" nor the "tables"; it is simply a brute enumeration of code.
To which the fix is, ask for the right thing. Ask for it to analyze the key data structures (tables) and provide you the flow through the program (the flowchart). It'll do it no problem. Might be inaccurate, as is a hazard with all documentation, but it makes as good a try at this style of documentation as "conventional" documentation.
Honestly one of the biggest problems I have with AI coding and documentation is just that the training set is filled to the brim with mediocrity and the defaults are inferior like this on numerous fronts. Also relevant to this conversation is that AI tends to code the same way it documents and it won't have either clear flow charts or tables unless you carefully prompt for them. It's pretty good at doing it when you ask, but if you don't ask you're gonna get a mess.
(And I find, at least in my contexts, using opus, you can't seem to prompt it to "use good data structures" in advance, it just writes scripting code like it always does and like that part of the prompt wasn't there. You pretty much have to come back in after its first cut and tell it what data structures to create. Then it's really good at the rest. YMMV, as is the way of AI.)
kleiba•Mar 18, 2026
I believe the "premature evil" quote is by Knuth, not Hoare?!
swiftcoder•Mar 18, 2026
Potentially its by either (or even both independently). Knuth originally attributed it to Hoare, but there's no paper trail to demonstrate Hoare actually coined it first
Intermernet•Mar 18, 2026
Turns out that premature attribution is actually the root of all evil...
Bengalilol•Mar 18, 2026
Every empirical programmer will, at some point, end up yelling it out loud (too).
bsenftner•Mar 18, 2026
Obvious. Why the elevation of the obvious?
bazoom42•Mar 18, 2026
Definitely not obvious to everybody.
DrScientist•Mar 18, 2026
I think for people starting out - rule 5 isn't perhaps that obvious.
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
If want to solve a problem - it's natural to think about logic flow and the code that implements that first and the data structures are an after thought, whereas Rule 5 is spot on.
Conputers are machines that transform an input to an output.
mosura•Mar 18, 2026
> If want to solve a problem - it's natural to think about logic flow and the code that implements that first and the data structures are an after thought, whereas Rule 5 is spot on.
It is?
How can you conceive of a precise idea of how to solve a problem without a similarly precise idea of how you intend to represent the information fundamental to it? They are inseparable.
DrScientist•Mar 18, 2026
Obviously they are linked - the question is where do you start your thinking.
Do you start with the logical task first and structure the data second, or do you actually think about the data structures first?
Let's say I have a optimisation problem - I have a simple scoring function - and I just want to find the solution with the best score. Starting with the logic.
for all solutions, score, keep if max.
Simple eh? Problem is it's a combinatorial solution space. The key to solving this before the entropic death of the universe is to think about the structure of the solution space.
TheOtherHobbes•Mar 18, 2026
I mean - no. If you're coming to a completely new domain you have to decide what the important entities are, and what transformations you want to apply.
Neither data structures nor algorithms, but entities and tasks, from the user POV, one level up from any kind of implementation detail.
There's no point trying to do something if you have no idea what you're doing, or why.
When you know the what and why you can start worrying about the how.
Iff this is your 50th CRUD app you can probably skip this stage. But if it's green field development - no.
DrScientist•Mar 18, 2026
Sure context is important - and the important context you appear to have missed is the 5 rules aren't about building websites. It's about solving the kind of problems which are easy to state but hard to do (well) .
eg sort a list.
praptak•Mar 18, 2026
A good chunk of great advice is obvious things that people still fail to do.
That's why a collection of "obvious" things formulated in a convincing way by a person with big street cred is still useful and worth elevating.
pm215•Mar 18, 2026
Also, "why these 5 in particular" is definitely not obvious -- there are a great many possible "obvious in some sense but also true in an important way" epigrams to choose from (the Perlis link from another comment has over a hundred). That Pike picked these 5 to emphasise tells you something about his view of programming, and doubly so given that they are rather overlapping in what they're talking about.
pjc50•Mar 18, 2026
You've got to elevate some obviously correct things, otherwise social media will fill the void with nonobviously incorrect things.
mosura•Mar 18, 2026
Better to have 100 comments on one topic than 10 comments on 10 topics.
HunterWare•Mar 18, 2026
Can't be but so obvious if the first comment I saw here was that the first two rules didn't seem so important. =)
knorker•Mar 18, 2026
I'd call it more derivative than obvious.
"Why quote someone who's just quoting someone else?" — Michael Scott — knorker
How good is your model at picking good data structures?
There’s several orders of magnitude less available discussion of selecting data structures for problem domains than there is code.
If the underlying information is implicit in high volume of code available then maybe the models are good at it, especially when driven by devs who can/will prompt in that direction. And that assumption seems likely related to how much code was written by devs who focus on data.
skydhash•Mar 18, 2026
> There’s several orders of magnitude less available discussion of selecting data structures for problem domains than there is code.
I believe that’s what most algorithms books are about. And most OS book talks more about data than algorithms. And if you watch livestream or read books on practical projects, you’ll see that a lot of refactor is first selecting a data structure, then adapt the code around it. DDD is about data structure.
ozgrakkurt•Mar 18, 2026
Would be cool to see the live reaction of Rob Pike to this comment
andsoitis•Mar 18, 2026
> Would be cool to see the live reaction of Rob Pike to this comment
Based on everything public, Pike is deeply hostile to generative AI in general:
- ideologically, he's spent his career chasing complexity reduction, adovcating for code sobriety, resource efficiency, and clarity of thought. Large, opaque, energy-intensive LLMs represent the antithesis.
The whole article is an AI hallucination. It refers to the same "Christmas 2025 incident". The internet is dead for real.
phh•Mar 18, 2026
Unironically. Every time I asked a LLM to make something faster, they always tried blind code optimisations, rather than measure.
CharlieDigital•Mar 18, 2026
I feel like 1 and 2 are only applicable in cases of novelty.
The thing is, if you build enough of the same kinds of systems in the same kinds of domains, you can kinda tell where you should optimize ahead of time.
Most of us tend to build the same kinds of systems and usually spend a career or a good chunk of our careers in a given domain. I feel like you can't really be considered a staff/principal if you can't already tell ahead of time where the perf bottleneck will be just on experience and intuition.
PaulKeeble•Mar 18, 2026
I feel like every time I have expected an area to be the major bottleneck it has been. Sometimes some areas perform worse than I expected, usually something that hasn't been coded well, but generally its pretty easy to spot the computationally heavy or many remote call areas well before you program them.
I have several times done performance tests before starting a project to confirm it can be made fast enough to be viable, the entire approach can often shift depending on how quickly something can be done.
pydry•Mar 18, 2026
The number 1 issue Ive experienced with poor programmers is a belief that theyre special snowflakes who can anticipate the future.
It's the same thing with programmers who believe in BDUF or disbelieve YAGNI - they design architectures for anticipated futures which do not materialize instead of evolving the architecture retrospectively in line with the future which did materialize.
I think it's a natural human foible. Gambling, for instance, probably wouldnt exist if humans' gut instincts about their ability to predict future defaulted to realistic.
This is why no matter how many brilliant programmers scream YAGNI, dont do BDUF and dont prematurely optimize there will always be some comment saying the equivalent of "akshually sometimes you should...", remembering that one time when they metaphorically rolled a double six and anticipated the necessary architecture correctly when it wasnt even necessary to do so.
These programmers are all hopped up on a different kind of roulette these days...
rcxdude•Mar 18, 2026
Aye. The number one way to make software amenable to future requirements is to keep it simple so that it's easy to change in future. Adding complexity for anticipated changes works against being able to support the unanticipated ones.
tbrownaw•Mar 18, 2026
Sure, don't build your system to keep audit trails until after you have questions to answer so that you know what needs to go in those audit trails.
Don't insist on file-based data ingestion being a wrapper around a json-rpc api just because most similar things are moving that direction; what matters is whether someone has specifically asked for that for this particular system yet.
.
Not all decisions can be usefully revisited later. Sometimes you really do need to go "what if..." and make sure none of the possibilities will bite too hard. Leaving the pizza cave occasionally and making sure you (have contacts who) have some idea about the direction of the industry you're writing stuff for can help.
CharlieDigital•Mar 18, 2026
> Sure, don't build your system to keep audit trails until after you have questions to answer so that you know what needs to go in those audit trails...what matters is whether someone has specifically asked for that for this particular system yet.
I spent ~15 years in life sciences.
You're going to build an audit trail, no matter what. There's no validated system in LS that does not have an audit trail.
It's just like e-commerce; you're going to have a cart and a checkout page. There's no point in calling that a premature optimization. Every e-commerce website has more or less the same set of flows with simply different configuration/parameters/providers.
pydry•Mar 18, 2026
Going "what if?" and then validating a customer requirement that exists NOW is NOT the same thing as trying to pre-empt a customer's requirement which might exist in the future.
Audit trails are commonly neglected coz somebody didnt ask the right questions, not coz somebody didnt try to anticipate the future.
projektfu•Mar 18, 2026
It really depends on your requirements. C10k requires different design than a web server that sees a few requests per second at most, but the web might never have been invented if the focus was always on that level of optimization.
relaxing•Mar 18, 2026
Rob Pike wrote Unix and Golang, but sure, you’re built different.
andsoitis•Mar 18, 2026
> Rob Pike wrote Unix
Unix was created by Ken Thompson and Dennis Ritchie at Bell Labs (AT&T) in 1969. Thompson wrote the initial version, and Ritchie later contributed significantly, including developing the C programming language, which Unix was subsequently rewritten in.
9rx•Mar 18, 2026
Pike didn’t create Unix initially, but was a contributor to it. He, with a team, unquestionably wrote it.
andsoitis•Mar 18, 2026
> but was a contributor to it. He, with a team, unquestionably wrote it.
contribute < wrote.
His credits are huge, but I think saying he wrote Unix is misattribution.
Credits include: Plan 9 (successor to Unix), Unix Window System, UTF-8 (maybe his most universally impactful contribution), Unix Philosophy Articulation, strings/greps/other tools, regular expressions, C successor work that ultimately let him to Go.
9rx•Mar 18, 2026
Are you under the impression he was, like, a hands-off project manager or something? His involvement was in writing it. Not singlehandedly, but certainly as part of a team. He unquestionably wrote it. He did not envision it like he did the other projects you mention, but the original credit was only in the writing of.
amw-zero•Mar 18, 2026
To say "Rob Pike wrote Unix" is completely inaccurate. He joined after v7, in 1980.
9rx•Mar 18, 2026
Nobody seems to be questioning that he was involved in Unix. Given that he didn't write it, what did he do for the project? Quality assurance? Support? Marketing? Court jester?
Intermernet•Mar 18, 2026
Rob Pike is responsible for many cool things, but Unix isn't one of them. Go is a wonderful hybrid (with its own faults) of the schools of Thompson and Wirth, with a huge amount of Pike.
If you'd said Plan 9 and UTF-8 I'd agree with you.
jacquesm•Mar 18, 2026
Rob Pike definitely wrote large chunks of Unix while at Bell Labs. It's wrong to say he wrote all of it like the GP did but it is also wrong to diminish his contributions.
Unless you meant to imply that UNIX isn't cool.
relaxing•Mar 18, 2026
I did not say he wrote all of it. “Write” can include co-authorship.
A lot of people are learning some history today, beautiful to see.
jacquesm•Mar 18, 2026
I think that if you meant co-authorship you could have made that clearer. A 'contributed to' would have saved some unique ids.
my-next-account•Mar 18, 2026
Do you think Rob Pike ever decided that maybe what was done before isn't good enough? Stop putting artificial limits on your own competency.
HunterWare•Mar 18, 2026
ROFL, I wish Pike had known what he was talking about. /s ;)
CharlieDigital•Mar 18, 2026
Rob Pike and I (and probably most of us) work(ed) on different kind of things.
Notice my use of the word "Novelty".
I get hired because I'm very good at building specific kinds of systems so I tend to build many variants of the same kinds of systems. They are generally not that different and the ways in which the applications perform are similar.
I do not generally write new algorithms, operating systems, nor programming languages.
I don't think this is so hard to understand the nuance of Pike's advice and what we "mortals" do in or day-to-day to earn a living.
Bengalilol•Mar 18, 2026
> you can kinda tell where you should optimize ahead of time
Rules are "kinda" made to be broken. Be free.
I've been sticking to these rules (and will keep sticking to them) for as long as I can program (I've been doing it for the last 30 years).
IMHO, you can feel that a bottleneck is likely to occur, but you definitely can't tell where, when, or how it will actually happen.
Mercuriusdream•Mar 18, 2026
never expected it to be a single HTML file so kind of surprised, but straight to the point, to be honest.
andsoitis•Mar 18, 2026
KISS
epolanski•Mar 18, 2026
Well, even simpler, could've been plain text, every browser supports them.
anthk•Mar 18, 2026
9front it's distilled Unix. I corrected Russ Cox' 'xword' to work in 9front and I am just a newbie. No LLM's, that's Idiocratic, like the movie; just '9intro.us.pdf' and man pages.
LLM's work will never be reproducible by design.
piranha•Mar 18, 2026
> Rule 5 is often shortened to "write stupid code that uses smart objects".
This is probably the worst use of the word "shortened" ever, and it should be more like "mutilated"?
andsoitis•Mar 18, 2026
Syntactic sugar is cancer of the semicolon.
franktankbank•Mar 18, 2026
Tide goes in tide goes out, can't explain that.
paradox460•Mar 18, 2026
Tide goes in, clean laundry comes out
keyle•Mar 18, 2026
Rule 5 is definitely king. Code acts on data, if the data is crap, you're already lost.
edit: s/data/data structure/
andsoitis•Mar 18, 2026
… if the data structures are crap.
Good software can handle crap data.
keyle•Mar 18, 2026
That is not what I meant. I meant crap data structures. Sorry it's late here.
DaleBiagio•Mar 18, 2026
The attribution to Hoare is a common error — "Premature optimization is the root of all evil" first appeared in Knuth's 1974 paper "Structured Programming with go to Statements."
Knuth later attributed it to Hoare, but Hoare said he had no recollection of it and suggested it might have been Dijkstra.
Rule 5 aged the best. "Data dominates" is the lesson every senior engineer eventually learns the hard way.
zabzonk•Mar 18, 2026
I've always thought it was Dijkstra - it even sounds Dijkstra-ish.
YesThatTom2•Mar 18, 2026
If Dijkstra blamed Knuth it would have been the best recursive joke ever.
elcapitan•Mar 18, 2026
Meta: Love the simplicity of the page, no bullshit.
Funny handwritten html artifact though:
<title> <h1>Rob Pike's 5 Rules of Programming</h1> </title>
divbzero•Mar 18, 2026
Yes, I noticed this too. Timeless wisdom in 2128 bytes of handcrafted HTML.
doe88•Mar 18, 2026
Great rules, but Rule 3.: WOW, so true, so well enunciated, masterful.
bell-cot•Mar 18, 2026
Yes, and I'd say it's more true now than then. Best case, your fancy algorithms are super-sizing code that runs 1% of the time, always kicking more-often-run code out of the most critical CPU caches. Worst case, your fancy algorithms contain security bugs, and the bad guys cash in.
Devasta•Mar 18, 2026
> "Premature optimization is the root of all evil."
This Axiom has caused far and away more damage to software development than the premature optimization ever will.
gjadi•Mar 18, 2026
Because people only quote it partially.
> We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Rule 4, I have always practiced and demanded of junior programmers, to make algorithms and structures that are simple to understand, for our main user: the one who will modify this code in the future.
I believe that's why Golang is a very simple but powerful language.
anymouse123456•Mar 18, 2026
There are very few phrases in all of history that have done more damage to the project of software development than:
"Premature optimization is the root of all evil."
First, let's not besmirch the good name of Tony Hoare. The quote is from Donald Knuth, and the missing context is essential.
From his 1974 paper, "Structured Programming with go to Statements":
"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."
He was talking about using GOTO statements in C. He was talking about making software much harder to reason about in the name of micro-optimizations. He assumed (incorrectly) that we would respect the machines our software runs on.
Multiple generations of programmers have now been raised to believe that brutally inefficient, bloated, and slow software is just fine. There is no limit to the amount of boilerplate and indirection a computer can be forced to execute. There is no ceiling to the crystalline abstractions emerging from these geniuses. There is no amount of time too long for a JVM to spend starting.
I worked at Google many years ago. I have lived the absolute nightmares that evolve from the willful misunderstanding of this quote.
No thank you. Never again.
I have committed these sins more than any other, and I'm mad as hell about it.
devnullbrain•Mar 18, 2026
> and the missing context is essential.
Oh yes, I'd recommend everyone who uses the phrase reads the rest of the paper to see the kinds of optimisations that Knuth considers justified. For example, optimising memory accesses in quicksort.
anymouse123456•Mar 18, 2026
Exactly!
I wish Knuth would come out and publicly chastise the many decades of abuse this quote has enabled.
trey-jones•Mar 18, 2026
To be fair, I think human nature is probably a bigger culprit here than the quote. Yes, it was one of the first things told to me as a new programmer. No, I don't think it influenced very heavily how I approach my work. It's just another small (probably reasonable) voice in the back of my head.
kalaksi•Mar 18, 2026
This shows how hard it is to create a generalized and simple rule regarding programming. Context is everything and a lot is relative and subjective.
Tips like "don't try to write smart code" are often repeated but useless (not to mention that "smart" here means over-engineered or overly complex, not smart).
pydry•Mar 18, 2026
I dunno, Ive seen people try to violate "dont prematurely optimize" probably a thousand times (no exaggeration) and never ONCE seen this happen:
1. Somebody verifies with the users that speed is actually one of the most burning problems.
2. They profile the code and discover a bottleneck.
3. Somebody says "no, but we shouldnt fix that, that's premature optimization!"
Ive heard all sorts of people like OP moan that "this is why pieces of shit like slack are bloated and slow" (it isnt) when advocating skipping steps 1 and 2 though.
I dont think they misunderstand the rule, either, they just dont agree with it.
Did pike really have to specify explicitly that you have to identify that a problem is a problem before solving it?
devnullbrain•Mar 18, 2026
>1. Somebody verifies with the users that speed is actually one of the most burning problems.
Sometimes this is too late.
C++98 introduce `std::set` and `std::map`. The public interface means that they are effectively constrained to being red-black trees, with poor cache locality and suboptimal lookup. It took until C++11 for `std::unordered_map` and `std::unordered_set`, which brought with them the adage that you should probably use them unless you know you want ordering. Now since C++23 we finally have `std::flat_set` and `std::flat_map`, with contiguous memory layouts. 25 years to half-solve an optimisation problem and naive developers will still be using the wrong thing.
As soon as the interface made contact with the public, the opportunity to follow Rob Pike's Rule 5 was lost. If you create something where you're expected to uphold a certain behaviour, you need to consider if the performance of data structures could be a functional constraint.
At this point, the rule becomes cyclical and nonsensical: it's not premature if it's the right time to do it. It's not optimisation if it's functional.
pydry•Mar 18, 2026
You've inadvertently made an argument for deprecation, not ignoring rob's rule.
When building interfaces you are bound to make mistakes which end users will end up depending on (not just regarding optimization).
The correct lesson to learn from this is not "just dont make mistakes" but to try and minimize migration costs to prevent these mistakes from getting tightly locked in and try to detect these mistakes earlier on in the design process with more coordinated experimentation.
C++ seems pretty bad at both. It's not unusual, either - migration and upgrade paths are often the most neglected part of a product.
devnullbrain•Mar 18, 2026
How would you have minimised migration costs for std::map?
9rx•Mar 18, 2026
> the opportunity to follow Rob Pike's Rule 5 was lost.
std::set/std::map got into trouble because they chose the algorithm first and then made the data model match. Rule 5 suggests choosing the right data model first, indicating that it is most important.
lynndotpy•Mar 18, 2026
Yep. If one is implementing quicksort for a library where it will be used and relied on, I'd sure hope they're optimizing it as much as they can.
frereubu•Mar 18, 2026
Totally agree. Out of this context, the word "premature" can mean too many things.
aljgz•Mar 18, 2026
In all honesty, this is one of the less abused quotes, and I have seen more benefit from it than harm.
Like you, I've seen people produce a lot of slow code, but it's mostly been from people who would have a really hard time writing faster code that's less wrong.
I hate slow software, but I'd pick it anytime over bogus software. Also, generally, it's easier to fix performance problems than incorrect behavior, especially so when the error has created data that's stored somewhere we might not have access to. But even more so, when the harm has reached the real world.
anymouse123456•Mar 18, 2026
I don't believe there is any tension at all between fast and simple software.
We can and should have both.
This is a fraud, made up by midwits to justify their leaning towers of abstraction.
embedding-shape•Mar 18, 2026
User-facing, sure, nothing stopping us from doing "simple and fast" software. But when it comes to the code, design and architecture, "simple" is often at odds with "fast", and also "secure". Once you need something to be fast and secure, it often leads to a less simple design, because now you care about more things, it's kind of hard to avoid.
ndriscoll•Mar 18, 2026
IME doing application servers and firmware my whole career, simple and fast are usually the same thing, and "simple secure" is usually better security posture than "complex secure".
embedding-shape•Mar 18, 2026
Interesting, never done firmware, but plenty of backends and frontends. Besides the whole "do less and things get faster", I can't think of a single case where "simple" and "fast" is the same thing.
And I'd agree that "simple secure" is better than "complex secure" but you're kind of side-stepping what I said, what about "not secure at all", wouldn't that lead to simpler code? Usually does for me, especially if you have to pile it on top of something that is already not so secure, but even when taking it into account when designing from ground up.
ndriscoll•Mar 18, 2026
Not really. `return 0` is the simplest program you could write, but it's not terribly useful. There's an underlying assumption that there's some purpose/requirement for the program to exist. Through that lens "secure" is just assumed as a requirement, and the simplest way to meet your requirements will usually still give you the fastest program too.
"Do less and things get faster" is a very wide class of fixes. e.g. you could do tons of per-packet decision making millions of times per second for routing and security policies, or you could realize the answer changes slowly in time, and move that to upfront work, separating your control vs data processing, and generally making it easier to understand. Or you could build your logic into your addressing/subnets and turn it into a simple mask and small table lookup. So your entire logic gets boiled down to a table (incidentally why I can't understand why people say ipv6 is complex. Try using ipv4! Having more bits for addresses is awesome!).
MyHonestOpinon•Mar 18, 2026
> "simple" is often at odds with "fast"
Sort of. But if you keep the software simple, then it is easier to optimize the bottlenecks. You don't really need to make everything complicated to make it faster, just a few well selected places need to be refactored.
gspr•Mar 18, 2026
> I have seen more benefit from it than harm.
Same. I, too, am sick of bloated code. But I use the quote as a reminder to myself: "look, the fact that you could spend the rest of the workday making this function run in linear instead of quadratic time doesn't mean you should – you have so many other tasks to tackle that it's better that you leave the suboptimal-but-obviously-correct implementation of this one little piece as-is for now, and return to it later if you need to".
bko•Mar 18, 2026
> Multiple generations of programmers have now been raised to believe that brutally inefficient, bloated, and slow software is just fine. There is no limit to the amount of boilerplate and indirection a computer can be forced to execute. There is no ceiling to the crystalline abstractions emerging from these geniuses. There is no amount of time too long for a JVM to spend starting.
I think that's due to people doing premature optimization! If people took the quote to heart, they would be less inclined to increasing the amount of boilerplate and indirection.
ummonk•Mar 18, 2026
The boilerplate and indirection isn't done for performance
YesThatTom2•Mar 18, 2026
I hear you, friend!
While you were seeing those problems with Java at Google, I saw seeing it with Python.
So many levels of indirection. Holy cow! So many unneeded superclasses and mixins! You can’t reason about code if the indirection is deeper than the human mind can grasp.
There was also a belief that list comprehensions were magically better somehow and would expand to 10-line monstrosities of unreadable code when a nested for loop would have been more readable and just as fast but because list comprehensions were fetishized nobody would stop at their natural readability limits. The result was like reading the run-on sentence you just suffered through.
twoodfin•Mar 18, 2026
Ignoring optimization opportunities until you see the profile only works when you actually profile!
Profiling never achieved its place in most developers’ core loop the way that compiling, linting, or unit testing did.
How many real CI/CD pipelines spit out flame graphs alongside test results?
pydry•Mar 18, 2026
I usually defer this until a PM does the research to highlight that speed is a burning issue.
I find 98% of the time that users are clamoring to get something implemented or fixed which isnt speed related so I work on that instead.
When I do drill down what I tend to find in the flame graphs is that your scope for making performance improvements a user will actually notice is bottlenecked primarily by I/O not by code efficiency.
Meanwhile my less experienced coworkers will spot a nested loop that will never take more than a couple of milliseconds and demand it be "optimised".
lokar•Mar 18, 2026
Even at Google, the tendency is (or was when I was there), to only profile things that we know are consuming a lot of resources (or for sure will), or are hurting overall latency.
Also the rule (quote?) says "speed hack", I don't think he is saying ignore runtime complexity totally, just don't go crazy with really complex stuff until you are sure you need it.
Jensson•Mar 18, 2026
That depends on which part of Google. I worked in the hot path of the search queries and there speed was extremely important for everything, they want to do so much there every single query and latency isn't allowed to go up.
IshKebab•Mar 18, 2026
The problem with ignoring performance is that you'll always end up with slow software that is awful to use but ticks all the feature boxes. As soon as someone comes along that is fast and nice people will switch to that.
People don't ask for software to be fast and usable because it obviously should be. Why would they ask? They might complain when it's unusably slow. But that doesn't mean they don't want it to be fast.
sph•Mar 18, 2026
Another one from my personal experience: apply DRY principles (don't repeat yourself) the third time you need something. Or in other words: you're allowed to copy-and-paste the same piece of code in two different places.
Far too often we generalise a piece of logic that we need in one or two places, making things more complicated for ourselves whenever they inevitably start to differ. And chances are very slim we will actually need it more than twice.
Premature generalisation is the most common mistake that separates a junior developer from an experienced one.
busfahrer•Mar 18, 2026
Agreed, I think even Carmack advocates this rule
cjs_ac•Mar 18, 2026
DRY follows WET (Write Everything Twice).
colechristensen•Mar 18, 2026
Ehh, people who are really excited about DRY write unreadable convoluted code, where the bulk of the code is abstractions invented to avoid rewriting a small amount of code and unless you're very familiar with the codebase reasoning about what it actually does is a mystery because related pieces of functionality are very far away from each other.
trey-jones•Mar 18, 2026
DRY is not to avoid writing code (of any amount). DRY is a maintainability feature. "Unless you're very familiar with the code" you probably won't remember that you have to make this change in two places instead of one. DRY makes life easier for future you, and anyone else unfortunate to encounter (y)our mess.
bluGill•Mar 18, 2026
You are confusing DRY done as intended vs what DRY looks like in the real world to many people.
colechristensen•Mar 18, 2026
Making maintainable code is a good goal.
DRY is one step removed from that goal and people use it to make very unmaintainable code because they confuse any repeated code with unmaintainability. (or their theory that some day we might want to repeat this code so we might as well pre-DRY it)
The result is often a horrendous complex mess. Imagine a cookbook with a cookie recipe that resided on 47 different pages (40 of which were pointers on where to find other pointers on where to find other pointers on where to find a step) in attempts to never write the same step twice in the whole book or your planned sequels in a 20 volume set.
chuckadams•Mar 18, 2026
It's almost like there's a "reasonable person" type of standard that's impossible to nail down in a general rule...
colechristensen•Mar 18, 2026
If you can describe a rule in one sentence it'll probably lead to as much trouble as it fixes.
The problem is zealots. Zealotry doesn't work for indeterminate things that require judgement like "code quality" or "maintainability", but a simple rule like "don't repeat yourself" is easy for a zeal. They take a rule and shut down any argument with "because the rule!"
If you're arguing about code quality and maintainability without one sentence rules then you actually have to make arguments. If the rule is your argument there's no discussion only dogma.
As a result? Easy to distill rules spread fast, breed zealots, and result in bad code.
matthewkayin•Mar 18, 2026
I think we should not even generalize it down to a rule of three, because then you're outsourcing your critical thinking to a rule rather than doing the thinking yourself.
Instead, I tend to ask: if I change this code here, will I always also need to change it over there?
Copy-paste is good as long as I'm just repeating patterns. A for loop is a pattern. I use for loops in many places. That doesn't mean I need to somehow abstract out for loops because I'm repeating myself.
But if I have logic that says that button_b.x = button_a.x + button_a.w + padding, then I should make sure that I only write that information down once, so that it stays consistent throughout the program.
nostrademons•Mar 18, 2026
The reason for the rule of thumb is because you don't know whether you will need to change this code here when you change it there until you've written several instances of the pattern. Oftentimes different generalizations become appropriate for N=1, N=2, N>=3 && N <= 10, N>=10 && N<=100, and N>=100.
Your example is a pretty good one. In most practical applications, you do not want to be setting button x coordinates manually. You want to use a layout manager, like CSS Flexbox or Jetpack Compose's Row or Java Swing's FlowLayout, which takes in a padding and a direction for a collection of elements and automatically figures out where they should be placed. But if you only have one button, this is overkill. If you only have two buttons, this is overkill. If you have 3 buttons, you should start to realize this is the pattern and reach for the right abstraction. If you get to 10 buttons, you'll realize that you need to arrange them in 2D as well and handle how they grow & shrink as you resize the window, and there's a good chance you need a more powerful abstraction.
rkomorn•Mar 18, 2026
> Instead, I tend to ask: if I change this code here, will I always also need to change it over there?
IMO, this is the exact (and arguably only) question to ask.
9rx•Mar 18, 2026
Critical thinkers understand that rules aren't written for critical thinkers; that they are written for beginners who don't yet have the necessary experience to be able to think critically.
TeMPOraL•Mar 18, 2026
I really like Casey Muratori's "[Semantic] Compression-oriented programming" - which is the philosophical backing of "WET" (Write Everything Twice) counterpart to DRY.
I think this is a reasonable rule of thumb, but there are also times that the code you are about to write a second time is extremely portable and can easily be made reusable (say less than 5 minutes of extra time to make the abstraction). In these cases I think it's worth it to go ahead and do it.
Having identical logic in multiple places (even only 2) is a big contributor to technical debt, since if you're searching for something and you find it and fix it /once/ we often thing of the job as done. Then the "there is still a bug and I already fixed that" confusion is avoided by staying DRY.
andrewmutz•Mar 18, 2026
You say that, but I've created plenty of production bugs because two different implementations diverge. Easier to avoid such bugs if we just share the implementation.
bluGill•Mar 18, 2026
I've also seen a lot of production bugs because two things that appeared to be a copy/paste where actually conceptually different and making them common made the whole much more complex trying to get common code to handle things that diverged even though they started from the same place.
hackemmy•Mar 18, 2026
This is so true. I have been burned by this more times than I can count. You see two functions that look similar, you extract a shared utility, and then six months later one of them needs a slightly different behavior and now you are fighting your own abstraction instead of just changing one line in a copy. The rule of three is a good default. Let the pattern prove itself before you try to generalize it.
UncleMeat•Mar 18, 2026
More critical in my mind is investigating the "inevitably start to differ" option.
If two pieces of code use the same functionality by coincidence but could possibly evolve differently then don't refactor. Don't even refactor if this happens three, four, or five times. Because even if the code may be identical today the features are not actually identical.
But if you have two uses of code that actually semantically identical and will assuredly evolve together then go ahead and refactor to remove duplication.
The rule of 3 is awful because it focuses on the wrong thing. If two instances of the same logic represent the same concept, they should be shared. If 10 instances of the same logic represent unrelated concepts, they should be duplicated.
The goal is to have code that corresponds to a coherent conceptual model for whatever you are doing, and the resulting codebase should clearly reflect the design of the system. Once I started thinking about code in these terms, I realized that questions like "DRY vs YAGNI" were not meaningful.
taneq•Mar 18, 2026
> If two instances of the same logic represent the same concept, they should be shared. If 10 instances of the same logic represent unrelated concepts, they should be duplicated.
Exactly.
sph•Mar 18, 2026
The devil’s in the details, as usual. No rule should be followed to the letter, which is what the top comment was initially complaining about.
Yet again, understanding when to follow a rule of thumb or not is another thing that separates the junior from the senior.
miloignis•Mar 18, 2026
Of course, the rule of 3 is saying that you often _can't tell_ what the shared concept between different instances is until you have at least 3 examples.
It's not about copying identical code twice, it's about refactoring similar code into a shared function once you have enough examples to be able to see what the shared core is.
imajoredinecon•Mar 18, 2026
3 just seems arbitrary in practice though. In my job we share code when it makes sense and don’t when it doesn’t, and that serves us just fine
datsci_est_2015•Mar 18, 2026
But don’t let the rule of 3 be an excuse for you to not critically assess the abstract concepts that your program is operating upon and within.
I too often see junior engineers (and senior data scientists…) write code procedurally, with giant functions and many, many if statements, presumably because in their brain they’re thinking about “1st I do this if this, 2nd I do that if that, etc”.
eyelidlessness•Mar 18, 2026
I agree. And I think this also distills down to Rob Pike’s rule 5, or something quite like it. If your design prioritizes modeling the domain’s data, shaping algorithms around that model, it’s usually trivial to determine how likely some “duplication” is operating on shared concepts, versus merely following a similar pattern. It may even help you refine the data model itself when confronted with the question.
fauigerzigerk•Mar 18, 2026
Agreed. DRY is a compression algorithm. The rule of 3 is a bad compression algorithm. Good abstraction is not compression at all.
taneq•Mar 18, 2026
“Once, twice, automate/abstract” is a good general rule but you have to understand that the thing you’re counting isn’t appearances in the source code, it’s repetitions of the same logic in the same context. It’s gotta mean the same, not just look the same.
krilcebre•Mar 18, 2026
The instances should be based on the context. For example we had a few different API providers for the same thing, and someone refactored the separate classes into a single one that treats all of the APIs the same.
Well, turns out that 3 of the APIs changed the way they return the data, so instead of separating the logic, someone kept adding a bunch of if statements into a single function in order to avoid repeating the code in multiple places. It was a nightmare to maintain and I ended up completely refactoring it, and even tho some of the code was repeated, it was much easier to maintain and accommodate to the API changes.
Pxtl•Mar 18, 2026
Depends on length and complexity, imho. If it's more than a line or two of procedure? Or involves anything counterintuitive? DRY at 2.
Extract a method or object if it's something that feels conceptually a "thing" even if it has only one use. Most tools to DRY your code also help by providing a bit of encapsulation that do a great job of tidying things up to force you to think about "should I be letting this out of domain stuff leak in here?"
mock-possum•Mar 18, 2026
My rule of thumb is “when I have to make changes to this later, how annoying is it going to be to make the same change in multiple places?”
Sometimes four or five doesn’t seem too bad, sometimes two is too many
moogly•Mar 18, 2026
The D stands for "dependency", the R stands for "regret" and I'm not sure what the Y stands for yet.
zer00eyz•Mar 18, 2026
Yelling... it stands for yelling...
Mostly at the massive switch statements and 1000 line's of flow control logic that end up embedded someplace where they really dont belong in the worst cases.
mort96•Mar 18, 2026
IMO, the right way to think about DRY is to consider why a given piece of code would ever change.
If you have two copies of some piece of code, and you can reasonably say that if you ever want to update one copy then you will almost certainly want to update the other copy as well, then it's probably a good idea to try to merge them and keep that logic in some centralized place.
On the other hand, if you have three copies of the same piece of code, but they kind of just "happen to" be identical and it's completely plausible that any one of the copies will be modified in the future for reasons which won't affect the other copies, maybe keeping them separate is a good idea.
And of course, it's sometimes worth it to keep two or more different copies which do share the same "reason to change". This is especially clear when you have the copies in different repositories, where making the code "DRY" would mean introducing dependencies between repositories which has its own costs.
bsza•Mar 18, 2026
It’s not how many times, it’s what you do about it. DRY doesn’t mean you have to make abstractions for everything. It means you don’t repeat yourself. That is, if two pieces of code are identical, chances are one of them shouldn’t exist. There are a lot of simple ways you might be able to address that, starting from the most obvious one, which is to just literally delete one of them. Abstraction should be about the last tool you reach for, but for most people it’s unfortunately the first.
noufalibrahim•Mar 18, 2026
I had a situation where we need to implement a protocol. The spec was fairly decent but the public implementations of the other end were slightly non compliant which necessitated special casing. Plus multiple versions etc.
An expensive consultant suggested creating pristine implementation and then writing a rule layer that would modify things as needed and deploying the whole thing as a pile of lamdba functions.
I copy pasted the protocol consumer file per producer and made all the necessary changes with proper documentation and mocks. Got it working quickly and we could add new ones without affecting.
If I'd try to keep it DRY, i think it would be a leaky mess.
dwb•Mar 18, 2026
Totally agree. I’ve see that quote used to justify wilfully ignoring basic performance techniques. Then people are surprised when the app is creaking exactly due to the lack of care taken earlier. I would tend to argue the other way most of the time: a little performance consideration goes a long way!
Maybe I’ve had an unrepresentative career, but I’ve never worked anywhere where there’s much time to fiddle with performance optimisations, let alone those that make the code/system significantly harder to understand. I expect that’s true of most people working in mainstream tech companies of the last twenty years or so. And so that quote is basically never applicable.
pjc50•Mar 18, 2026
Slow code is more of a project management problem. Features are important and visible on the roadmap. Performance usually isn't until it hits "unacceptable", which may take a while to feed back. That's all it is.
(AI will probably make this worse as well, having a bloat tendency all of its own)
lowmagnet•Mar 18, 2026
I always point out the operational word is "premature".
dominotw•Mar 18, 2026
> I have lived the absolute nightmares that evolve from the willful misunderstanding of this quote.
how do you know which code was written using this quote in mind.
BoneShard•Mar 18, 2026
He doesn't know, but that quote makes for a cool talking point. Software is slow or bloated because of budget, deadlines, and skill levels - not because of a quote.
globular-toast•Mar 18, 2026
So you're saying people have misunderstood "premature optimisation is the root of all evil" as "optimisation is the root of all evil"?
I don't think you can blame this phrase if people are going to drop an entire word out of an eight word sentence. The very first word, no less.
bluGill•Mar 18, 2026
Don't confuse premature pessimization for the warnings against premature optimization.
I can write bubble sort, it is simple and I have confidence it will work. I wrote quicksort for class once - I turned in something that mostly worked but there were bugs I couldn't fix in time (but I could if I spent more time - I think...)
However writing bubble sort is wrong because any good language has a sort in the standard library (likely timsort or something else than quicksort in the real world)
taneq•Mar 18, 2026
> I have lived the absolute nightmares that evolve from the willful misunderstanding of this quote.
Then the quote wasn’t the problem. The wilful misunderstanding was the problem.
koliber•Mar 18, 2026
Picking the starting point is very important. "optimization" is the process of going from that starting point to a more performant point.
If you don't know enough to pick good starting points you probably won't know enough to optimize well. So don't optimize prematurely.
If you are experienced enough to pick good starting points, still don't optimize prematurely.
If you see a bad starting point picked by someone else, by all means, point it out if it will be problematic now or in the foreseeable future, because that's a bug.
JTbane•Mar 18, 2026
Anyone else feel like bloat is usually not an algorithmic problem, but rather a library or environment issue most of the time?
wavemode•Mar 18, 2026
I don't think the quote itself is responsible for any of that.
It's true that premature optimization (that is, optimization before you've measured the software and determined whether the optimization is going to make any real-world difference) is bad.
The reality, though, is that most programmers aren't grappling with whether their optimizations are premature, they're grappling with whether to optimize at all. At most companies, once the code works, it ships. There's little, if any, time given for an extra "optimization" pass.
It's only after customers start complaining about performance (or higher-ups start complaining about compute costs) that programmers are given any time to go through and optimize things. By which point refactoring the code is now much harder than it wouldn've been originally.
Megranium•Mar 18, 2026
Huh, I've always understood that quote very differently, with emphasis on "premature" ... not as in, "don't optimize" but more as in "don't optimize before you've understood the problem" ... or, as a CS professor of mine said "Make it work first, THEN make it work fast" ...
DarkCrusader2•Mar 18, 2026
well you see, in corporate (atleast in big tech), this is usually used as a justification to merge inefficient code (we will optimize it later). That later never comes, either the developers/management moves on or the work item never gets prioritized. That is until the bad software either causes outages or customer churn. Then it is fixed and shown as high impact in your next promo packet.
lokar•Mar 18, 2026
And if you know in advance that a function will be in the critical path, and it needs to perform some operation on N items, and N will be large, it’s not premature to consider the speed of that loop.
lokar•Mar 18, 2026
Another thought: many (most?) of these "rules" were before widespread distributed computing. I don't think Knuth had in mind a loop that is reading from a database at 100ms each time.
I've seen people write some really head shaking code that makes remote calls in a loop that don't actually depend on each other. I wonder to what extend they are thinking "don't bother with optimization / speed for now"
rob74•Mar 18, 2026
If you really have a loop that is reading from a database at 100ms each time, that's not because of not having optimized it prematurely, that's just stupid.
lokar•Mar 18, 2026
And yet... :)
I think there is just a current (I've seen it mostly in Jr engineers) that you should just ignore any aspect of performance until "later"
lokar•Mar 18, 2026
and, I guess, context does matter. If you need to make 10 calls to gather up some info to generate something, but you only need to do this once a day, or hour, and if the whole process takes a few seconds that's fine, I could see the argument that just doing the calls one at a time linearly is simpler write/read/maintain.
heliumtera•Mar 18, 2026
Got it. What about initiating a 800mb image on a CPU limited virtual machine that THEN hits a database, before responding to a user request on a 300ms roundtrip?
I think we need a new word to describe the average experience, stupidity doesn't fit.
patrickthebold•Mar 18, 2026
Reminds me of this quote which I recently found and like:
> look, I'm sorry, but the rule is simple:
if you made something 2x faster, you might have done something smart
if you made something 100x faster, you definitely just stopped doing something stupid
But second, I'd remove "optimization" from considering here. The code you're describing isn't slow, it's bad code that also happens to be slow. Don't write bad code, ever, if you can knowingly avoid it.
It's OK to write good, clear, slow code when correctness and understandability is more important that optimizing that particular bit. It's not OK to write boneheaded code.
(Exception: After you've written the working program, it turns out that you have all the information to make the query once in one part of the broader program, but don't have all the information to make it a second time until flow reaches another, decoupled part of the program. It may be the lesser evil to do that than rearrange the entire thing to pass all the necessary state around, although you're making a deal with the devil and pinky swearing never to add a 3rd call, then a 4th, then a 5th, then...)
redbar0n•Mar 18, 2026
Just remember Rob Pike's 1st rule: don't assume where bottlenecks will occur, but verify it.
ummonk•Mar 18, 2026
I've worked on optimizing modern slow code. Once you optimize a few bottlenecks it turns out it's very hard to optimize because the rest of the time is spread out over the whole code without any small bottlenecks and it's all written in a slow language with no thought for performance.
senfiaj•Mar 18, 2026
From my understanding you still need to care care about the algorithms and architecture. If N is sufficiently large, you should pick O(N) algorithm over O(N^2). But usually there is a tradeoff, simple code (or hiding something behind some abstraction) might be easier to understand and maintain, but it might work slower with large input data and vice versa. I would rather write code that will be easier to optimize if there is some bottleneck than to optimize it overagressivelly. Also, different code needs different kind of optimization. Sometimes the code might be more IO heavy (disk / DB or network), for this type of code, the IO operation planning and caching is more critical than the optimization of the raw CPU time. Sometimes the input is too small to have any significant performance effects, and, what's paradoxical, choosing smarter algorithms might even hurt performance (alongside the maintanability). For example, for 10 - 100 items a simple linear scan in an array might be faster than using a O(log n) binary search tree. It's also well known that faster executable code (regardless of being hand written, machine generated, high level or machine code) usually has larger size (mostly because it's more "unrolled", duplicated and more complex when advanced algorithms are used). If you optimize the speed everywhere, the binary size tends to increase, causing more cache misses, which might hurt the performance more than improve. This is why some profiling is often needed for large software than simply passing O3.
vbezhenar•Mar 18, 2026
We will optimize it later, we don't have time for that right now, it seems it works fast enough for our needs right now.
"Later" never comes and all critical performance issues are either ignored, hot-patched externally with caches of various quality or just with more expensive hardware.
Nemi•Mar 18, 2026
While what you say is often true, it is a different problem and does not change the fact of the prior posters.
zygentoma•Mar 18, 2026
My favourite quote for that is:
Broken gets fixed, but crappy stays forever
mort96•Mar 18, 2026
Plenty of people seem to understand it as, "don't even think about performance until someone has made a strong enough business case that the performance is sufficiently bad as to impact profits".
ekropotin•Mar 18, 2026
IDK if it can be applied in all situations.
Sometimes, especially when it comes to distributed systems, going from working solution to fast working solution requires full blown up redesign from scratch.
bdangubic•Mar 18, 2026
> Make it work first, THEN make it work fast
1. I have seen too many "make it work first" that ended up absolute shitshow that was notoriously difficult to do anything with. You can build the software right the first time
2. The "fast" part is what I think too many people are focusing on and in my experience the "THEN" part is always missing resources utilization and other types of inefficiency that are not necessarily related to speed. I have seen absolute messes of software that work really fast
rco8786•Mar 18, 2026
Ditto here
artyom•Mar 18, 2026
I agree with you. "Premature" is the keyword. Bloated software is the result of not having the intention to optimize it at all.
divan•Mar 18, 2026
> generations of programmers have now been raised to believe that brutally inefficient, bloated, and slow software is just fine.
I believe people don't think about Knuth when they choose to write app in Electron. Some other forces might be at play here.
imjonse•Mar 18, 2026
I was a bit worried you are paraphrasing Rob Pike, but no, he actually agrees with that Knuth quote.
I am almost certain that people building bloated software are not willfully misunderstanding this quote; it's likely they never heard about it. Let's not ignore the relevance of this half a century old advice just because many programmers do not care about efficiency or do not understand how computers work. Premature optimization is exactly that, the fact that is premature makes it wrong, regardless if it's about GOTO statements in the 70s or a some modern equivalent where in the name of craft or fun people make their apps a lot more complex than they should be. I wouldn't be surprised if some of the brutally inefficient code you mention was so because people optimized prematurely for web-scale and their app never ever needed those abstractions and extra components. The advice applies both to hackers doing micro-optimizations and architecture astronauts dreaming too big IMHO.
IshKebab•Mar 18, 2026
No I've definitely heard plenty of people use this as some kind of inarguable excuse to not care about performance. Especially if they're writing something in Python that should really be not super slow. "It's fine! Premature optimisation and all that. We'll optimise it later."
And then of course later is too late; you can't optimise most Python.
lynndotpy•Mar 18, 2026
I think the bigger problem is that "Premature optimization is the root of all evil" is a statement made by software engineers to feel more comfortable in their shortcomings.
That's not to bemoan the engineer with shortcomings. Even the most experienced and educated engineer might find themself outside their comfort zone, implementing code without the ability to anticipate the performance characteristics under the hood. A mental model of computation can only go so far.
Articulated more succinctly, one might say "Use the profiler, and use it often."
trgn•Mar 18, 2026
> Multiple generations of programmers have now been raised to believe that brutally inefficient, bloated, and slow software is just fine
100%
trgn•Mar 18, 2026
A lot of developers get enamored by fetishes. Just one example, because it's one i always struggle to vanquish in any of my teams.
Devs are obsessed with introducing functional-style constructs everywhere, just for the sake of it. FP is great for some classes of software, but baseline crufty for anything that requires responsiveness (front-ends basically), let alone anything at real interactive speeds (games, geo-software, ...)
The "premature optimization" quote is then always used as a way to ignore that entire code paths will be spamming the heap with hundreds of thousands of temporary junk, useless lexical scopes, and so forth. Writing it lean the first time is never considered, because of adherence to these fetishes (mutability is bad, oo is bad, loops lead to off-by-one errors, ...). It's absolutely exhausting to have these conversations, it's always starting from the ground up and these quotes like "premature optimization is the root of all evil" are only used as invocations to ward of criticism.
01100011•Mar 18, 2026
This is the sort of pontifical statement that old guys like me tend to make which is strictly wrong but also contains a lot of wisdom.
Yes, software is bloated, full of useless abstractions and bad design. You kids(well, anyone programming post 1980, so myself included) should be ashamed. Also let's not forget that those abstractions helped us solve problems and our friends in silicon valley(ok that no longer makes sense but imagine if SillyValley still just made HW) covered our mistakes. But yeah, we write crap a lot of the time.
But as other folks have said, it doesn't mean "don't optimize."
I've always used my own version of the phrase, which is: "Don't be stupid." As in, don't do dumb, expensive things unless you need to for a prototype. Don't start with a design that is far from optimal and slow. After profiling, fix the slow things. I'm pretty sure that's what most folks do on some level.
whatever1•Mar 18, 2026
At least before the LLM world you would have to trade money (aka more compute) for time to market. What is the point of spending a single second optimizing a query when your database has 10 users?
Of course today this has changed. You can have multiple agents working on micro optimizing everything and have the pie and eat it too.
pphysch•Mar 18, 2026
It has less to do with a quote and more to do with CS education (and the market) rewarding minimal functionality over performance, security, fault-tolerance, etc.
The average university CS student in USA (and India I presume) is taught to "hack it" at any cost, and we see the results.
dblohm7•Mar 18, 2026
I too learned this the hard way, via a supposedly concurrent priority queue that did quadratic-time work while holding a lock over the entire thing. I was told that "premature optimization is the root of all evil."
Sorry, folks, but that's just an excuse to make dumb choices. Premature _micro_optimization is the root of all evil.
EDIT: It was great training for when I started working on browser performance, though!
dblohm7•Mar 18, 2026
And if I may add a corollary: Measurement doesn't need to be held off until the end of the project! Start doing it as soon as you can!
jayd16•Mar 18, 2026
This discussion kind of irks me. I just read these posts as:
"The quote saying A is bad. Actually it said A all along!"
It's just complaining about others making a different value judgement for what is a worthwhile optimization. Hiding behind the 'true meaning of the quote' is pointless.
andoando•Mar 18, 2026
As someone currently writing 16-18 tables all with common definition, and crud, Id like some abstraction
didgetmaster•Mar 18, 2026
I agree. Faster hardware or horizontal scaling on distributed cloud environments can mask the problem; but it certainly doesn't solve the problem of bloated, inefficient software.
While it might not be necessary to spend hours fine-tuning every function; code optimization should be the mindset of every programmer no matter what they are coding.
How many fewer data centers would we need if all that software running in them was more efficient?
I wish we lived in a world where quotes could be that powerful.
But I'm afraid in reality this quote, like any other, is just used as a justification after the fact.
Actually, I do not believe devs are to blame, or that CS education is to blame; I believe that's an unfortunate law of society that complexity piles up faster than we can manage it. Of course the economic system rewards shiping today at the expense of tomorrow's maintenance, and also rewards splitting systems in seemingly independent subsystems that are simpler in isolation but results in a more complex machinery (cloud, microservices...)
I'm even wondering if it's not a more fundamental law than that, because adding complexity is always simpler than removing it, right? Kind of a second law of termodynamic for code.
Someone•Mar 18, 2026
> From his 1974 paper, "Structured Programming with go to Statements":
> He was talking about using GOTO statements in C.
I don’t think he was talking about C. That paper is from December 1974, and (early) C is from 1972, and “The UNIX Time-Sharing System” (https://dsf.berkeley.edu/cs262/unix.pdf) is from July 1974, so time wise, he could have known C, but AFAICT that paper doesn’t mention C, and the examples are PL/I or (what to me looks like) pseudocode, using ‘:=’ for assignment, ‘if…fi’ and ‘while…repeat’ for block, ‘go to’ and not C’s ‘goto’, etc.
zahlman•Mar 18, 2026
Yes, just like in Dijkstra's earlier (1968) "Go To Statement Considered Harmful". The syntax is not C and "go to" is two words, and of course that's definitely too early.
Shawn19s83•Mar 18, 2026
surprised this isn't talked about more
tasuki•Mar 18, 2026
The first four are kind of related. For me the fifth is the important – and oft overlooked – one:
> Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
ta20211004_1•Mar 18, 2026
Can't agree more on 5. I've repeatedly found that any really tricky programming problem is (eventually) solved by iterative refinement of the data structures (and the APIs they expose / are associated with). When you get it right the control flow of a program becomes straightforward to reason about.
To address our favorite topic: while I use LLMs to assist on coding tasks a lot, I think they're very weak at this. Claude is much more likely to suggest or expand complex control flow logic on small data types than it is to recognize and implement an opportunity to encapsulate ideas in composable chunks. And I don't buy the idea that this doesn't matter since most code will be produced and consumed by LLMs. The LLMs of today are much more effective on code bases that have already been thoughtfully designed. So are humans. Why would that change?
zer00eyz•Mar 18, 2026
> refinement of the data structures (and the APIs they expose / are associated with)
I think rule 5 is often ignored by a lot of distributed services. Where you have to make several calls, each with their own http, db and "security" overhead, when one would do. Then these each end up with caching layers because they are "slow" (in aggregate).
nostrademons•Mar 18, 2026
If you're doing it right, you start with a centralized service; get the product, software architecture, and data flows right while it's all in one process; and then distribute along architectural boundaries when you need to scale.
Very few software services built today are doing it right. Most assume they need to scale from day one, pick a technology stack to enable that, and then alter the product to reflect the limitations of the tech stack they picked. Then they wonder why they need to spend millions on sales and marketing to convince people to use the product they've built, and millions on AWS bills to scale it. But then, the core problem was really that their company did not need to exist in the first place and only does because investors insist on cargo-culting the latest hot thing.
This is why software sucks so much today.
skeeter2020•Mar 18, 2026
>> If you're doing it right, you start with a centralized service; get the product, software architecture, and data flows right while it's all in one process; and then distribute along architectural boundaries when you need to scale.
I'll add one more modification if you're like me (and apparently many others): go too far with your distribution and pull it back to a sane (i.e. small handful) number of distributed services, hopefully before you get too far down the implementation...
alain94040•Mar 18, 2026
Agreed, in my experience, rule 5 should be rule 1. I think I also heard it said (paraphrased) as "show we your code and I'll be forever confused, show me your database schema and everything will become obvious".
Having implemented my shared of highly complex high-performance algorithms in the past, the key was always to figure out how to massage the raw data into structures that allow the algorithm to fly. It requires both a decent knowledge of the various algorithm options you have, as well as being flexible to see that the data could be presented a different way to get to the same result orders of magnitude faster.
skeeter2020•Mar 18, 2026
I have seen a huge decline in data first over the past decade-plus; maybe related to a lot more pragmatic training where code-first and abstraction helped you go faster, earlier but I definitely came of age starting with the schema and there are an awful lot of problems & systems that essentially are UI and functions on top of the schema.
danielmarkbruce•Mar 18, 2026
UI + functions on top of schema if you've designed the schema well. Otherwise, it's a whole other thing.
beachy•Mar 18, 2026
I think you are referring to:
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
eikenberry•Mar 18, 2026
I don't think they were ever meant to be in order of importance.
dasil003•Mar 18, 2026
These rules aged well overall. The only change I would make these days is to invert the order.
Number 5 is timeless and relevant at all scales, especially as code iterations have gotten faster and faster, data is all the more relevant. Numbers 4 and 3 have shifted a bit since data sizes and performance have ballooned, algorithm overhead isn't quite as big a concern, but the simplicity argument is relevant as ever. Numbers 2 and 1 while still true (Amdahl's law is a mathematical truth after all), are also clearly a product of their time and the hard constraints programmers had to deal with at the time as well as the shallowness of the stack. Still good wisdom, though I think on the whole the majority of programmers are less concerned about performance than they should be, especially compared to 50 years ago.
treetalker•Mar 18, 2026
I'm not a skilled programmer (but would like to be someday). Would someone kindly resolve what appears to me to be a contradiction between the following?
1(a) Torvalds: "Bad programmers worry about the code. Good programmers worry about data structures and their relationships."
1(b) Pike Rule 5: "Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming."
— versus —
2. Perlis 2: "Functions delay binding; data structures induce binding. Moral: Structure data late in the programming process."
---
Ignorant as I am, I read these to advise that I ought to put data structures centrally, first, foremost — but not until the end of the programming process.
colechristensen•Mar 18, 2026
That is indeed contradictory and what those things say.
Good structure comes from exploring until you understand the problem well AND THEN letting data structure dominate.
foltik•Mar 18, 2026
Afaict Perlis is more saying not to expose data layout in the boundaries between abstractions, rather to keep them pure and functional.
So not really a contradiction, just Perlis talking about the functional shell and Torvalds/Pike talking about the imperative core.
zweifuss•Mar 18, 2026
When you explore a problem, use Python and lists/sets/dictionaries/JSON. Wait with types and specific data structures till you have understanding.
Speed of development over speed of execution.
When you know what and how to build commit to good data structures. Do the types, structs, classes, Trie, CRDTs, XML, Protobuf, Parquet and whatnot where apropriate. Instrument your program.
The efficiency of the final product counts.
dkarl•Mar 18, 2026
I think it's fine and generous that he credited these rules to the better-known aphorisms that inspired them, but I think his versions are better, they deserve to be presented by themselves, instead of alongside the mental clickbait of the classic aphorisms. They preserve important context that was lost when the better-known versions were ripped out of their original texts.
For example, I've often heard "premature optimization is the root of all evil" invoked to support opposite sides of the same argument. Pike's rules are much clearer and harder to interpret creatively.
Also, it's amusing that you don't hear this anymore:
> Rule 5 is often shortened to "write stupid code that uses smart objects".
In context, this clearly means that if you invest enough mental work in designing your data structures, it's easy to write simple code to solve your problem. But interpreted through an OO mindset, this could be seen as encouraging one of the classic noob mistakes of the heyday of OO: believing that your code could be as complex as you wanted, without cost, as long as you hid the complicated bits inside member methods on your objects. I'm guessing that "write stupid code that uses smart objects" was a snappy bit of wisdom in the pre-OO days and was discarded as dangerous when the context of OO created a new and harmful way of interpreting it.
zemo•Mar 18, 2026
> but I think his versions are better, they deserve to be presented by themselves, instead of alongside the mental clickbait of the classic aphorisms
keeping the historical chain of thinking alive is good, actually
Matthyze•Mar 18, 2026
"mental clickbait of the classic aphorisms" is one way to phrase 'attribution"
mcdonje•Mar 18, 2026
Very performance focused. Could more accurately be 5 rules of perf. Good list, though.
_philipalan•Mar 18, 2026
CS Unc remains un-chopped.
ajpaulson•Mar 18, 2026
I think I’m going to copy and paste this directly into my AGENTS.md file!
d_burfoot•Mar 18, 2026
I don't disagree with these principles, but if I wanted to compress all my programming wisdom into 5 rules, I wouldn't spend 3 out of the 5 slots on performance. Performance is just a component of correctness : if you have a good methodology to achieve correctness, you will get performance along the way.
My #1 programming principle would be phrased using a concept from John Boyd: make your OODA loops fast. In software this can often mean simple things like "make compile time fast" or "make sure you can detect errors quickly".
patwolf•Mar 18, 2026
These rules apply equally well to system architecture. I've been trying to talk our team out of premature optimization (redis cluster) and fancy algorithms (bloom filters) to compensate for poor data structures (database schema) before we know if performance is going to be a problem.
Even knowing with 100% certainty that performance will be subpar, requirements change often enough that it's often not worth the cost of adding architectural complexity too early.
bob1029•Mar 18, 2026
> Even knowing with 100% certainty that performance will be subpar
I think there is value in attempting to do something the "wrong way" on purpose to some extent. I have walked into many situations where I was beyond convinced that the performance of something would suck only to be corrected harshly by the realities of modern computer systems.
Framing things as "yes, I know the performance is definitely not ideal in this iteration" puts that monkey in a proper cage until the next time around. If you don't frame it this way up front, you might be constantly baited into chasing the performance monkey around. Its taunts can be really difficult to ignore.
sayYayToLife•Mar 18, 2026
I suppose this makes me feel a little bit better about a multi-month process that ended up requiring the eight Queens problem.
That said management did not quite understand. They thought that I should have known about the bottleneck (Actually I did but I was told not to prematurely optimize)
I end up writing the program three times, the final solution was honestly beautiful.
Management was not happy. The customer was happy.
ekjhgkejhgk•Mar 18, 2026
Uuuh doesn't look good that you claim something is important enough to be in your top 5, but then misattribute it.
thecodemonkey•Mar 18, 2026
Running the same codebase for 10+ years with a small team is what finally made me fully internalize these rules.
I've always been a KISS/DRY person but over a decade there are plenty of moments where you're tempted to reach for a fancier database or rewrite something in a trendier stack. What's actually kept things running well at scale is boring, known technologies and only optimizing in the places where it actually matters.
I think that Rob Pike was far more of a wordcel than a shape rotator for a famous computer scientist (which historically were very much on the shape rotator side).
pdpi•Mar 18, 2026
There's an important property that emerges from rules 3 and 4 — because the simple algorithm is easier to implement correctly, you can test the fancy algorithm for correctness by comparing its output to the simple one.
publicdebates•Mar 18, 2026
"Rule 6: Don't waste time on syntax highlighting unless you're incompetent."
OpenDQV•Mar 18, 2026
Golden rules for sure!
yandrypozo•Mar 18, 2026
does _a speed hack_ mean adding time.Sleep() for testing? or it's something else?
possiblydrunk•Mar 18, 2026
Optimization usually trades complexity for speed. Complexity hinders debugging and maintenance. Don't optimize unless you have to and not before you know where the bottleneck is. Straightforward common sense advice as long as hardware is not persistently constraining.
TimLeland•Mar 18, 2026
Take a look at the page title. I guess he didn't follow his own rules for this 100-line HTML file
<title> <h1>Rob Pike's 5 Rules of Programming</h1> </title>
zahlman•Mar 18, 2026
The page was created by some CS professor at UNC, not by Pike.
Pike's rules are from 1989, which is before it was common to publish HTML.
BTAQA•Mar 18, 2026
Rule 5 is the one that took me longest to internalize. Coming from
frontend development into building a full product with a real
database, I kept reaching for complex query logic when the real
fix was just restructuring the data. Once the schema was right the
queries became obvious. Brooks was right 50 years ago and it's
still true.
netbioserror•Mar 18, 2026
This resonates as true, as long as the fundamentals of your tools are there. Picking interpreted languages or browsers as targets shoots you in the foot and sets you magnitudes behind when performance starts to matter. But if you're using a native-compiled language with value- and move-semantics, immutable data, and a composable type system, it's shocking how easy it can be to write obvious, maintainable, fast programs that perform under pressure, even when you're not being clever.
Thankfully newer languages like Nim, Odin, and Swift lean hard into value semantics. They drastically reduce the cost of focusing on data structures and writing obvious algorithms. Then, when bottlenecks appear, you can choose to opt into fine-tuning.
fogzen•Mar 18, 2026
Rule 5 should be rule 1.
SomaticPirate•Mar 18, 2026
How quaint. I hope Claude/Codex reads this since from what I've heard here I'm not likely to need this rules anymore /s
I am curious if anyone has attempted to use codex/claude with something like this in the prompt
sneedenheimer•Mar 18, 2026
Given that Rob Pike seems to think having a shell script that uses tar is better than having a `-r` flag for cp [1], I wouldn't give much weight to his philosophy of programming :P.
Rule 3 gets me into trouble with CS majors a lot. I'm an EE by education and entered into SW via the bottom floor(embedded C/ASM) so it was late in my career before I knew the formal definition of big-O and complexity.
For most of my career, sticking to rule 3 made the most sense. When the CS major would be annoying and talk about big-O they usually forgot n was tiny. But then my job changed. I started working on different things. Suddenly my job started sounding more like a leetcode interview people complain about. Now n really is big and now it really does matter.
Keep in mind that Rob Pike comes from a different era when programming for 'big iron' looked a lot more like programming for an embedded microcontroller now.
SoftTalker•Mar 18, 2026
My father did some programming in Fortran and Assembly of various flavors. He was always partial to lookup tables where they could replace complicated conditionals or computations. Memory was precious in his day but it could still be worth it if your program did something repeatedly (which most do).
kevincox•Mar 18, 2026
I actually disagree with Rule 3! While numbers are usually small being fast on small cases generally isn't as important as performing acceptably on large cases. So I prefer to take the better big-O so that it doesn't slow down unacceptably on real-world edge-case stresses. (The type of workloads that the devs often don't experience but your big customers will.)
Of course there is a balance to this, the engineering time to implement both options is an important consideration. But given both algorithms are relatively easy to implement I will default to the one that is faster at large sizes even if it is slower at common sizes. I do suspect that there is an implicit assumption that "fancy" algorithms take longer and are harder to implement. But in many cases both algorithms are in the standard library and just need to be selected. If this post focused on "fancy" in terms of actual time to implement rather than speed for common sizes I would be more inclined to agree with it.
Rule 3 was true 1989, back then computers were so slow and had barely any ram so most things you did only was reasonable for small number of inputs. Today we almost always have large amounts of inputs so its different.
danielmarkbruce•Mar 18, 2026
This very much depends on where you work... and basically isn't true for most people. It's extremely true for some people.
cjwoodall•Mar 18, 2026
Rule 3 is still very much real. Fancy fast algorithms often have other trade-offs. The best algorithm for the job is the one that meets all requirements well... Big-O is one aspect, data is another, determinism of the underlying things that are needed (dynamic memory allocation, etc) can be another.
It is important to remember that the art of sw engineering (like all engineering) lives in a balance between all these different requirements; not just in OPTIMIZE BIG-O.
danielmarkbruce•Mar 18, 2026
Sure but the default (and usually correct) assumption when working at google (as an example) is basically "all numbers are big", so you you have to cluey about algorithms and data structures and not default to brute forcing something.
At 99% of shops it should be the other way around .
jltsiren•Mar 18, 2026
Even when you are working with large numbers, most numbers are usually small. Most of the code is probably not dealing with the large things, and a large thing may consist of a large number of instances that are individually small.
I've personally found it useful to always have concrete numbers in mind. An algorithm or data structure designed for N will probably be fine for N/10 and 10N, but it will often be inefficient for N/1000 or 1000N.
grogers•Mar 18, 2026
Well it is hedged with the word "fancy". I think a charitable reading is to understand the problem domain. If N is always small then trying to minimize the big-O is just showing off and likely counterproductive in many ways. If N is large, it might be a requirement.
Most people don't need FFT algorithm for multiplying large numbers, Karatsuba's algorithm is fine. But in some domains the difference does matter.
Personally I usually see the opposite effect - people first reach for a too-naive approach and implement some O(n^2) algorithm where it wouldn't have even been more complex to implement something O(n) or O(n log n). And n is almost always small so it works fine, until it blows up spectacularly.
bartread•Mar 18, 2026
> Personally I usually see the opposite effect - people first reach for a too-naive approach and implement some O(n^2) algorithm where it wouldn't have even been more complex to implement something O(n) or O(n log n). And n is almost always small so it works fine, until it blows up spectacularly.
Same. People solve in ways that are very obviously going to cause serious problems in only a few short weeks or months and it’s endlessly frustrating. If you’re building a prototype, fine, but if you’re building for production, very far from fine.
Most frustrating because often there’s next to no cost in selecting and implementing the correct architecture, domain model, data structure, or algorithm up front.
big-chungus4•Mar 18, 2026
Idk about rule 1, in my experience it's usually pretty clear which part of code is slow. Maybe it depends on projects, programming language, etc
wsesamemr81•Mar 18, 2026
this matches my experience exactly
justacatbot•Mar 18, 2026
Rule 2 is the one that keeps biting me. You can spend days micro-optimizing functions only to realize the real bottleneck was storing data in a map when you needed a sorted list. The structure of the data almost always determines the structure of the code.
ummonk•Mar 18, 2026
That's Rule 5 no?
antirez•Mar 18, 2026
In programming, the only rule to follow is that there are no rules: only taste and design efforts. There are too many different conditions and tradeoffs: sometimes what is going to be the bottleneck is actually very clear and one could decide to design with that already in mind, for instance.
GuB-42•Mar 18, 2026
The opposite conclusion can be taken from the premise of rule #1 "You can't tell where a program is going to spend its time"
If you can't tell in advance what is performance critical, then consider everything to be performance critical.
I would then go against rule #3 "Fancy algorithms are slow when n is small, and n is usually small". n is usually small, except when it isn't, and as per rule #1, you may not know that ahead of time. Assuming n is going to be small is how you get accidentally quadratic behavior, such as the infamous GTA bug. So, assume n is going to be big unless you are sure it won't be. Understand that your users may use your software in ways you don't expect.
Note that if you really want high performance, you should properly characterize your "n" so that you can use the appropriate technique, it is hard because you need to know all your use cases and their implications in advance. Assuming n will be big is the easy way!
About rule #4, fancy algorithms are often not harder to implement, most of the times, it means using the right library.
About rule #2 (measure), yes, you absolutely should, but it doesn't mean you shouldn't consider performance before you measure. It would be like saying that you shouldn't worry about introducing bugs before testing. You should do your best to make your code fast and correct before you start measuring and testing.
What I agree with is that you shouldn't introduce speed hacks unless you know what you are doing. Most of performance come from giving it consideration on every step. Avoiding a copy here, using a hash map instead of a linear search there, etc... If you have to resort to a hack, it may be because you didn't consider performance early enough. For example, if took care of making a function fast enough, you may not have to cache results later on.
As for #5, I agree completely. Data is the most important. It applies to performance too, especially on modern hardware. To give you an very simplified idea, RAM access is about 100x slower than running a CPU instruction, it means you can get massive speed improvement by making your memory footprint smaller and using cache-friendly data structures.
epolanski•Mar 18, 2026
> If you can't tell in advance what is performance critical, then consider everything to be performance critical.
As for rule 2: first you measure.
fl0ki•Mar 18, 2026
> Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants.
I get where he's coming from, but I've seen people get this very wrong in practice. They use an algorithm that's indeed faster for small n, which doesn't matter because anything was going to be fast enough for small n, meanwhile their algorithm is so slow for large n that it ends up becoming a production crisis just a year later. They prematurely optimized after all, but for an n that did not need optimization, while prematurely pessimizing for an n that ultimately did need optimization.
R0m41nJosh•Mar 18, 2026
When I was young I took time to "optimize" my code where it obviously had no impact, like on simple python scripts. It was just by ego, to look smart. I guess the "early optimization" takes are aimed at young developers who want to show their skills on completely irrelecant places.
Of course with experience, you start to feel when the straight forward suboptimal code will cause massive performance issued. In this case it's critical to take action up front to avoid the mess. Its called software architecture, I guess.
shashank-100•Mar 18, 2026
rules are there until you break them
vishnugupta•Mar 18, 2026
I can’t emphasize the importance of rule-5 enough.
I learnt about rule-5 through experience before I had heard it was a rule.
I used to do tech due diligence for acquisition of companies. I had a very short time, about a day. I hit upon a great time saver idea of asking them to show their DB schema and explain it. It turned out to be surprisingly effective. Once I understood the scheme most of the architecture explained itself.
Now I apply the same principle while designing a system.
SoftTalker•Mar 18, 2026
Yes, fully agree. Rule 5 has been the center of my approach to designing and writing software for over 30 years now. Fad methodologies and platforms come and go but Rule 5 works as well for me today as it did in 1995.
artyom•Mar 18, 2026
I can't agree more. I live and breath by rule #5.
Getting competent at it, however, is no joke and takes time.
nlawalker•Mar 18, 2026
This reminds me of a portion of a talk Jonathan Blow gave[1], where he justifies this from a productivity angle. He explains how his initial implementation for virtually everything in Braid used arrays of records, and only after finding bottlenecks did he make changes, because if he had approached every technical challenge by trying to find the optimal data structure and algorithm he would never have shipped.
"There's a third thing [beyond speed and memory] that you might want to optimize for which is much more important than either of these, which is years of your life required per program implementation." This is of course from the perspective of a solo indie game developer, but it's a good and interesting perspective to consider.
It's easy to see this outside of the perspective of a solo game developer. We all have deadlines to manage even in the regular 'corporate world'. And engineering time spent on a problem also translates to an actual cost.
It's a good consideration tbh.
dgb23•Mar 18, 2026
It's also notable that video games are programs that run for hours and iterate over large sets of very similar entities at 60 frames or more per second repeatedly and often do very similar operations on each of the entities.
That also means that "just do an array of flat records" is a very sane default even if it seems brutish at first.
quietbritishjim•Mar 18, 2026
I think when he said "just do an array of flat records" he meant as opposed to record of arrays (i.e. row oriented vs column oriented), as opposed to fancy data structures which I think you're assuming he was implying. Separate arrays for each data member are common in game engines exactly because they're good for iterating over, which as you said is common.
O3marchnative•Mar 18, 2026
That's also great for letting the compiler unlock auto-vectorization opportunities without delving into the world of manual SIMD.
Even storing something simple such as an array of complex numbers as a structure of arrays (SoA) rather than an array of structures (AoS) can unlock a lot of optimizations. For example, less permutes/shuffles and more arithmetic instructions.
Depending on how many fields you actually need when you iterate over the data, you prevent cache pollution as well.
mabster•Mar 18, 2026
I haven't watched his videos on his language for ages, but this was a big thing he wanted in his language: being able to swap between array-of-structs and struct-of-array quickly and easily.
MaxBarraclough•Mar 18, 2026
Jonathan Blow's own unreleased Jai programming language has a feature to make it trivial to switch between array-of-structs and struct-of-arrays.
I'd be careful extending learnings from games (solo or team efforts) to general programming as the needs and intent seem to be so different. We rarely see much code re-use in games outside of core, special-purpose buy largely isolated components and assets. Outside of games there's a much bigger emphasis on the data IME, and performance is often a nice-to-have.
awesome_dude•Mar 18, 2026
Yeah - A game is (generally) a one and done enterprise. Like a start up it's all about getting it out the door, there's little to no expectation of having to maintain it for any real length of time.
fidotron•Mar 18, 2026
HN has some hot takes but that one is particularly impressive.
Fortnite, Roblox, League of Legends, Counter Strike . . . all very short term projects.
wat10000•Mar 18, 2026
For every game like that, there are a thousand games that shipped, maybe got a few updates, and then they were done.
awesome_dude•Mar 18, 2026
We can probably add to that the fact that the nominated titles almost certainly started out with spaghetti code that had to be refactored, reworked, and gave maintainers nightmares in their sleep
jjmarr•Mar 18, 2026
Roblox didn't have server functions until 2014 and they weren't mandatory until 2018. Anything the client did automatically replicated to every other client.
That meant I could attach Cheat Engine to Roblox, edit memory addresses to give myself in-game cash, and inject whatever code I wanted to everyone else.
Thankfully it was sandboxed so I couldn't give people viruses.
This was probably a good decision because most of Roblox's best games from that period weren't updated to use server functions. It's too difficult.
mabster•Mar 18, 2026
It's been mixed moving to normal code: I haven't had to low-level optimise for ages now (man I miss that). But performance in the O() sense has been the same.
Game engine development is very much about processing of data. The pipeline is long and the tree is wide. Being able to reason about complicated data processing topologies mapped very easily across.
suzzer99•Mar 18, 2026
> how his initial implementation for virtually everything in Braid used arrays of records
This is me with hash maps.
Fraterkes•Mar 18, 2026
This is slightly surprising to me, since Braid is kinda infamous for being in development for a pretty long time for a puzzle platformer
SatvikBeri•Mar 18, 2026
Was 3 years a long time for an indie platformer in the early 2000s? Looking at some similar examples:
* Braid was 3 years
* Cave Story was 5 years
* World of Goo was 2 years
* Limbo was about 3 years (but with 8-16 people)
So Braid seems pretty average.
Fraterkes•Mar 18, 2026
Yeah, you’re right. I must’ve misremembered.
IshKebab•Mar 18, 2026
I feel like a game is a bit different though because you control the input. You can't profile input you've never seen.
gowld•Mar 18, 2026
Jonathan blow has spent the past 10 years and almost all of his money trying to build the perfect programming language for his 3rd game.
Also, Rule 5 contradicts the idea you quoted from Blow.
parasense•Mar 18, 2026
The funny thing is even data structure is really an array of memory depending how low down deep one looks. All the people who took an intro computer science course knows, those who had to implement a heap allocator, it's all really just arrays underneath...
jasonwatkinspdx•Mar 18, 2026
That's very much the norm in the game dev world, especially for someone like Jonathan who's been doing this since the early era of PC gaming.
You've got 16 ms to get that next frame rendered, so stuffing everything into fixed sized tables and linear walks is a simple way to structure against that limit. You've got an entity budget of so many, and there's no unpredictable delays from dynamic allocation or similar.
Even when using scripting languages that offer more flexibility, like LUA or C#, you see a tendency towards this pattern.
cestith•Mar 18, 2026
Any software developer who hasn’t read _The Practice of Programming_ by Kernighan and Pike should. It’s not that long and much of it is timeless.
Insanity•Mar 18, 2026
Yeah, but I doubt many of the newer generation are going to read this. I manage a team of engineers, and one of the recent-ish graduates asked me in our 1-on-1 if it's still worth learning Python given that he can just write prompts. (Python is the language all our tools use).
If the next generation doesn't even want to learn a programming language, they're definitely not going to learn how to write _clean_ code.
Maybe I'm just overly pessimistic about junior engineers at the moment because of that conversation lol.
calepayson•Mar 18, 2026
Junior here. There are still a few of us who value books and documentation. It's a weird time though. Hard to feel confident that you're learning in the correct way.
Anyway, I've found that if you want to get a coworker into reading technical books, the best way is with a novel or three. I've had good success with The Martian. The Phoenix Project might work too. Slip them fun books until they've built a habit and then drop The Mythical Man Month on them. :)
zahlman•Mar 18, 2026
> Hard to feel confident that you're learning in the correct way.
This was hard before, too.
LVB•Mar 18, 2026
Here's my optimistic take: the fundamental things that spark joy about learning a novel algorithm, pattern, technique, etc. haven't gone anywhere, and there's no reason to think those things won't continue to be interesting. Furthermore, it seems like reading code isn't going anywhere too soon, and that definitely benefits from clean code. It follows that someone who can actually recognize clean from spaghetti, and tell the LLM to refactor it into XYZ style, is going to be relatively more valuable.
Random side note: my teen son has grown up with iPhone-level tech, yet likes and finds my old Casio F91 watch very interesting. I still have faith :)
wduquette•Mar 18, 2026
In almost forty years of experience, the fraction of developers I've known who read in the field beyond what's strictly needed for their task is very small. I'm always delighted when I find one.
fwip•Mar 18, 2026
I obviously wasn't there, but it sounds like maybe they were asking for reassurance. There's a lot of people out there saying that LLMs are going to totally replace regular programming, and for a new grad who doesn't know much about the world, they value your expertise.
Insanity•Mar 18, 2026
That's a positive interpretation. You might be right, either way that's what I pointed them to. I don't think the LLMs will really replace engineers in the foreseeable future, and so learning the languages and the fundamentals is still needed.
cestith•Mar 18, 2026
I have a laptop and a phone right here, right now. I have actual calculators around here somewhere. I’ve been out of schools for decades. I still can do arithmetic and basic algebra in my head or on paper and often do.
I’m hoping the situation with LLMs will be the same. Teach the basics and allow people to fall back on them for at least the simpler tasks for their lifetimes. I know people, by the way, who can still use an abacus and a slide rule. I can too, but with a refresher beforehand because I seldom use those.
kshacker•Mar 18, 2026
IMO it is a valid question. Our AI has not yet reached that level, our prompts have not yet reached that level of sophistication. But I do not code in assembly any more, I do not do pointer arithmetic any more, so maybe some day we get to a state where we do not write python also. It is not going to be soon despite the AI bandwagon saying so, there are too many legacy pieces that are not documented well and not easy decipherable due to context window limits. But in 10 years ...maybe prompts is all we need.
PS: Not that we do not have people working at all levels of stack today, just that each level of stack, like a discussion going on today about python's JIT compiler will be a few (dozen or hundred) specialists. Everyone else can work with prompts.
ryguz•Mar 18, 2026
The interesting thing about Rule 1 is that it makes Rules 3-5 follow almost mechanically. If you genuinely accept that you cannot predict where the bottleneck is, then writing straightforward code and measuring becomes the only rational strategy. The problem is most people treat these rules as independent guidelines rather than as consequences of a single premise.
In practice what I see fail most often is not premature optimization but premature abstraction. People build elaborate indirection layers for flexibility they never need, and those layers impose real costs on every future reader of the code. The irony is that abstraction is supposed to manage complexity but prematurely applied it just creates a different kind.
eru•Mar 18, 2026
> In practice what I see fail most often is not premature optimization but premature abstraction.
Philip Wadler gave us a sound type system. Rob Pike gave us a programming language for stupid people. Each contributed in their own way, towards different goals.
rob•Mar 18, 2026
Really need that [flag bot] button added to HN.
tech_hutch•Mar 18, 2026
Are you saying the parent comment seems like a bot?
macintux•Mar 18, 2026
Comment history is suspect.
rd•Mar 18, 2026
It would be easier if we could just block comments from green users. I get that it loses ~.1% of authors who might have made an account to comment on a blogpost of theirs that was posted here. I'd rather have that loss than have to deal with the 99.9% of spam.
suzzer99•Mar 18, 2026
TIL green means new. I thought it was special for some reason.
tracker1•Mar 18, 2026
You tend to see it a lot in "Enterprise" software (.Net and Java shops in particular). A lot of Enterprise Software Architects will reach for their favored abstractions out of practice as opposed to if they fit. Custom solution providers will build a bunch of the same out of practice.
This is something I tend to consider far, far worse than "AI Slop" in practice. I always hated Microsoft Enterprise Library's Data Access Application Block (DAAB) in practice. I've literally only ever seen one product that supported multiple database backends that necessitated that level of abstraction... but I've seen that library well over a dozen times in practice. Just as a specific example.
IMO, abstractions should generally serve to make the rest of the codebase reasonable more often than not... abstractions that hide complexity are useful... abstractions that add complexity much less so.
silisili•Mar 18, 2026
> In practice what I see fail most often is not premature optimization but premature abstraction
This matches my experience as well.
Someone here commented once that abstractions should be emergent, not speculative, and I loved that line so much I use it with my team all the time now when I see the craziness starting.
hugey010•Mar 18, 2026
I completely agree with you, and that is an amazing quote.
senderista•Mar 18, 2026
“Abstractions should be written in blood”
fl0ki•Mar 18, 2026
I only agree if you have a bounded dataset size that you know will never grow. If it can grow in future (and if you're not sure, you should assume it can), not only will many data structures and algorithms scale poorly along the way, but they will grow to dominate the bottleneck as well. By the time it no longer meets requirements and you get a trouble ticket, you're now under time pressure to develop, qualify, and deploy a new solution. You're much more likely to encounter regressions when doing this under time pressure.
If you've been monitoring properly, you buy yourself time before it becomes a problem as such, but in my experience most developers who don't anticipate load scaling also don't monitor properly.
I've seen a "senior software engineer with 20 years of industry experience" put code into production that ended up needing 30 minute timeouts for a HTTP response only 2 years after initial deployment. That is not a typo, 30 minutes. I had to take over and rewrite their "simple" code to stop the VP-level escalations our org received because of this engineering philosophy.
9rx•Mar 18, 2026
> You're much more likely to encounter regressions when doing this under time pressure.
There is nothing to suggest you should wait to optimize under pressure, only that you should optimize only after you have measured. Benchmark tests are still best written during the development cycle, not while running hot in production.
Starting with the naive solution helps quickly ensure that your API is sensible and that your testing/benchmarking is in good shape before you start poking at the hard bits where you are much more likely to screw things up, all while offering a baseline score to prove that your optimizations are actually necessary and an improvement.
munk-a•Mar 18, 2026
As someone who believes strongly in type based programming and the importance of good data structure choice I'm not seeing how Rule 5 follows Rule 1. I think it's important to reinforce how impactful good data structure choice is compared to trying to solve everything through procedural logic since a well structured coordination of data interactions can end up greatly simplifying the amount of standalone logic.
astrobe_•Mar 18, 2026
Data cache issues is one case of something being surprising slow because of how data is organized. That said, Structure of Arrays vs Array of structures is an example where rule 4 and 5 somewhat contradict each other, if one confuses "simple" and "easy" - Structure of Array style is "harder" because we don't see it often; but then if it's harder, it is is likely more bug-prone.
akkad33•Mar 18, 2026
But good data structure is not always evident from the get go. And if your types are too specific it would make future development hard if the specs change. This is what I struggle with
munk-a•Mar 18, 2026
Professionally I'm a data architect. Modeling data in a way that is functional, performant and forward facing is not an easy problem so it's perfectly fine to struggle with it. We do our best job with what we've got within reasonable constraints - we can't do anything more than that.
I found that over time my senses have been honed to more quickly identify things that are important to deeply study and plan right now and areas where I can skimp more and fix it later if problems develop. I don't know if there was a short cut to honing those senses that didn't involve a lot of pain as I needed to pick apart and rework oversights.
mkehrt•Mar 18, 2026
This comment is fascinating to me, as it indicates an entirely different mindset than mine. I'm much more interested in code readability and maintainabilty (and simplicty and elegance) than performance, unless it's necessary. So I would start by saying everything flows from rule 4 or maybe 5. Rule 1 is a consequence of rule 4 for me.
Eiriksmal•Mar 18, 2026
Maybe it's because the comment you are replying to is from a new account posting paragraphs of LLMese in multiple comments in the same minute. It's unsurprising that soulless LLM output doesn't match your mindset!
I was just dealing with this is some home-grown configuration nightmare where every line of the file was pulled from some other namespace to the point where you end up with two dozen folders, each with half a dozen partial configuration files in them, and it takes half a hour to figure out where a single value is coming from.
I'm sure it's super flexible but the exactly same thing could have been achieved with 8 YAML files and 60% of the content between them would be identical.
hugey010•Mar 18, 2026
Premature abstraction is one of the worst pitfalls when writing software. It's paid for up front, it costs developer time to understand and work with, it adds complexity (tech debt), increases likeliness of bugs, and increases refactor time. All for someone to say "if we ever want to do X". If we wanted to do it, we'd do it now.
I truly believe this comes from devs who want to feel smart by "architecting" solutions to future problems before those problems have become well defined.
akoboldfrying•Mar 19, 2026
I think it mostly happens because a little bit of abstraction is nearly always uncontroversially good. If you want to print a line of text 5 times, you'll instinctively write a for loop to do it, instead of copy-pasting the original print() statement an extra 4 times. The cognitive overhead upon reading this code is near zero, since we're all so familiar with it. This is abstraction, nonetheless.
So a little bit is always good, and more is sometimes very good -- even memorably good. Together these cause many of us to extrapolate too far, but for understandable reasons.
tracker1•Mar 18, 2026
Pretty much live by these in practice... I've had a lot of arguments over #3 though... yes nested loops can cause problems... but when you're dealing with < 100 or so items in each nested loop and outer loop, it's not a big deal in practice. It's simpler and easier to reason with... don't optimize unless you really need to for practical reasons.
On #5, I think most people tend to just lean on RDBMS databases for a lot of data access patterns. I think it helps to have some fundamental understandings in terms of where/how/why you can optimize databases as well as where it make sense to consider non-relational (no-sql) databases too. A poorly structured database can crawl under a relatively small number of users.
jkaptur•Mar 18, 2026
It's interesting to contrast "Measure. Don't tune for speed until you've measured" with Jeff Dean's "Latency Numbers Every Programmer Should Know" [0].
Dean is saying (implicitly) that you can estimate performance, and therefore you can design for speed a priori - without measuring, and, indeed, before there is anything to measure.
I suspect that both authors would agree that there's a happy medium: you absolutely can and should use your knowledge to design for speed, but given an implementation of a reasonable design, you need measurement to "tune" or improve incrementally.
I mean...you should always design with speed in mind (In that Jeff Dean sense :) but what 'premature optimization' is referring to, is more like localized speed optimizations/hacks. Don't do those until a) you know you'll need it and b) you know where it will help.
sifar•Mar 18, 2026
Yeah, the latency numbers provide a ceiling for your algorithm. The actual performance depends on the implementation, code generation, runtime hazards, small dependencies one may have overlooked etc.
SatvikBeri•Mar 18, 2026
I've had the pleasure of working with some truly fast pieces of code written by experts. It's always both. You have to have a good sense of what's generally fast and what's not in order to design a system that doesn't contain intractable bottlenecks. And once you have a good design you can profile and optimize the remaining constraints.
But e.g. if you want to do fast math, you really need to design your pipeline around cache efficiency from the beginning – it's very hard to retrofit. Whereas reducing memory allocations in order to make parallel algorithms faster is something you can usually do after profiling.
PaulHoule•Mar 18, 2026
The "bottleneck" model of performance has limitations.
There are a lot of systems where useless work and other inefficiencies are spread all over the place. Even though I think garbage collection is underrated (e.g. Rustifarians will agree with me in 15 years) it's a good example because of the nonlocality that profilers miss or misunderstand.
You can make great prop bets around "I'll rewrite your Array-of-Structures code to Structure-of-Arrays code and it will get much faster"
because SoA usually is much more cache friendly and AoS makes the memory hierarchy perform poorly in a way profilers can't see. The more time somebody spends looking at profilers and more they quote Rule 1 the more they get blindsided by it.
okamiueru•Mar 18, 2026
Rule 1 seems similar to Donald Knuths "premature optimization" from 1974.
> Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Rule 2 follows rule 1.
Rule 3 & 4 is a variation of Keep it Simple, Stupid (KISS) from the 1960s.
... and... now I feel stupid, because I read the last part, which is summarizing it in the same way.
dreko•Mar 18, 2026
Later never comes" is true, but the fix isn't to optimize early, it's to write code simple enough that optimization is easy when later finally does come. That's what Rule 5 is really about. Get the data structures right and the rest is tractable.
munro•Mar 18, 2026
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
It's so true, when specing things I always try to focused on DDL because even the UI will fall into place as well, and a place I see claude opus fail as well when building things.
cleaver•Mar 18, 2026
I recall a similar statement from Ed Yourdon in one of his books (90's?)
EvanAnderson•Mar 18, 2026
The article makes reference to Fred Brooks and "The Mythical Man Month", but doesn't make a direct quote. The quote I'd have referenced is:
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
oxag3n•Mar 18, 2026
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
I'm a big fan of Data Oriented Design. Once you conceptualize how data is stored and transformed in your program, it just has to be reflected in data structures that make it possible.
Modern design approaches tend to focus on choosing a right abstraction like columnar/row layout, caching etc. They mostly fail to optimally work with the data. Optimal in this case meaning getting most of all underlying hardware capabilities, for example reading large and preferably continuous blocks of data from magnetic storage, parallel data processing, keeping intermediate results in CPU caches, utilizing all physical SSD queues.
aaronblohowiak•Mar 18, 2026
The fundamental tension between nouns and verbs and the attempts to unify them like events have made programming a long art form to study.
It's all use-case and priority-specific, and I think the more varied your experience and more tools you have in the tool belt, the better off you can be to bring the right solution to bear. Of course, then you think you have the right solution in mind (lets say using partitions in postgres for something) but you find the ORM your service is using doesn't support it, then what is "best" becomes not only problem-specific but also tool-specific. Finally, even if you have the best solution and your existing ecosystem supports it but the rest of the engineering staff you have is unfamiliar with it, it may again no longer be "best".
this ladder of problem-fit, ecosystem-fit, staffing-fit is something I have grappled with in my career.
LLMs are only so-so at any of the above (even when including the agent as "staff".)
ryguz•Mar 18, 2026
Rule 5 about data dominating resonates most in modern systems. The trend of just throwing more code at it when most performance and correctness issues come down to how data flows through the system. Most junior engineers optimize the wrong layer because they start with the code instead of the data model.
I received some 'interesting' feedback when I spoke about commenting that I was using an O(n) search instead of an O(log n) algorithm because of the normally small size of n [0]
What I should have done was point to Rob's third rule (either in my comment or in the resulting threads)
Once upon a time in the 90's I was at work at 2am and I needed to implement a search over a data set. This function was going to be eventually called for every item, thus if I implemented it as a linear search, it would be n^2 behavior. Since it was so late and I was so tired, I marked it as something to fix later, and just did linear search.
Later that week, now that things were working, I profiled the n^2 search. The software controlled a piece of industrial test equipment, and the actual test process would take something around 4 hours to complete. Using the very worst case, far-beyond-reasonable data set, if I left the n^2 behavior in, would have added something like 6 seconds to that 4 hour runtime.
(Ultimately I fixed it anyways, but because it was easy, not because it mattered.)
senderista•Mar 18, 2026
For small enough n, linear search might have been faster.
bsder•Mar 18, 2026
The problem with O(n^2) algorithms is that they are fast enough to get into production and slow enough to explode in production.
jancsika•Mar 18, 2026
Can you actually follow rule #1 for things like realtime audio programming?
eichin•Mar 18, 2026
Heh, in the early days of C++ (1990ish) I had a notable application of 3+4 involving a doubly linked list with cache pointers (time-sequence data browser so references were likely "nearby" as the user zoomed in; spec was to handle streaming data eventually.) Had problems with it crashing in pointer-related ways (in 1990, nobody had a lot of C++ experience) so I cooked up a really dumb "just realloc an array" version so I could figure out if the problem was above or below data structure... and not only didn't the "dumb" version crash, it was also much faster (and of faster order!) due to amortized realloc - doing a more expensive operation much less often turns out to be a really good trick :-)
rsc•Mar 18, 2026
Worth noting these were not written as rules of programming generally but rules specifically targeted at complexity. They are lifted from the "Complexity" section of Rob's "Notes on Programming in C".
Even agree with most of the stuff he said, however in real life, you won't be able to win a fight using these arguments. You can only create a environment to execute these strategies first to get a good product and them use them as a propaganda afterwards.
nelsonfigueroa•Mar 18, 2026
> Data structures, not algorithms, are central to programming
I'm a big time leetcode interview hater and reading that felt validating. Why the f*ck am I always asked about algorithms.
piskov•Mar 18, 2026
> Pike's rules 1 and 2 restate Tony Hoare's famous maxim "Premature optimization is the root of all evil."
This thing never resonated with me.
I often hear it as an excuse to ignore “optimization” at all.
It’s like “broken windows” theory. This allows slop, rot, and technical debt in. And it spreads fast.
Also if everything is unoptimized, this is not what could be easily fixed.
Death of thousand cuts, if you will.
kinnth•Mar 18, 2026
I used this with Claude to cross compare against my current project and it found 11 pretty significant improvements. Very awesome set of prompts for the ai to then work on.
apf6•Mar 18, 2026
In a world of AI coding I think rule 5 is still as important than ever. I don’t validate everything Claude does but I do put attention on data structure design since it’s so important.
78 Comments
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
Always preferred Perlis' version, that might be slightly over-used in functional programming to justify all kinds of hijinks, but with some nuance works out really well in practice:
> 9. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
Bill Gates, for example, always advocated for thinking through the entire program design and data structures before writing any code, emphasizing that structure is crucial to success.
Microsoft is another story.
While developing Altair BASIC, his choice of data structures and algorithms enabled him to fit the code into just 4 kilobytes.
You don't need to be able to pass a leet code interview, but you should know about big O complexity, you should be able to work out if a linked list is better than an array, you should be able to program a trie, and you should be at least aware of concepts like cache coherence / locality. You don't need to be an expert, but these are realities of the way software and hardware work. They're also not super complex to gain a working knowledge of, and various LLMs are probably a really good way to gain that knowledge.
This is what I've observed with using AI on relatively small (~1000 line) programs. When I add a requirement that requires a different data structure, Claude will happily move to the new optimal data structure, and rewrite literally everything accordingly.
I've heard that it gets dicier when you have source files that are 30K-40K lines and programs that are in the million+ line range. My reports have reported that Gemini falls down badly in this case, because the source file blows the context window. But even then, they've also reported that you can make progress by asking Gemini to come up with the new design, and then asking it to come up with a list of modules that depend upon the old structure, and then asking it to write a shim layer module-by-module to have the old code use the new data structure, and then have it replace the old data structure with the new one, and then have it remove the shim layer and rewrite the code of each module to natively use the new data structure. Basically, babysit it through the same refactoring that an experienced programmer would use to do a large-scale refactoring in a million+ line codebase, but have the AI rewrite modules in 5 minutes that would take a programmer 5 weeks.
Pike is right.
I would guess Pike is simply wise enough not to get involved in such arguments.
“If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident.”
And:
“Use simple algorithms as well as simple data structures.”
A data structure general enough to solve enough problems to be meaningful will either be poorly suited to some problems or have complex algorithms for those problems, or both.
There are reasons we don’t all use graph databases or triple stores, and rely on abstractions over our byte arrays.
Let's say you're working for the DMV on a program for driver's licenses. The idea is to use one structure for driver's license data, as opposed to using one structure for new driver's licenses, a different one for renewals, and yet a third for expired ones, and a fourth one for name changes.
It is not saying that you should use byte arrays for driver's license records, so that you can use the same data structure for driver's license data and missile tracks. Generalize within your program, not across all possible programs running on all computers.
You do not write programs with one map of id to thing as you are suggesting here.
>I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
-- Linus Torvalds
This kind of exploration can be a really positive use case for AI I think, like show me a sketch of this design vs that design and let's compare them together.
Not sure if SoTA codegen models are capable of navigating design space and coming up with optimal solutions. Like for cybersecurity, may be specialized models (like DeepMind's Sec-Gemini), if there are any, might?
I reckon, a programmer who already has learnt about / explored the design space, will be able to prompt more pointedly and evaluate the output qualitatively.
> sometimes a barrier to getting started for me
Plenty great books on the topic (:
Algorithms + Data Structures = Programs (1976), https://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures...
My recommendation is to truly learn a functional language and apply it to a real world product. Then you’ll learn how to think about data, in its pure state, and how it is transformed to get from point A to point B. These lessons will make for much cleaner design that will be applicable to imperative languages as well.
Or learn C where you do not have the luxury of using high-level crutches.
> -- Linus Torvalds
What about programmers
- for whom the code is a data structure?
- who formulate their data structures in a way (e.g. in a very powerful type system) such that all the data structures are code?
- who invent a completely novel way of thinking about computer programs such that in this paradigm both code and data structures are just trivial special cases of some mind-blowing concept ζ of which there exist other special cases that are useful to write powerful programs, but these special cases are completely alien from anything that could be called "code" or "data (structures)", i.e. these programmers don't think/worry about code or data structures, but about ζ?
That's great
https://ocw.mit.edu/courses/6-001-structure-and-interpretati...
which I found very helpful in (finally) managing to get through that entire text (and do all the exercises).
My interpretation of his point of view is that what you need is a process/interpreter/live object that 'explains' the data.
https://news.ycombinator.com/item?id=11945722
EDIT: He writes more about it in Quora. In brief, he says it is 'meaning', not 'data' that is central to programming.
https://qr.ae/pCVB9m
One part of it has interesting new resonance in the era of agentic LLMs:
alankay on June 21, 2016 | root | parent | next [–]
This is why "the objects of the future" have to be ambassadors that can negotiate with other objects they've never seen. Think about this as one of the consequences of massive scaling ...
Nowdays rather than the methods associated with data objects, we are dealing with "context" and "prompts".
I should probably be thinking more in this direction.
What I am getting at is that when one has such gigantic data structure there is no separation of concerns.
Anytime one has access to a database one has access to one large global data structure that one can access from anywhere is a program.
This same concept goes for one's global state in one's game if one is making a game.
You keep saying you believe it, but that is literally what a database is, game state manipulation, string manipulation, iterator algorithms, list comprehensions, range algorithms, image manipulations, etc. These are all instances where you use the same data structures over and over with as many algorithms and functions and you need.
> Busywork code is not important. Data is important. And data is not difficult. It's only data. If you have too much, filter it. If it's not what you want, map it. Focus on the data; leave the busywork behind.
The key difficulty is identifying what these are is far from obvious upfront, and so often an index appears adjacent to a table that represents what the table should have been in the first place.
This is why I fundamentally find SQL too conservative and outdated. There are obvious patterns for cross-cutting concerns that would mitigate things like this but enterprise SQL products like Oracle and MS are awful at providing ways to do these reusable cross-cutting concerns consistently.
> Good programmers worry about data structures and their relationships.
> -- Linus Torvalds
I was specifically thinking about the "relationship" issues. The worst messes to fix are the ones where the programmer didn't consider how to relate the objects together - which relationships need to be direct PK bindings, which can be indirect, which things have to be cached vs calculated live, which things are the cache (vs the master copy), what the cardinality of each relationship is, which relationships are semantically ownerships vs peers, which data is part of the system itself vs configuration data vs live, how you handle changes to the data, (event sourcing vs changelogging vs vs append-only vs yolo update), etc.
Not quite "data structures" I admit but absolutely thinking hard about the relationship between all the data you have.
SQL doesn't frame all of these questions out for you but it's good getting you to start thinking about them in a way you might not otherwise.
Perlin: stringly typed logic is great!
I don't take it like that. A map could be the right data structure for something people typically reach for classes to do, and then you get a whole bunch of functions that can already operate on a map-like thing for free.
If you take a look at the standard library and the data structures of Clojure you'd see this approach taken to a somewhat extreme amount.
If I have learned one thing in my 30-40 years spent writing code, it is this.
You never know how requirements are going to change over the next 5 years, and pure structures are always the most flexible to work with.
You still have to worry about someone using kg when you use g, but you avoid a large class of problems and make your logic easier.
> 2. Functions delay binding; data structures induce binding. Moral: Structure data late in the programming process.
This is the worst sort of documentation; technically true but quite unenlightening. It is, in the parlance of the Fred Brooks quote mentioned in a sibling comment, neither the "flowchart" nor the "tables"; it is simply a brute enumeration of code.
To which the fix is, ask for the right thing. Ask for it to analyze the key data structures (tables) and provide you the flow through the program (the flowchart). It'll do it no problem. Might be inaccurate, as is a hazard with all documentation, but it makes as good a try at this style of documentation as "conventional" documentation.
Honestly one of the biggest problems I have with AI coding and documentation is just that the training set is filled to the brim with mediocrity and the defaults are inferior like this on numerous fronts. Also relevant to this conversation is that AI tends to code the same way it documents and it won't have either clear flow charts or tables unless you carefully prompt for them. It's pretty good at doing it when you ask, but if you don't ask you're gonna get a mess.
(And I find, at least in my contexts, using opus, you can't seem to prompt it to "use good data structures" in advance, it just writes scripting code like it always does and like that part of the prompt wasn't there. You pretty much have to come back in after its first cut and tell it what data structures to create. Then it's really good at the rest. YMMV, as is the way of AI.)
> Rule 5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
If want to solve a problem - it's natural to think about logic flow and the code that implements that first and the data structures are an after thought, whereas Rule 5 is spot on.
Conputers are machines that transform an input to an output.
It is?
How can you conceive of a precise idea of how to solve a problem without a similarly precise idea of how you intend to represent the information fundamental to it? They are inseparable.
Do you start with the logical task first and structure the data second, or do you actually think about the data structures first?
Let's say I have a optimisation problem - I have a simple scoring function - and I just want to find the solution with the best score. Starting with the logic.
for all solutions, score, keep if max.
Simple eh? Problem is it's a combinatorial solution space. The key to solving this before the entropic death of the universe is to think about the structure of the solution space.
Neither data structures nor algorithms, but entities and tasks, from the user POV, one level up from any kind of implementation detail.
There's no point trying to do something if you have no idea what you're doing, or why.
When you know the what and why you can start worrying about the how.
Iff this is your 50th CRUD app you can probably skip this stage. But if it's green field development - no.
eg sort a list.
That's why a collection of "obvious" things formulated in a convincing way by a person with big street cred is still useful and worth elevating.
"Why quote someone who's just quoting someone else?" — Michael Scott — knorker
https://news.ycombinator.com/item?id=47325225
There’s several orders of magnitude less available discussion of selecting data structures for problem domains than there is code.
If the underlying information is implicit in high volume of code available then maybe the models are good at it, especially when driven by devs who can/will prompt in that direction. And that assumption seems likely related to how much code was written by devs who focus on data.
I believe that’s what most algorithms books are about. And most OS book talks more about data than algorithms. And if you watch livestream or read books on practical projects, you’ll see that a lot of refactor is first selecting a data structure, then adapt the code around it. DDD is about data structure.
Based on everything public, Pike is deeply hostile to generative AI in general:
- The Christmas 2025 incident (https://simonwillison.net/2025/Dec/26/slop-acts-of-kindness/)
- he's labeled GenAI as nuclear waste (https://www.webpronews.com/rob-pike-labels-generative-ai-nuc...)
- ideologically, he's spent his career chasing complexity reduction, adovcating for code sobriety, resource efficiency, and clarity of thought. Large, opaque, energy-intensive LLMs represent the antithesis.
The whole article is an AI hallucination. It refers to the same "Christmas 2025 incident". The internet is dead for real.
The thing is, if you build enough of the same kinds of systems in the same kinds of domains, you can kinda tell where you should optimize ahead of time.
Most of us tend to build the same kinds of systems and usually spend a career or a good chunk of our careers in a given domain. I feel like you can't really be considered a staff/principal if you can't already tell ahead of time where the perf bottleneck will be just on experience and intuition.
I have several times done performance tests before starting a project to confirm it can be made fast enough to be viable, the entire approach can often shift depending on how quickly something can be done.
It's the same thing with programmers who believe in BDUF or disbelieve YAGNI - they design architectures for anticipated futures which do not materialize instead of evolving the architecture retrospectively in line with the future which did materialize.
I think it's a natural human foible. Gambling, for instance, probably wouldnt exist if humans' gut instincts about their ability to predict future defaulted to realistic.
This is why no matter how many brilliant programmers scream YAGNI, dont do BDUF and dont prematurely optimize there will always be some comment saying the equivalent of "akshually sometimes you should...", remembering that one time when they metaphorically rolled a double six and anticipated the necessary architecture correctly when it wasnt even necessary to do so.
These programmers are all hopped up on a different kind of roulette these days...
Don't insist on file-based data ingestion being a wrapper around a json-rpc api just because most similar things are moving that direction; what matters is whether someone has specifically asked for that for this particular system yet.
.
Not all decisions can be usefully revisited later. Sometimes you really do need to go "what if..." and make sure none of the possibilities will bite too hard. Leaving the pizza cave occasionally and making sure you (have contacts who) have some idea about the direction of the industry you're writing stuff for can help.
You're going to build an audit trail, no matter what. There's no validated system in LS that does not have an audit trail.
It's just like e-commerce; you're going to have a cart and a checkout page. There's no point in calling that a premature optimization. Every e-commerce website has more or less the same set of flows with simply different configuration/parameters/providers.
Audit trails are commonly neglected coz somebody didnt ask the right questions, not coz somebody didnt try to anticipate the future.
Unix was created by Ken Thompson and Dennis Ritchie at Bell Labs (AT&T) in 1969. Thompson wrote the initial version, and Ritchie later contributed significantly, including developing the C programming language, which Unix was subsequently rewritten in.
contribute < wrote.
His credits are huge, but I think saying he wrote Unix is misattribution.
Credits include: Plan 9 (successor to Unix), Unix Window System, UTF-8 (maybe his most universally impactful contribution), Unix Philosophy Articulation, strings/greps/other tools, regular expressions, C successor work that ultimately let him to Go.
If you'd said Plan 9 and UTF-8 I'd agree with you.
Unless you meant to imply that UNIX isn't cool.
A lot of people are learning some history today, beautiful to see.
Notice my use of the word "Novelty".
I get hired because I'm very good at building specific kinds of systems so I tend to build many variants of the same kinds of systems. They are generally not that different and the ways in which the applications perform are similar.
I do not generally write new algorithms, operating systems, nor programming languages.
I don't think this is so hard to understand the nuance of Pike's advice and what we "mortals" do in or day-to-day to earn a living.
Rules are "kinda" made to be broken. Be free.
I've been sticking to these rules (and will keep sticking to them) for as long as I can program (I've been doing it for the last 30 years).
IMHO, you can feel that a bottleneck is likely to occur, but you definitely can't tell where, when, or how it will actually happen.
LLM's work will never be reproducible by design.
This is probably the worst use of the word "shortened" ever, and it should be more like "mutilated"?
edit: s/data/data structure/
Good software can handle crap data.
Knuth later attributed it to Hoare, but Hoare said he had no recollection of it and suggested it might have been Dijkstra.
Rule 5 aged the best. "Data dominates" is the lesson every senior engineer eventually learns the hard way.
Funny handwritten html artifact though:
This Axiom has caused far and away more damage to software development than the premature optimization ever will.
> We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Rob Pike’s Rules of Programming (1989) - https://news.ycombinator.com/item?id=38097031 - Nov 2023 (259 comments)
Rob Pike’s Rules of Programming (1989) - https://news.ycombinator.com/item?id=24135189 - Aug 2020 (323 comments)
Rob Pike’s Rules of Programming (1989) - https://news.ycombinator.com/item?id=15776124 - Nov 2017 (18 comments)
Rob Pike’s Rules of Programming (1989) - https://news.ycombinator.com/item?id=15265356 - Sept 2017 (112 comments)
Rob Pike’s Rules of Programming (1989) - https://news.ycombinator.com/item?id=7994102 - July 2014 (96 comments)
I believe that's why Golang is a very simple but powerful language.
"Premature optimization is the root of all evil."
First, let's not besmirch the good name of Tony Hoare. The quote is from Donald Knuth, and the missing context is essential.
From his 1974 paper, "Structured Programming with go to Statements":
"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."
He was talking about using GOTO statements in C. He was talking about making software much harder to reason about in the name of micro-optimizations. He assumed (incorrectly) that we would respect the machines our software runs on.
Multiple generations of programmers have now been raised to believe that brutally inefficient, bloated, and slow software is just fine. There is no limit to the amount of boilerplate and indirection a computer can be forced to execute. There is no ceiling to the crystalline abstractions emerging from these geniuses. There is no amount of time too long for a JVM to spend starting.
I worked at Google many years ago. I have lived the absolute nightmares that evolve from the willful misunderstanding of this quote.
No thank you. Never again.
I have committed these sins more than any other, and I'm mad as hell about it.
Oh yes, I'd recommend everyone who uses the phrase reads the rest of the paper to see the kinds of optimisations that Knuth considers justified. For example, optimising memory accesses in quicksort.
I wish Knuth would come out and publicly chastise the many decades of abuse this quote has enabled.
Tips like "don't try to write smart code" are often repeated but useless (not to mention that "smart" here means over-engineered or overly complex, not smart).
1. Somebody verifies with the users that speed is actually one of the most burning problems.
2. They profile the code and discover a bottleneck.
3. Somebody says "no, but we shouldnt fix that, that's premature optimization!"
Ive heard all sorts of people like OP moan that "this is why pieces of shit like slack are bloated and slow" (it isnt) when advocating skipping steps 1 and 2 though.
I dont think they misunderstand the rule, either, they just dont agree with it.
Did pike really have to specify explicitly that you have to identify that a problem is a problem before solving it?
Sometimes this is too late.
C++98 introduce `std::set` and `std::map`. The public interface means that they are effectively constrained to being red-black trees, with poor cache locality and suboptimal lookup. It took until C++11 for `std::unordered_map` and `std::unordered_set`, which brought with them the adage that you should probably use them unless you know you want ordering. Now since C++23 we finally have `std::flat_set` and `std::flat_map`, with contiguous memory layouts. 25 years to half-solve an optimisation problem and naive developers will still be using the wrong thing.
As soon as the interface made contact with the public, the opportunity to follow Rob Pike's Rule 5 was lost. If you create something where you're expected to uphold a certain behaviour, you need to consider if the performance of data structures could be a functional constraint.
At this point, the rule becomes cyclical and nonsensical: it's not premature if it's the right time to do it. It's not optimisation if it's functional.
When building interfaces you are bound to make mistakes which end users will end up depending on (not just regarding optimization).
The correct lesson to learn from this is not "just dont make mistakes" but to try and minimize migration costs to prevent these mistakes from getting tightly locked in and try to detect these mistakes earlier on in the design process with more coordinated experimentation.
C++ seems pretty bad at both. It's not unusual, either - migration and upgrade paths are often the most neglected part of a product.
std::set/std::map got into trouble because they chose the algorithm first and then made the data model match. Rule 5 suggests choosing the right data model first, indicating that it is most important.
Like you, I've seen people produce a lot of slow code, but it's mostly been from people who would have a really hard time writing faster code that's less wrong.
I hate slow software, but I'd pick it anytime over bogus software. Also, generally, it's easier to fix performance problems than incorrect behavior, especially so when the error has created data that's stored somewhere we might not have access to. But even more so, when the harm has reached the real world.
We can and should have both.
This is a fraud, made up by midwits to justify their leaning towers of abstraction.
And I'd agree that "simple secure" is better than "complex secure" but you're kind of side-stepping what I said, what about "not secure at all", wouldn't that lead to simpler code? Usually does for me, especially if you have to pile it on top of something that is already not so secure, but even when taking it into account when designing from ground up.
"Do less and things get faster" is a very wide class of fixes. e.g. you could do tons of per-packet decision making millions of times per second for routing and security policies, or you could realize the answer changes slowly in time, and move that to upfront work, separating your control vs data processing, and generally making it easier to understand. Or you could build your logic into your addressing/subnets and turn it into a simple mask and small table lookup. So your entire logic gets boiled down to a table (incidentally why I can't understand why people say ipv6 is complex. Try using ipv4! Having more bits for addresses is awesome!).
Sort of. But if you keep the software simple, then it is easier to optimize the bottlenecks. You don't really need to make everything complicated to make it faster, just a few well selected places need to be refactored.
Same. I, too, am sick of bloated code. But I use the quote as a reminder to myself: "look, the fact that you could spend the rest of the workday making this function run in linear instead of quadratic time doesn't mean you should – you have so many other tasks to tackle that it's better that you leave the suboptimal-but-obviously-correct implementation of this one little piece as-is for now, and return to it later if you need to".
I think that's due to people doing premature optimization! If people took the quote to heart, they would be less inclined to increasing the amount of boilerplate and indirection.
While you were seeing those problems with Java at Google, I saw seeing it with Python.
So many levels of indirection. Holy cow! So many unneeded superclasses and mixins! You can’t reason about code if the indirection is deeper than the human mind can grasp.
There was also a belief that list comprehensions were magically better somehow and would expand to 10-line monstrosities of unreadable code when a nested for loop would have been more readable and just as fast but because list comprehensions were fetishized nobody would stop at their natural readability limits. The result was like reading the run-on sentence you just suffered through.
Profiling never achieved its place in most developers’ core loop the way that compiling, linting, or unit testing did.
How many real CI/CD pipelines spit out flame graphs alongside test results?
I find 98% of the time that users are clamoring to get something implemented or fixed which isnt speed related so I work on that instead.
When I do drill down what I tend to find in the flame graphs is that your scope for making performance improvements a user will actually notice is bottlenecked primarily by I/O not by code efficiency.
Meanwhile my less experienced coworkers will spot a nested loop that will never take more than a couple of milliseconds and demand it be "optimised".
Also the rule (quote?) says "speed hack", I don't think he is saying ignore runtime complexity totally, just don't go crazy with really complex stuff until you are sure you need it.
People don't ask for software to be fast and usable because it obviously should be. Why would they ask? They might complain when it's unusably slow. But that doesn't mean they don't want it to be fast.
Far too often we generalise a piece of logic that we need in one or two places, making things more complicated for ourselves whenever they inevitably start to differ. And chances are very slim we will actually need it more than twice.
Premature generalisation is the most common mistake that separates a junior developer from an experienced one.
DRY is one step removed from that goal and people use it to make very unmaintainable code because they confuse any repeated code with unmaintainability. (or their theory that some day we might want to repeat this code so we might as well pre-DRY it)
The result is often a horrendous complex mess. Imagine a cookbook with a cookie recipe that resided on 47 different pages (40 of which were pointers on where to find other pointers on where to find other pointers on where to find a step) in attempts to never write the same step twice in the whole book or your planned sequels in a 20 volume set.
The problem is zealots. Zealotry doesn't work for indeterminate things that require judgement like "code quality" or "maintainability", but a simple rule like "don't repeat yourself" is easy for a zeal. They take a rule and shut down any argument with "because the rule!"
If you're arguing about code quality and maintainability without one sentence rules then you actually have to make arguments. If the rule is your argument there's no discussion only dogma.
As a result? Easy to distill rules spread fast, breed zealots, and result in bad code.
Instead, I tend to ask: if I change this code here, will I always also need to change it over there?
Copy-paste is good as long as I'm just repeating patterns. A for loop is a pattern. I use for loops in many places. That doesn't mean I need to somehow abstract out for loops because I'm repeating myself.
But if I have logic that says that button_b.x = button_a.x + button_a.w + padding, then I should make sure that I only write that information down once, so that it stays consistent throughout the program.
Your example is a pretty good one. In most practical applications, you do not want to be setting button x coordinates manually. You want to use a layout manager, like CSS Flexbox or Jetpack Compose's Row or Java Swing's FlowLayout, which takes in a padding and a direction for a collection of elements and automatically figures out where they should be placed. But if you only have one button, this is overkill. If you only have two buttons, this is overkill. If you have 3 buttons, you should start to realize this is the pattern and reach for the right abstraction. If you get to 10 buttons, you'll realize that you need to arrange them in 2D as well and handle how they grow & shrink as you resize the window, and there's a good chance you need a more powerful abstraction.
IMO, this is the exact (and arguably only) question to ask.
https://caseymuratori.com/blog_0015
Having identical logic in multiple places (even only 2) is a big contributor to technical debt, since if you're searching for something and you find it and fix it /once/ we often thing of the job as done. Then the "there is still a bug and I already fixed that" confusion is avoided by staying DRY.
If two pieces of code use the same functionality by coincidence but could possibly evolve differently then don't refactor. Don't even refactor if this happens three, four, or five times. Because even if the code may be identical today the features are not actually identical.
But if you have two uses of code that actually semantically identical and will assuredly evolve together then go ahead and refactor to remove duplication.
https://xkcd.com/1205/
https://xkcd.com/974/
The goal is to have code that corresponds to a coherent conceptual model for whatever you are doing, and the resulting codebase should clearly reflect the design of the system. Once I started thinking about code in these terms, I realized that questions like "DRY vs YAGNI" were not meaningful.
Exactly.
Yet again, understanding when to follow a rule of thumb or not is another thing that separates the junior from the senior.
It's not about copying identical code twice, it's about refactoring similar code into a shared function once you have enough examples to be able to see what the shared core is.
I too often see junior engineers (and senior data scientists…) write code procedurally, with giant functions and many, many if statements, presumably because in their brain they’re thinking about “1st I do this if this, 2nd I do that if that, etc”.
Well, turns out that 3 of the APIs changed the way they return the data, so instead of separating the logic, someone kept adding a bunch of if statements into a single function in order to avoid repeating the code in multiple places. It was a nightmare to maintain and I ended up completely refactoring it, and even tho some of the code was repeated, it was much easier to maintain and accommodate to the API changes.
Extract a method or object if it's something that feels conceptually a "thing" even if it has only one use. Most tools to DRY your code also help by providing a bit of encapsulation that do a great job of tidying things up to force you to think about "should I be letting this out of domain stuff leak in here?"
Sometimes four or five doesn’t seem too bad, sometimes two is too many
Mostly at the massive switch statements and 1000 line's of flow control logic that end up embedded someplace where they really dont belong in the worst cases.
If you have two copies of some piece of code, and you can reasonably say that if you ever want to update one copy then you will almost certainly want to update the other copy as well, then it's probably a good idea to try to merge them and keep that logic in some centralized place.
On the other hand, if you have three copies of the same piece of code, but they kind of just "happen to" be identical and it's completely plausible that any one of the copies will be modified in the future for reasons which won't affect the other copies, maybe keeping them separate is a good idea.
And of course, it's sometimes worth it to keep two or more different copies which do share the same "reason to change". This is especially clear when you have the copies in different repositories, where making the code "DRY" would mean introducing dependencies between repositories which has its own costs.
An expensive consultant suggested creating pristine implementation and then writing a rule layer that would modify things as needed and deploying the whole thing as a pile of lamdba functions.
I copy pasted the protocol consumer file per producer and made all the necessary changes with proper documentation and mocks. Got it working quickly and we could add new ones without affecting.
If I'd try to keep it DRY, i think it would be a leaky mess.
Maybe I’ve had an unrepresentative career, but I’ve never worked anywhere where there’s much time to fiddle with performance optimisations, let alone those that make the code/system significantly harder to understand. I expect that’s true of most people working in mainstream tech companies of the last twenty years or so. And so that quote is basically never applicable.
(AI will probably make this worse as well, having a bloat tendency all of its own)
how do you know which code was written using this quote in mind.
I don't think you can blame this phrase if people are going to drop an entire word out of an eight word sentence. The very first word, no less.
I can write bubble sort, it is simple and I have confidence it will work. I wrote quicksort for class once - I turned in something that mostly worked but there were bugs I couldn't fix in time (but I could if I spent more time - I think...)
However writing bubble sort is wrong because any good language has a sort in the standard library (likely timsort or something else than quicksort in the real world)
Then the quote wasn’t the problem. The wilful misunderstanding was the problem.
If you don't know enough to pick good starting points you probably won't know enough to optimize well. So don't optimize prematurely.
If you are experienced enough to pick good starting points, still don't optimize prematurely.
If you see a bad starting point picked by someone else, by all means, point it out if it will be problematic now or in the foreseeable future, because that's a bug.
It's true that premature optimization (that is, optimization before you've measured the software and determined whether the optimization is going to make any real-world difference) is bad.
The reality, though, is that most programmers aren't grappling with whether their optimizations are premature, they're grappling with whether to optimize at all. At most companies, once the code works, it ships. There's little, if any, time given for an extra "optimization" pass.
It's only after customers start complaining about performance (or higher-ups start complaining about compute costs) that programmers are given any time to go through and optimize things. By which point refactoring the code is now much harder than it wouldn've been originally.
I've seen people write some really head shaking code that makes remote calls in a loop that don't actually depend on each other. I wonder to what extend they are thinking "don't bother with optimization / speed for now"
I think there is just a current (I've seen it mostly in Jr engineers) that you should just ignore any aspect of performance until "later"
> look, I'm sorry, but the rule is simple: if you made something 2x faster, you might have done something smart if you made something 100x faster, you definitely just stopped doing something stupid
https://x.com/rygorous/status/1271296834439282690
But second, I'd remove "optimization" from considering here. The code you're describing isn't slow, it's bad code that also happens to be slow. Don't write bad code, ever, if you can knowingly avoid it.
It's OK to write good, clear, slow code when correctness and understandability is more important that optimizing that particular bit. It's not OK to write boneheaded code.
(Exception: After you've written the working program, it turns out that you have all the information to make the query once in one part of the broader program, but don't have all the information to make it a second time until flow reaches another, decoupled part of the program. It may be the lesser evil to do that than rearrange the entire thing to pass all the necessary state around, although you're making a deal with the devil and pinky swearing never to add a 3rd call, then a 4th, then a 5th, then...)
"Later" never comes and all critical performance issues are either ignored, hot-patched externally with caches of various quality or just with more expensive hardware.
Broken gets fixed, but crappy stays forever
Sometimes, especially when it comes to distributed systems, going from working solution to fast working solution requires full blown up redesign from scratch.
1. I have seen too many "make it work first" that ended up absolute shitshow that was notoriously difficult to do anything with. You can build the software right the first time
2. The "fast" part is what I think too many people are focusing on and in my experience the "THEN" part is always missing resources utilization and other types of inefficiency that are not necessarily related to speed. I have seen absolute messes of software that work really fast
I believe people don't think about Knuth when they choose to write app in Electron. Some other forces might be at play here.
I am almost certain that people building bloated software are not willfully misunderstanding this quote; it's likely they never heard about it. Let's not ignore the relevance of this half a century old advice just because many programmers do not care about efficiency or do not understand how computers work. Premature optimization is exactly that, the fact that is premature makes it wrong, regardless if it's about GOTO statements in the 70s or a some modern equivalent where in the name of craft or fun people make their apps a lot more complex than they should be. I wouldn't be surprised if some of the brutally inefficient code you mention was so because people optimized prematurely for web-scale and their app never ever needed those abstractions and extra components. The advice applies both to hackers doing micro-optimizations and architecture astronauts dreaming too big IMHO.
And then of course later is too late; you can't optimise most Python.
That's not to bemoan the engineer with shortcomings. Even the most experienced and educated engineer might find themself outside their comfort zone, implementing code without the ability to anticipate the performance characteristics under the hood. A mental model of computation can only go so far.
Articulated more succinctly, one might say "Use the profiler, and use it often."
100%
Devs are obsessed with introducing functional-style constructs everywhere, just for the sake of it. FP is great for some classes of software, but baseline crufty for anything that requires responsiveness (front-ends basically), let alone anything at real interactive speeds (games, geo-software, ...)
The "premature optimization" quote is then always used as a way to ignore that entire code paths will be spamming the heap with hundreds of thousands of temporary junk, useless lexical scopes, and so forth. Writing it lean the first time is never considered, because of adherence to these fetishes (mutability is bad, oo is bad, loops lead to off-by-one errors, ...). It's absolutely exhausting to have these conversations, it's always starting from the ground up and these quotes like "premature optimization is the root of all evil" are only used as invocations to ward of criticism.
Yes, software is bloated, full of useless abstractions and bad design. You kids(well, anyone programming post 1980, so myself included) should be ashamed. Also let's not forget that those abstractions helped us solve problems and our friends in silicon valley(ok that no longer makes sense but imagine if SillyValley still just made HW) covered our mistakes. But yeah, we write crap a lot of the time.
But as other folks have said, it doesn't mean "don't optimize."
I've always used my own version of the phrase, which is: "Don't be stupid." As in, don't do dumb, expensive things unless you need to for a prototype. Don't start with a design that is far from optimal and slow. After profiling, fix the slow things. I'm pretty sure that's what most folks do on some level.
Of course today this has changed. You can have multiple agents working on micro optimizing everything and have the pie and eat it too.
The average university CS student in USA (and India I presume) is taught to "hack it" at any cost, and we see the results.
Sorry, folks, but that's just an excuse to make dumb choices. Premature _micro_optimization is the root of all evil.
EDIT: It was great training for when I started working on browser performance, though!
It's just complaining about others making a different value judgement for what is a worthwhile optimization. Hiding behind the 'true meaning of the quote' is pointless.
While it might not be necessary to spend hours fine-tuning every function; code optimization should be the mindset of every programmer no matter what they are coding.
How many fewer data centers would we need if all that software running in them was more efficient?
https://didgets.substack.com/p/finding-and-fixing-a-billion-...
Actually, I do not believe devs are to blame, or that CS education is to blame; I believe that's an unfortunate law of society that complexity piles up faster than we can manage it. Of course the economic system rewards shiping today at the expense of tomorrow's maintenance, and also rewards splitting systems in seemingly independent subsystems that are simpler in isolation but results in a more complex machinery (cloud, microservices...)
I'm even wondering if it's not a more fundamental law than that, because adding complexity is always simpler than removing it, right? Kind of a second law of termodynamic for code.
> He was talking about using GOTO statements in C.
I don’t think he was talking about C. That paper is from December 1974, and (early) C is from 1972, and “The UNIX Time-Sharing System” (https://dsf.berkeley.edu/cs262/unix.pdf) is from July 1974, so time wise, he could have known C, but AFAICT that paper doesn’t mention C, and the examples are PL/I or (what to me looks like) pseudocode, using ‘:=’ for assignment, ‘if…fi’ and ‘while…repeat’ for block, ‘go to’ and not C’s ‘goto’, etc.
> Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
To address our favorite topic: while I use LLMs to assist on coding tasks a lot, I think they're very weak at this. Claude is much more likely to suggest or expand complex control flow logic on small data types than it is to recognize and implement an opportunity to encapsulate ideas in composable chunks. And I don't buy the idea that this doesn't matter since most code will be produced and consumed by LLMs. The LLMs of today are much more effective on code bases that have already been thoughtfully designed. So are humans. Why would that change?
I think rule 5 is often ignored by a lot of distributed services. Where you have to make several calls, each with their own http, db and "security" overhead, when one would do. Then these each end up with caching layers because they are "slow" (in aggregate).
Very few software services built today are doing it right. Most assume they need to scale from day one, pick a technology stack to enable that, and then alter the product to reflect the limitations of the tech stack they picked. Then they wonder why they need to spend millions on sales and marketing to convince people to use the product they've built, and millions on AWS bills to scale it. But then, the core problem was really that their company did not need to exist in the first place and only does because investors insist on cargo-culting the latest hot thing.
This is why software sucks so much today.
I'll add one more modification if you're like me (and apparently many others): go too far with your distribution and pull it back to a sane (i.e. small handful) number of distributed services, hopefully before you get too far down the implementation...
Having implemented my shared of highly complex high-performance algorithms in the past, the key was always to figure out how to massage the raw data into structures that allow the algorithm to fly. It requires both a decent knowledge of the various algorithm options you have, as well as being flexible to see that the data could be presented a different way to get to the same result orders of magnitude faster.
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
Number 5 is timeless and relevant at all scales, especially as code iterations have gotten faster and faster, data is all the more relevant. Numbers 4 and 3 have shifted a bit since data sizes and performance have ballooned, algorithm overhead isn't quite as big a concern, but the simplicity argument is relevant as ever. Numbers 2 and 1 while still true (Amdahl's law is a mathematical truth after all), are also clearly a product of their time and the hard constraints programmers had to deal with at the time as well as the shallowness of the stack. Still good wisdom, though I think on the whole the majority of programmers are less concerned about performance than they should be, especially compared to 50 years ago.
1(a) Torvalds: "Bad programmers worry about the code. Good programmers worry about data structures and their relationships."
1(b) Pike Rule 5: "Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming."
— versus —
2. Perlis 2: "Functions delay binding; data structures induce binding. Moral: Structure data late in the programming process."
---
Ignorant as I am, I read these to advise that I ought to put data structures centrally, first, foremost — but not until the end of the programming process.
Good structure comes from exploring until you understand the problem well AND THEN letting data structure dominate.
So not really a contradiction, just Perlis talking about the functional shell and Torvalds/Pike talking about the imperative core.
When you know what and how to build commit to good data structures. Do the types, structs, classes, Trie, CRDTs, XML, Protobuf, Parquet and whatnot where apropriate. Instrument your program. The efficiency of the final product counts.
For example, I've often heard "premature optimization is the root of all evil" invoked to support opposite sides of the same argument. Pike's rules are much clearer and harder to interpret creatively.
Also, it's amusing that you don't hear this anymore:
> Rule 5 is often shortened to "write stupid code that uses smart objects".
In context, this clearly means that if you invest enough mental work in designing your data structures, it's easy to write simple code to solve your problem. But interpreted through an OO mindset, this could be seen as encouraging one of the classic noob mistakes of the heyday of OO: believing that your code could be as complex as you wanted, without cost, as long as you hid the complicated bits inside member methods on your objects. I'm guessing that "write stupid code that uses smart objects" was a snappy bit of wisdom in the pre-OO days and was discarded as dangerous when the context of OO created a new and harmful way of interpreting it.
keeping the historical chain of thinking alive is good, actually
My #1 programming principle would be phrased using a concept from John Boyd: make your OODA loops fast. In software this can often mean simple things like "make compile time fast" or "make sure you can detect errors quickly".
Even knowing with 100% certainty that performance will be subpar, requirements change often enough that it's often not worth the cost of adding architectural complexity too early.
I think there is value in attempting to do something the "wrong way" on purpose to some extent. I have walked into many situations where I was beyond convinced that the performance of something would suck only to be corrected harshly by the realities of modern computer systems.
Framing things as "yes, I know the performance is definitely not ideal in this iteration" puts that monkey in a proper cage until the next time around. If you don't frame it this way up front, you might be constantly baited into chasing the performance monkey around. Its taunts can be really difficult to ignore.
That said management did not quite understand. They thought that I should have known about the bottleneck (Actually I did but I was told not to prematurely optimize)
I end up writing the program three times, the final solution was honestly beautiful.
Management was not happy. The customer was happy.
I've always been a KISS/DRY person but over a decade there are plenty of moments where you're tempted to reach for a fancier database or rewrite something in a trendier stack. What's actually kept things running well at scale is boring, known technologies and only optimizing in the places where it actually matters.
We wrote our principles down recently and it basically just reads like Pike's rules in different words: https://www.geocod.io/code-and-coordinates/2025-09-30-develo...
I think that Rob Pike was far more of a wordcel than a shape rotator for a famous computer scientist (which historically were very much on the shape rotator side).
<title> <h1>Rob Pike's 5 Rules of Programming</h1> </title>
Pike's rules are from 1989, which is before it was common to publish HTML.
Thankfully newer languages like Nim, Odin, and Swift lean hard into value semantics. They drastically reduce the cost of focusing on data structures and writing obvious algorithms. Then, when bottlenecks appear, you can choose to opt into fine-tuning.
[1]: https://wiki.c2.com/?WhatIsNotInPlanNine
For most of my career, sticking to rule 3 made the most sense. When the CS major would be annoying and talk about big-O they usually forgot n was tiny. But then my job changed. I started working on different things. Suddenly my job started sounding more like a leetcode interview people complain about. Now n really is big and now it really does matter.
Keep in mind that Rob Pike comes from a different era when programming for 'big iron' looked a lot more like programming for an embedded microcontroller now.
Of course there is a balance to this, the engineering time to implement both options is an important consideration. But given both algorithms are relatively easy to implement I will default to the one that is faster at large sizes even if it is slower at common sizes. I do suspect that there is an implicit assumption that "fancy" algorithms take longer and are harder to implement. But in many cases both algorithms are in the standard library and just need to be selected. If this post focused on "fancy" in terms of actual time to implement rather than speed for common sizes I would be more inclined to agree with it.
I wrote an article about this a while back: https://kevincox.ca/2023/05/09/less-than-quadratic/
It is important to remember that the art of sw engineering (like all engineering) lives in a balance between all these different requirements; not just in OPTIMIZE BIG-O.
At 99% of shops it should be the other way around .
I've personally found it useful to always have concrete numbers in mind. An algorithm or data structure designed for N will probably be fine for N/10 and 10N, but it will often be inefficient for N/1000 or 1000N.
Most people don't need FFT algorithm for multiplying large numbers, Karatsuba's algorithm is fine. But in some domains the difference does matter.
Personally I usually see the opposite effect - people first reach for a too-naive approach and implement some O(n^2) algorithm where it wouldn't have even been more complex to implement something O(n) or O(n log n). And n is almost always small so it works fine, until it blows up spectacularly.
Same. People solve in ways that are very obviously going to cause serious problems in only a few short weeks or months and it’s endlessly frustrating. If you’re building a prototype, fine, but if you’re building for production, very far from fine.
Most frustrating because often there’s next to no cost in selecting and implementing the correct architecture, domain model, data structure, or algorithm up front.
If you can't tell in advance what is performance critical, then consider everything to be performance critical.
I would then go against rule #3 "Fancy algorithms are slow when n is small, and n is usually small". n is usually small, except when it isn't, and as per rule #1, you may not know that ahead of time. Assuming n is going to be small is how you get accidentally quadratic behavior, such as the infamous GTA bug. So, assume n is going to be big unless you are sure it won't be. Understand that your users may use your software in ways you don't expect.
Note that if you really want high performance, you should properly characterize your "n" so that you can use the appropriate technique, it is hard because you need to know all your use cases and their implications in advance. Assuming n will be big is the easy way!
About rule #4, fancy algorithms are often not harder to implement, most of the times, it means using the right library.
About rule #2 (measure), yes, you absolutely should, but it doesn't mean you shouldn't consider performance before you measure. It would be like saying that you shouldn't worry about introducing bugs before testing. You should do your best to make your code fast and correct before you start measuring and testing.
What I agree with is that you shouldn't introduce speed hacks unless you know what you are doing. Most of performance come from giving it consideration on every step. Avoiding a copy here, using a hash map instead of a linear search there, etc... If you have to resort to a hack, it may be because you didn't consider performance early enough. For example, if took care of making a function fast enough, you may not have to cache results later on.
As for #5, I agree completely. Data is the most important. It applies to performance too, especially on modern hardware. To give you an very simplified idea, RAM access is about 100x slower than running a CPU instruction, it means you can get massive speed improvement by making your memory footprint smaller and using cache-friendly data structures.
As for rule 2: first you measure.
I get where he's coming from, but I've seen people get this very wrong in practice. They use an algorithm that's indeed faster for small n, which doesn't matter because anything was going to be fast enough for small n, meanwhile their algorithm is so slow for large n that it ends up becoming a production crisis just a year later. They prematurely optimized after all, but for an n that did not need optimization, while prematurely pessimizing for an n that ultimately did need optimization.
Of course with experience, you start to feel when the straight forward suboptimal code will cause massive performance issued. In this case it's critical to take action up front to avoid the mess. Its called software architecture, I guess.
I learnt about rule-5 through experience before I had heard it was a rule.
I used to do tech due diligence for acquisition of companies. I had a very short time, about a day. I hit upon a great time saver idea of asking them to show their DB schema and explain it. It turned out to be surprisingly effective. Once I understood the scheme most of the architecture explained itself.
Now I apply the same principle while designing a system.
Getting competent at it, however, is no joke and takes time.
"There's a third thing [beyond speed and memory] that you might want to optimize for which is much more important than either of these, which is years of your life required per program implementation." This is of course from the perspective of a solo indie game developer, but it's a good and interesting perspective to consider.
[1] https://www.youtube.com/watch?v=JjDsP5n2kSM
It's a good consideration tbh.
That also means that "just do an array of flat records" is a very sane default even if it seems brutish at first.
Even storing something simple such as an array of complex numbers as a structure of arrays (SoA) rather than an array of structures (AoS) can unlock a lot of optimizations. For example, less permutes/shuffles and more arithmetic instructions.
Depending on how many fields you actually need when you iterate over the data, you prevent cache pollution as well.
From a quick search, it seems HackerNews's own jcelerier has put together a C++ library for doing this. https://github.com/celtera/ahsohtoa
Fortnite, Roblox, League of Legends, Counter Strike . . . all very short term projects.
That meant I could attach Cheat Engine to Roblox, edit memory addresses to give myself in-game cash, and inject whatever code I wanted to everyone else.
Thankfully it was sandboxed so I couldn't give people viruses.
This was probably a good decision because most of Roblox's best games from that period weren't updated to use server functions. It's too difficult.
Game engine development is very much about processing of data. The pipeline is long and the tree is wide. Being able to reason about complicated data processing topologies mapped very easily across.
This is me with hash maps.
* Braid was 3 years
* Cave Story was 5 years
* World of Goo was 2 years
* Limbo was about 3 years (but with 8-16 people)
So Braid seems pretty average.
Also, Rule 5 contradicts the idea you quoted from Blow.
You've got 16 ms to get that next frame rendered, so stuffing everything into fixed sized tables and linear walks is a simple way to structure against that limit. You've got an entity budget of so many, and there's no unpredictable delays from dynamic allocation or similar.
Even when using scripting languages that offer more flexibility, like LUA or C#, you see a tendency towards this pattern.
If the next generation doesn't even want to learn a programming language, they're definitely not going to learn how to write _clean_ code.
Maybe I'm just overly pessimistic about junior engineers at the moment because of that conversation lol.
Anyway, I've found that if you want to get a coworker into reading technical books, the best way is with a novel or three. I've had good success with The Martian. The Phoenix Project might work too. Slip them fun books until they've built a habit and then drop The Mythical Man Month on them. :)
This was hard before, too.
Random side note: my teen son has grown up with iPhone-level tech, yet likes and finds my old Casio F91 watch very interesting. I still have faith :)
I’m hoping the situation with LLMs will be the same. Teach the basics and allow people to fall back on them for at least the simpler tasks for their lifetimes. I know people, by the way, who can still use an abacus and a slide rule. I can too, but with a refresher beforehand because I seldom use those.
PS: Not that we do not have people working at all levels of stack today, just that each level of stack, like a discussion going on today about python's JIT compiler will be a few (dozen or hundred) specialists. Everyone else can work with prompts.
In practice what I see fail most often is not premature optimization but premature abstraction. People build elaborate indirection layers for flexibility they never need, and those layers impose real costs on every future reader of the code. The irony is that abstraction is supposed to manage complexity but prematurely applied it just creates a different kind.
Compare and contrast https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf
This is something I tend to consider far, far worse than "AI Slop" in practice. I always hated Microsoft Enterprise Library's Data Access Application Block (DAAB) in practice. I've literally only ever seen one product that supported multiple database backends that necessitated that level of abstraction... but I've seen that library well over a dozen times in practice. Just as a specific example.
IMO, abstractions should generally serve to make the rest of the codebase reasonable more often than not... abstractions that hide complexity are useful... abstractions that add complexity much less so.
This matches my experience as well.
Someone here commented once that abstractions should be emergent, not speculative, and I loved that line so much I use it with my team all the time now when I see the craziness starting.
If you've been monitoring properly, you buy yourself time before it becomes a problem as such, but in my experience most developers who don't anticipate load scaling also don't monitor properly.
I've seen a "senior software engineer with 20 years of industry experience" put code into production that ended up needing 30 minute timeouts for a HTTP response only 2 years after initial deployment. That is not a typo, 30 minutes. I had to take over and rewrite their "simple" code to stop the VP-level escalations our org received because of this engineering philosophy.
There is nothing to suggest you should wait to optimize under pressure, only that you should optimize only after you have measured. Benchmark tests are still best written during the development cycle, not while running hot in production.
Starting with the naive solution helps quickly ensure that your API is sensible and that your testing/benchmarking is in good shape before you start poking at the hard bits where you are much more likely to screw things up, all while offering a baseline score to prove that your optimizations are actually necessary and an improvement.
I found that over time my senses have been honed to more quickly identify things that are important to deeply study and plan right now and areas where I can skimp more and fix it later if problems develop. I don't know if there was a short cut to honing those senses that didn't involve a lot of pain as I needed to pick apart and rework oversights.
https://news.ycombinator.com/threads?id=ryguz
I'm sure it's super flexible but the exactly same thing could have been achieved with 8 YAML files and 60% of the content between them would be identical.
I truly believe this comes from devs who want to feel smart by "architecting" solutions to future problems before those problems have become well defined.
So a little bit is always good, and more is sometimes very good -- even memorably good. Together these cause many of us to extrapolate too far, but for understandable reasons.
On #5, I think most people tend to just lean on RDBMS databases for a lot of data access patterns. I think it helps to have some fundamental understandings in terms of where/how/why you can optimize databases as well as where it make sense to consider non-relational (no-sql) databases too. A poorly structured database can crawl under a relatively small number of users.
Dean is saying (implicitly) that you can estimate performance, and therefore you can design for speed a priori - without measuring, and, indeed, before there is anything to measure.
I suspect that both authors would agree that there's a happy medium: you absolutely can and should use your knowledge to design for speed, but given an implementation of a reasonable design, you need measurement to "tune" or improve incrementally.
0: https://gist.github.com/jboner/2841832
But e.g. if you want to do fast math, you really need to design your pipeline around cache efficiency from the beginning – it's very hard to retrofit. Whereas reducing memory allocations in order to make parallel algorithms faster is something you can usually do after profiling.
There are a lot of systems where useless work and other inefficiencies are spread all over the place. Even though I think garbage collection is underrated (e.g. Rustifarians will agree with me in 15 years) it's a good example because of the nonlocality that profilers miss or misunderstand.
You can make great prop bets around "I'll rewrite your Array-of-Structures code to Structure-of-Arrays code and it will get much faster"
https://en.wikipedia.org/wiki/AoS_and_SoA
because SoA usually is much more cache friendly and AoS makes the memory hierarchy perform poorly in a way profilers can't see. The more time somebody spends looking at profilers and more they quote Rule 1 the more they get blindsided by it.
> Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Rule 2 follows rule 1.
Rule 3 & 4 is a variation of Keep it Simple, Stupid (KISS) from the 1960s.
... and... now I feel stupid, because I read the last part, which is summarizing it in the same way.
It's so true, when specing things I always try to focused on DDL because even the UI will fall into place as well, and a place I see claude opus fail as well when building things.
"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)
I'm a big fan of Data Oriented Design. Once you conceptualize how data is stored and transformed in your program, it just has to be reflected in data structures that make it possible.
Modern design approaches tend to focus on choosing a right abstraction like columnar/row layout, caching etc. They mostly fail to optimally work with the data. Optimal in this case meaning getting most of all underlying hardware capabilities, for example reading large and preferably continuous blocks of data from magnetic storage, parallel data processing, keeping intermediate results in CPU caches, utilizing all physical SSD queues.
It's all use-case and priority-specific, and I think the more varied your experience and more tools you have in the tool belt, the better off you can be to bring the right solution to bear. Of course, then you think you have the right solution in mind (lets say using partitions in postgres for something) but you find the ORM your service is using doesn't support it, then what is "best" becomes not only problem-specific but also tool-specific. Finally, even if you have the best solution and your existing ecosystem supports it but the rest of the engineering staff you have is unfamiliar with it, it may again no longer be "best".
this ladder of problem-fit, ecosystem-fit, staffing-fit is something I have grappled with in my career.
LLMs are only so-so at any of the above (even when including the agent as "staff".)
What I should have done was point to Rob's third rule (either in my comment or in the resulting threads)
[0] https://news.ycombinator.com/threads?id=awesome_dude&next=47...
Later that week, now that things were working, I profiled the n^2 search. The software controlled a piece of industrial test equipment, and the actual test process would take something around 4 hours to complete. Using the very worst case, far-beyond-reasonable data set, if I left the n^2 behavior in, would have added something like 6 seconds to that 4 hour runtime.
(Ultimately I fixed it anyways, but because it was easy, not because it mattered.)
https://www.lysator.liu.se/c/pikestyle.html http://www.literateprogramming.com/pikestyle.pdf
I'm a big time leetcode interview hater and reading that felt validating. Why the f*ck am I always asked about algorithms.
This thing never resonated with me.
I often hear it as an excuse to ignore “optimization” at all.
It’s like “broken windows” theory. This allows slop, rot, and technical debt in. And it spreads fast.
Also if everything is unoptimized, this is not what could be easily fixed.
Death of thousand cuts, if you will.