A curated list of books for programmers who want to build things that endure. Click any title below to read commentary.
Books
- Douglas Hofstadter — Gödel, Escher, Bach (GEB)
- Harold Abelson and Gerald Jay Sussman - Structure and Interpretation of Computer Programs (SICP)
- Charles Petzold — The Annotated Turing
- Eric S. Raymond — The Cathedral and the Bazaar
Articles/Papers
Why I made this list
I’ve been working with novice and experienced programmers for almost fifteen years. Thousands of students have attended my lectures; I’ve supervised a few dozen more closely. And every so often, someone stands out — a young, curious mind who senses that something’s missing.
They can’t quite name it. They just feel a kind of void — like they’re doing things, solving problems, writing code — but not growing in the way they hoped.
I feel that too. Even after twenty years of professional work, first in electronics, now in protocols and software engineering; that sense never quite disappears. There’s always another layer. Another connection to make. Another system to understand more deeply.
This list is for them; and for the twenty-year-old me. If you feel that same restlessness, that hunger for something more than just the next framework or ticket, I hope this list offers a starting point.
It’s not a checklist. It’s a conversation across time with people who thought deeply and built carefully. You don’t read these books to finish them. You read them to join that tradition.
I hope it serves you well.
Now, truly why I care about this list...
Most software today is built to be forgotten
Written in haste, layered with abstractions, then discarded with the next trend. We build like amnesiacs: few know how the systems beneath them truly work. Fewer care. The result is not progress, but erosion masked as innovation. Like a civilization building towers higher and higher while forgetting how to quarry stone.
Jonathan Blow warned us of this trajectory: a slow technological decline driven not by catastrophe, but by accumulated ignorance. As layers pile on, foundations are lost. One day, the lights will go out—not because no one tried to keep them on, but because no one remembered how.
There is another path.
We can choose to treat software not as a disposable product, but as a form of cultural expression. A craft whose goal is not speed or scale, but clarity, permanence, and meaning.
This is the path of Emacs, Vim, Unix, and many other libraries and systems built and maintained in C — that old, unsafe language. The very same language used to build and maintain most of the internet’s infrastructure. Systems some now call “legacy,” but which are, in truth, civilizational infrastructure.
People say JavaScript is the language of the internet.
It is not.
The true languages of the internet are plain old C and C++, the very languages no one wants to teach or learn anymore. Sorry to say, but JavaScript is just a rushed wrapper over decades of C and C++. It can do some cool, good-looking stuff, because the marvel that is a modern web browser; guess what, written in C and C++. But we can’t count on it to keep the edifice from falling apart.
Don’t get me wrong — we need those things too. A Ferrari isn’t a Ferrari without leather seats and polished interiors. But no one dreams of owning one because of the leather. They dream of the engine, the pristine mechanics, the architecture that makes performance possible, and the artisanship of a machine built by engineers and technicians who are also craftsmen.
These things have not survived in spite of their age. They have survided because some people took the time to develop rare and deliberate skills. They have survived because these people want to transcend their own lives by doing timeless artifacts.
To walk this path requires more than technical competence. It requires erudition.
Erudition is not mere knowledge—it is the discipline of memory. It is the refusal to discard what came before simply because it is old. It is what separates the craftsman from the technician. The former builds tools; the latter uses them.
This is not a call for nostalgia. It is a call for depth.
Edsger Dijkstra argued that software should be beautiful, that clarity and elegance are moral virtues in code. Richard Stallman, despite his politics, glimpsed something similar: software, if well written and liberated from arbitrary dependencies, becomes free from its creators. It becomes self-sufficient, a thing that exists on its own terms.
To write such software is not merely engineering. It is a form of authorship.
And yet, why bother?
Economics can’t fully explain open source. Eric Raymond got close. Yes, incentives matter, but the real motivation is harder to measure. It’s not just reputation or signaling. It’s something older, deeper. The pride of the artisan. The drive to leave something behind. The desire to see one's soul reflected in a functioning machine.
We don't need more developers. We need stewards of tradition. Programmers who see themselves as participants in a conversation older than their tools. Programmers who understand that code is not just meant to work, but to last.
On Education and the Cult of Utility
Our institutions are not preparing the next generation to build what endures. I know that very well, I'm a professor at a major university in Brazil. And truth be told, I'm probably more part of the problem than the solution.
Schools are increasingly pressured into “project-based,” “real-world,” “hands-on” pedagogies—buzzwords that too often mean building whatever solves the most immediate problem, then moving on. This is the educational branch of the same Silicon Valley rot: fail fast, fail often, ship it, forget it.
At the same time, theory-driven programs—those that ground students in algorithms, computation, and systems—are dismissed as outmoded. Why study SMTP when you can deploy a serverless app in five minutes? Why implement a shell when you can call one? Why understand when you can use?
This is a catastrophe disguised as relevance.
Real-world problems aren’t just the ones we face now—they are the ones we’ve already solved. The wheel, once invented, is worth studying. But we rarely ask students to reimplement anything that matters. How many have written a shell? A CLI email client? A toy compiler? A browser? An operating system? These are exercises not in nostalgia, but in transmission—how to pass down the thinking behind software that actually lasts.
Instead, we hand students tools as if they were gifts from God.
But God does not maintain software. That’s up to us.
But wait, Edil — what do you mean by us? Where’s the timeless code you’ve written yourself? You might ask me that. And it hits me hard. I truly envy some of the programmers I’ve worked with. When I watch them do their craft, I wish I were doing that too.
But I’ve also come to realize that, almost as important as having the artists working, is inspiring and guiding the next generation of artists. I believe I could be building good software. But I also believe I’m most valuable when I help build good people.
I hope I’m doing that well enough. But only time will tell. And I don’t care if nobody sees it. I’m trying to shape people in a tradition I believe leads to real progress, not just good-looking frontends. If two or three of them end up helping to build something truly foundational — something that lasts — that’s probably more than I could have done myself.
On AI and the Seduction of Vibe Coding
The recent obsession with large language models has amplified the very disease we aim to treat. It is now fashionable to "vibe code"; to prompt a machine, skim the output, and paste until it runs. It feels like programming. It looks like productivity. But it is neither.
Vibe coding is just the latest form of disposable software creation. It encourages the illusion of mastery while hollowing out the discipline itself. You are not becoming a better developer by prompting until it compiles. You are simply becoming a more efficient operator of a glorified autocomplete engine.
There is nothing wrong with using LLMs, when used with intentionality and craft. For the erudite programmer, these models are like sharpened chef’s knives: amazing tools for thought that can accelerate judgment, precision, and depth. But in unskilled hands, they do not produce better software. They produce more software. Worse software. Cheap, flimsy, mass-produced code—the Chinese plastic of the digital world.
And worst of all: we are deceiving ourselves. We confuse activity with learning. We conflate output with experience. We mistake convenience for growth.
Like junk food for the mind, we’re intoxicating ourselves with crap software. It fills you up, but it doesn’t nourish. And over time, it rots your taste, until you can’t even tell the difference between a fine wine and vinegar.
This is why we read. Not to memorize syntax, but to train our taste. Not to chase novelty, but to understand roots. Not to master tools, but to become worthy of building them.
Let others chase the ephemeral. We are here to build what endures.
Why I Care About This List
Because I remember, every single day, what it feels like to be lost in the noise. To want to become a great engineer, but not know where to look. To feel that something important is missing, some deeper foundation, and yet be handed only tools, frameworks, and deadlines. And to feel lonely (and a little afraid that I'm crazy) because no one around seems to feel the same.
This list is my attempt to push back against that. To offer a path. A tradition. Not a curriculum. Not a checklist. Just a thread you can pull — if you're ready to go deeper.
This is a way to start a conversation with the great minds who came before us. Because great people have always existed. Because brilliant thinkers lost sleep over the problems we now treat as trivial. Because they offered solutions in a time of scarcity, when computing was expensive, memory was counted in kilobytes, and mistakes had consequences. They had no resources to waste like we do now.
I’ve read every reference in this list — some of them many times. I’ve read far more than what’s here, but these are the books that made me think. These are the ones I wish I had started with, twenty years ago. No, these are the ones I wish I had read in spite of all others. I can’t go back in time. But maybe I can help someone else avoid the fast-food books and disposable references.
Learning is not a linear process. Other than GEB and SICP, I don’t think there’s a right order to begin digesting these works. Embrace the chaos. Read these books in any order. Study them while you work your day job. Keep your mind’s eyes sharp and open and over time, you’ll begin to see where the pieces connect, and how to create better artifacts. Not by doing it right the first time, but by iterating. By refining. By caring.
Finally, I care because I’ve seen what happens when a curious student finds the right book or the right questions at the right time. It changes everything. Sometimes, all someone needs is a door left slightly open, and a hint that there’s more on the other side.
Douglas Hofstadter; Gödel, Escher, Bach (GEB)
If you’re going to read only one book from this list, make it this one.
It’s not a book about software development per se. The central question it explores is: how can inanimate systems become animate? Or put differently: how can self-awareness emerge from formal, mechanical rules?
The thesis is deeply relevant to the way we think as programmers. Formal systems are the atoms of computer science. Every programming language is a formal language; every computer implements a formal system. And yet, Hofstadter shows how these rigid systems can give rise to self-reference, recursion, and ultimately, meaning.
This book will stretch your intuition. It uses computers not just as tools, but as metaphors. It draws analogies between symbols in logic, notes in music, and patterns in biology. It connects Gödel’s incompleteness theorems to Bach’s fugues and Escher’s paradoxes, all in the service of understanding what it means for something to think.
To understand computing deeply is not to build apps or automate workflows. It is to see the world differently. Gödel, Escher, Bach offers a lens; one that most people will refuse to wear, but that will permanently rewire how you think if you do.
Harold Abelson and Gerald Jay Sussman - Structure and Interpretation of Computer Programs (SICP)
This is the best book on computer engineering.
It’s old. It uses LISP, a language many today consider dead or academic. So yes, it feels old-fashioned. But its content? Timeless.
SICP teaches you how to think about abstractions, the cornerstone of software engineering. It builds everything from first principles, using elegant code examples and exercises that still challenge experienced developers today.
If you’re used to only listening to hip-hop, the symphony that is this book might sound boring, maybe even useless. But every rock and pop star studied classical music. Not to write fugues, but to understand how good ideas are put together to make art.
You don’t read SICP to learn a language. You study it to learn how to build anything — in any language that matters today.
You can also watch the MIT lectures on their OpenCourseWare platform. There are also excellent commentaries on the code_report youtube channel.
Charles Petzold — The Annotated Turing
Ah, Alan Turing — the founding father of computer science.
He wasn’t trying to build machines or start a revolution. He was trying to solve a fundamental problem in mathematics: what can or cannot be proved? His 1936 paper on the Entscheidungsproblem (decision problem) is to computer science what Einstein’s first paper on relativity is to physics: a new kind of foundation we now take for granted.
Turing did the unthinkable. He defined what a computer is, before computers existed. In his time, a “computer” meant a person who carried out calculations by hand.
And yet, despite the influence of his work, I’d wager 99% of programmers and computer scientists have never read this paper.
It’s not that hard. The notation is strange, sure; we’ve had nearly a century to clean that up. But the ideas are astonishing. This is a human being formalizing the limits of reason, the boundary between mechanical logic and intuition. It’s philosophy in math’s clothing.
Charles Petzold’s book walks you through Turing’s original paper, line by line, with commentary and context. It’s like sitting beside Turing at the blackboard — with a patient guide explaining what he was trying to do and why it mattered.
If you call yourself a computer scientist and haven’t read this yet, consider this your invitation. Or your reckoning.
Eric S. Raymond — The Cathedral and the Bazaar
“Writing software is about people, not about computers.”
That’s something I say often, especially when someone comes to me eager to learn how to program.
On one hand, writing software is about communicating an idea. But it’s also about solving a problem; and problems are something people have, not computers. Computers just sit there. It’s people who use them as tools.
This book is a collection of essays about how software is developed by people. Most programmers experience the Cathedral model: a highly organized, top-down structure, usually a company, building a product in a planned, centralized way.
And yet, those same programmers are relying on dozens (or hundreds) of libraries and tools built under an entirely different model: open source.
Eric Raymond’s essays reflect on the Bazaar model — chaotic, decentralized, voluntary collaboration. Why do people do it? What are the unwritten rules of etiquette? And how can a supposedly disorganized mess of programmers working after hours — unpaid — somehow build the most reliable and fundamental parts of the internet?
Read this to think about the sociology of software. Because if these people ever stop doing what they do, we may well regress into the digital stone age.
John Backus — Can Programming Be Liberated from the von Neumann Style?
https://doi.org/10.1145/359576.359579
John Backus was an engineer, not an academic. He built Fortran — the first truly useful high-level language. So useful, in fact, that we maintain some of the best linear algebra libraries still in use today in Fortran, forming the numerical backbone of our so-called AI era.
Fortran is an imperative language — a style that mirrors how computers work. Each line is a command, compiled into more primitive machine instructions. And that’s still how most mainstream languages work today.
This paper is the transcript of Backus’s Turing Award lecture (the closest thing to a Nobel in computer science). He used the stage not to celebrate his creation, but to question it. To say: “I helped build this, it shaped the field… but I think we’ve gone astray.”
Backus observed that we design programming languages primarily to extract performance from imperative machines. And yes — software does have to run on computers. But programming languages are, above all, languages. People use them to express ideas.
And language shapes thought. The way we write code constrains how we reason about it — and even what we can imagine.
In this paper, Backus argues that we should design languages around how humans think, not how computers execute. The computer is the tool. It’s the machine that should adapt to us — not our minds to its architecture.