Ask HN: What books should I read to improve as a software engineer?
I'm a recent grad looking to keep improving. I've read Clean Code, Game Programming Patterns, and Architecture Patterns with Python, and I feel I learned / improved a lot from each of them.
I was looking at Coding Horror's book recommendation list: https://blog.codinghorror.com/recommended-reading-for-developers/
but all those books seem very out of date.
Any suggestions for best books for software engineers in 2024?
126 comments
[ 3.6 ms ] story [ 244 ms ] thread- A Philosophy of Software Design by John Ousterhout - Domain-driven Design: Tackling Complexity in the Heart of Software by Eric Evans
They both manage to explain some ideas that are really important, in a very simple way (which makes me feel like an idiot).
EDIT: An old book (also received an update) I'd add is The Psychology of Computer Programming by Weinberg. More descriptive than prescriptive compared to many other books, written in and about the same period as The Mythical Man-Month.
Programming Pearls too would still be worth reading
https://www.amazon.com/Effective-Engineer-Engineering-Dispro...
If you're experienced (senior or higher), it's still a good read, but fewer things will surprise you.
Another one I found very helpful is Designing Data-Intensive Applications by Martin Kleppmann.
Test-Driven Design by Example is a good (and fun) read.
Here some other recomendations
- Designing Data-Intensive Applications - Tidy First?: A Personal Exercise in Empirical Software Design - A Philosophy of Software Design - Refactoring UI - The Software Engineer's Guidebook - https://blog.rstankov.com/programing-books/
Surrounded by Idiots
Meditations
I'm sure a swimmer who reads books about swimming in addition to swimming is better all else being equal than a swimmer who just swims.
Can you show me such a book? Ie a book that teaches one to swim (different strokes etc) aimed at a professional swimmer (athlete).
I think your opinion on OSS/large codebases makes sense, but I don't think it's an either/or situation: reading should _support_ actual coding, similar to how studying a grammar textbook for a foreign language is there to support actually speaking/listening/communicating in that language.
The field has a lot of gurus who trick newcomers into falacies like 'clean code' when, instead, they should be out there banging together rocks like a JavaScript caveman seeking fire (Lisps).
For example, my go to when using a new framework or library that'll be a core part of a project, is to read its documentation in its entirety. I did this with eg Vue. It's a _huge_ time saver, and the big key, is that it reveals things you would never have known existed! They're things which you wouldn't have stumbled on yourself, but which come in handy in certain moments. Or they're things that would waste your time googling, and which might be difficult to understand out of context. The docs also let you understand the "spirit" of the tool you're using, which will let you work much more smoothly with it, and avoid unintentionally fighting with it and how it was designed to do things.
This is also true for more abstract concepts in CS, which might not come up often, but which are smart abstractions that can come into play at key moments in your projects.
Also with math, most math learning is done by books -- books with exercises, yes, but still books.
But at the end of the day, I think what matters is don't get stuck in one mode. Don't _only_ learn from books, since that won't give you the skills you need. But also don't _only_ learn by doing, since then you'll basically be wasting your time re-discovering the last 100 years of programming/CS experience. Do both!
I think an artist in YouTube summarised this best many years ago. He describes 3 ways to improve at something: innate improvement, inspired improvement, and developmental improvement. Innate improvement being by volume--i.e. if you do it every day, you'll get better even if you're not consciously trying to improve. Inspired improvement is using those bursts of inspiration/interest to propel your skills forward. And developmental improvement being the more traditional, sit down with a book and learn/practice approach. All are useful and contribute to improving, so use them all where possible! Jazza recommends making projects that allow for all three possible types of improvement.
Video: https://youtu.be/Bu3ulVhO3z4
So you need some sort of built experience that will allow you to consider those rules critically, and only add them to your toolset if it does seem to make sense for you and you understand why, you have past experience where you think "oh - that would have helped me a lot".
Coders at Work: Reflections on the Craft of Programming
If you're interested in performance, you can read https://algorithmica.org/ and https://people.freebsd.org/~lstewart/articles/cpumemory.pdf
Is there a reason why this is the opinion nowadays?
That being said, some of the advice in the book is simply dated, some of it seems to break code down arbitrarily instead of on responsibility.
On top of this, you get people who pedantically follow it and don't let a team of experienced engineers develop a style that works for them it's really annoying and it can make the codebase a bitch to work on.
In the end, it should be a guide. And you should be thinking about what bits to take. But what ends up happening is "uncle Bob says...".
But sometimes, what you want to do is so complex. You'll want one big file with few methods and a lot of comments.
I think the "perception shift" happened together with public discussions of sqllite.
The tldr would be: just don't consider it a Bible ;)
I spent years writing Ruby and found clean code OK, but preferred Sandi Metz’s writing. Then I started doing some Go and Bill Kennedy made me think about mechanical empathy more. Recently I’ve been diving back into my old haunt of C, particularly more modern C idioms and the C23 standard as I need very fine grained control for latency and memory efficiency reasons for the apps I’m writing (in a very different space to where I worked in Ruby). It’s kind of blown away a lot of the advice from clean code for me.
The thing you’re programming has a CPU with registers. There’s a stack. There’s a heap. The choices you make choose how those things are used by the language implementation you’re using. Languages and methods that try to pretend none of that is happening can lead to very inefficient code that makes everyone sad.
Yes, your code needs to be readable. It has to be maintainable, but the clean code way of doing things doesn’t seem to do that very well AND introduces patterns that can make it hard for compilers and interpreters to optimise.
https://github.com/unclebob/cmuratori-discussion/blob/main/c...
The linked video is also a good watch.
Just for context, this was posted to Github by the author of Clean Code himself, Robert C. Martin, while exchanging messages with Casey Muratory [0]. They talk about performance, about testing, and maybe other stuff (I'm at less than half of it)
[0] of Handmade Hero fame, https://www.youtube.com/watch?v=A2dxjOjWHxQ
Personally I still recommend it to people but with the caveat that they need to think about what aspects from it they adopt and which don't make sense for them in their environment. E.g. if function calls are expensive in the language they use and its compiler doesn't optimize their code by inline the ones it can, then they probably don't want to break things down to the level that Clean Code recommends.
But, even then, other resources are better IMO. (Metz, Ousterhout, Kay, PragProg, language-specific design patterns books, etc.)
The experience of doing things wrong is valuable, but I think you also don't want to get quagmired there. It just depends on how voraciously/quickly you read and seek out better patterns.
(Also, elephant in the room, Uncle Bob has been very vocal and opinionated on the internet about various things and it hasn't endeared many to him because of it)
But it captures it perfectly, a large number of engineers go from jr to intermediate with their new found tricks. Except they cling to these tricks like it’s law, they have upskilled in one direction while sacrificing the open minded pragmatism for dogmatism. They’ll argue with you about these things, it’s in a book how can you not adhere to it!!?
If you can onboard these lessons and still recognise the spots where it’s begging for some best practice but you should break the rule this time, then congrats you’re ahead of the curve, it takes most engineers a frustratingly long time to get pragmatic. If I had a penny for every awful DRY abstraction that became impossible to reason about, I’d have retired.
They don't try to understand the spirit of rules, but blindly apply rules to the letter
You’ll just get angry when writing it. We had written down solutions to problems with teams and offices in 50 years ago but just refused to follow them.
The first are books like Pragmatic Programmer, Mythical Man Month, Code Complete, Design Patterns, talks by Alan Kay on software systems and OOP, even Game Engine Architecture etc. A lot of the content there is distilled experience which is much faster to learn from a book than gain the hard way on the job.
The other books are practical deep dives. Here I would recommend a variety of books like Inside the Machine by Stokes, Computer Architecture by Hennessy, 21st Century C by Klemens, Crafting Interpreters by Nystrom, Compilers by Aho, Effective Java, Nature of Code by Shiffman, Ray Tracing Challenge by Buck, Hands on Hacking by Hickey, and others, depending on your interest and area of focus.
So there's the philosophy of the craft which are mostly the core skills of software engineering (or engineering in general). Problem solving, project planning, resources planning, methodologies,... Then the various theories (computer science). Algorithms, Data Structures, Computer organization, Networking, Databases, Operating Systems,... And then the skills. Programming Languages, Libraries, Tools (editors, shells, build systems, debuggers, test runners). Being a good programmer involves being fairly knowledgeable in all three.
For a good understanding of the basics of architecture: https://www.amazon.com/Web-Application-Architecture-Principl...
It is quite concise (about 190 pages), but in my opinion, it includes all the essential information that the other books in that category would teach you. It leaves out a lot of cruft that over the years turned out to be non-issues (code styling, problems that come with object oriented programming, etc.) but occasionally - when the topic is popular and widespread (e.g. Uncle Bob advice) - addresses them as alternative opinions. It was first published in 2018 and is therefore not in the out-of-date category.
So I would recommend it as essential read, hands down.
If you want something a bit more elaborate: "The Pragmatic Programmer" has a 20th Anniversary Edition. It is a timeless classic, worth reading at any rate.
That being said, I wish there was a single consistent resource[1] that summarizes truly modern software design philosophy in the sense that it leaves the object orientation inspired ideas behind that did not turn out to be useful and focusses on typed functional programming. Maybe with examples in Typescript and Rust.
[1] Possibly a book, but not necessarily. For me it would be important that it presents a consistent opinion, so it should be from a single author or small group of authors. The information I have in mind is mostly there, but spread out over many blog posts from people with slightly different takes and ideas about them. The overwhelming majority of recommendations in this thread are from authors I'd consider part of the founding generation. I'd love to read about software design from the perspective of a younger generation.
Maybe the only book that has actual made me a better programmer that I can objectively measure.
I didn't leave the community because of it. I left it because Ruby was slow as molasses, dynamic typing is a failed experiment (imo) and people would love magic and be proud of it. Which meant systems would break in production more times than I could take and I have done so many on call rotations because someone thought some magic was a fun way of doing it.
For personal projects I used Steel Bank Common Lisp because there the dynamic nature of the language actually has benefits such as programming through the REPL which is much more reliable than typing dynamic code in an editor to build programs.
Once I had to hire people I moved to Go but then again moved to Rust because I want to write programs which do not break, is fast as possible and doesn't take my users' memory and cpu for granted. Who am I to burn their electric bill or data plan while delivering broken buggy software to them. Plus I cannot stand null pointer exceptions and Go due to their ideological drive of remaining "simple" has null pointer exceptions in 2024.
Also the other meta thing I realized was because Rust is harder to get into, the discourse, libraries, tutorials, community is much higher quality compared to anything else I have seen so far so I really enjoy it. Plus Rust has some really cool things like high level maps and functional code while them compiling down to the same Assembly as for loops and other such zero cost abstractions that I like.
I’d agree that I wouldn’t like to support a large Ruby codebase commercially but in team of 1-4 devs and codebase not much larger than 10k lines it’s very productive (numbers pulled from thin air ofc).
I haven't read the original The Pragmatic Programmer, but picked up the 20th Anniversary Edition. Maybe it's because the original was so influential, but I didn't enjoy the 20th Anniversary Edition at all, it just full of boring platitudes.
Not Rust or TS, but i found Java to Kotlin a decent book which provides refactoring of java OOP to more functional patterns. One of the authors Nat Pryce was big into OOP design and was also a co-author of famous book Growing Object Oriented Software by Tests.
It takes time for them. It takes time for us. The small details are ironed out along the way but the big picture, the concept, is defined/designed before.
Note that I really like the books listed up to now, they are not "dependency injection in Typescript using framework x, y or z" kind of books, more general philosophy of coding (with people). Going to this upper level is for me where you go from good to great. And myself, going to my 50s, I am still learning...
[0]: https://m.youtube.com/watch?v=sL96mw1uCmA
I also have a personal recommendation for when you want to better understand testing, QA, or want to/have to work with QA people. [2]
[1] https://pragprog.com/titles/tpp20/the-pragmatic-programmer-2... [2] https://www.amazon.com/Lessons-Learned-Software-Testing-Cont...
I read this years ago and regularly recommend it, especially when people are all over talking about some special queue or database technology. I find so many people don’t understand even how databases work and it’s crucial to application performance and structuring. Adding queues works to relieve server load but if you get swamped by messages you need to make an active decision on how to handle that - and maybe a queue isn’t the best idea if you can’t lose them.
Depending on the tech stack you're interested in, I would also strongly suggest reading foundational books that go deep into that platform (e.g. the Java Language Specification if you're into Java [1]). These are not things you necessarily need to read end-to-end or understand completely, but your global understanding of your chosen platform will expand significantly.
Systems Performance by Brendan Gregg is another useful classic. While it's about performance, it touches upon a huge number of important topics (from memory allocation to pretty graphs), perusing it will make you smarter.
[1] https://docs.oracle.com/javase/specs/