Reading is very important for anyone who wants to be a good software developer. The key, however, is to not just read language manuals, “cookbooks”, and other hands-on texts; but to gain an actual understanding of software development, what makes a good program/function/design/whatnot, what common conceptual errors are, and so on. Regrettably, this is something that most developers, including many senior and chief developers, have neglected: It is not uncommon that someone with ten years of experience lacks insights that I had gained in my first year in the workforce because I made sure to read the right kind of literature from the go. (Note that this kind of prolonged ignorance is not in anyway unique to software development: I have come to consider it the norm, m.m., in executives, managers, PM’s, product managers, and business analysts. I strongly suspect that it is prevalent in other groups too.)
Below, I will (a) give a list of texts covering much of what I consider the “core curriculum”, (b) go into some related topics like how to read, what not to waste time on, what types of problems might be reduced through my recommendations, and similar.
This core curriculum consists of what (in my eyes) all software developers should have an understanding of, and where a beginner might do well to put in intense efforts. Once this material is reasonably covered, further educational efforts can be noticeably less intense; however, they must not stop! Later worthy areas of study include, but are not limited to, software architecture and high level design, algorithms, how hardware works, how databases work, details of various tools and languages, etc.; as well as excursions into relevant non-technical areas.
(This “Later” notwithstanding, some minimum of knowledge should be present earlier too: For instance, it can be a severe deficit to not know what basic collections are available and what their rough algorithmic implications are. However, such knowledge is secondary.)
Note that the current choices are limited to books that I know from personal experience to be very helpful (and that I do not rule out that other books can bring equal value). A side-effect of this is that somewhat newer books are less likely to be included, as my most intense and most basic readings took place in the very late 1990s and very early 2000s. It is very possible that this list will be extended in the future, as I encounter other worthy entries—or as I see a greater need for an understanding of an area not yet covered.
On the off chance that the reader is the author/publisher/proponent of a particular book that is not included: I will be happy to receive a complimentary issue of the work in question. If it also turns out to be of both high quality and high relevance, I will include it. A large cheque is also welcome, but will not affect the chances of inclusion.
Beware that my last reading of some entries was close to ten years prior to the first version of this list, and that some vagueness has often resulted.
Code Completew by Steve McConnell is one of the few good things to come from Microsoft. It is a very thorough introduction to good coding, in particular, and software development, in general—the best single book in the area that I know of. Even in those areas where I do not agree with this work, it still has the benefit of bringing the right kind of thinking to the attention of the reader.
This was not only one of the very first (and best) books that I read on the subject, but also my first pointer to some of the books mentioned below.
The Art of Unix Programmingw by Eric S. Raymond gives very valuable insights into the Unix philosophy, what makes Unix great, how to make good software, etc. Even reading just the few first chapters on philosophy can provide very valuable insights into the right kind of thinking. An absolute must read, even for those who do not intend to do Unix programming, per se. (Although these can obviously skip over some of the more detailed parts.)
Not to be forgotten are its additional benefits for those interested in developing operating systems or in the history of operating systems.
There are several free-of-charge online-editions of this book, including http://www.faqs.org/docs/artu/e and http://www.catb.org/~esr/writings/index.htmle. The latter also contains a number of other works by the same author, some very worthwhile, and links to some interesting texts by other authors.
Programming Pearlsw by John Bentley originated, IIRC, as a series of columns on programming. Correspondingly, it covers a variety of topics in reasonably stand-alone chapters. Here a wealth of insights and anecdotes can be found, in particular relating to practical issues and how to think.
The Mythical Man-Monthw by Fred Brooks is one of the true classics, with a first edition in 1975—my own year of birth. However, because the book focuses on higher level issues, its age is usually of little consequence. It can give a very good understanding of how software projects work, what typical misconceptions and errors tend to accompany software projects, and so on. A central theme is that a man-month is not the same as a man-month—but that other factors, such as individual competence, timing, overall head-count, the need for mentoring, and similar, play in. In an extreme example, one proficient developer can do more in one month than sixty beginners can do in one day—something that, unfortunately, many managers and PM’s do not seem to truly comprehend. (Arguably, the sixty beginners can do more harm in the allotted time, but ...)
This is a book that should be read not just by developers, but by anyone who makes decisions regarding or administrates a software project. (Many of the principles hold for work in other disciplines too.)
Looking back at the above from a 2023 perspective:
The possibly single most important lesson that I have learned through my years in software development, made solid by ever-repeating exposure to developers on various competence levels, is that developers are not fungible.
It is also one of the observations that far too many managers have not made. If this book can help spread that insight, that alone would make it worthwhile.
The Psychology of Computer Programming by Gerald M. Weinberg is another still relevant 1970s book: It has little to do with actual programming, but deals with issues of psychology, sociology, human behaviour in and around computer programming. Computer programming has changed tremendously since then—the people have not.
Design Patternsw by Gamma et al. is a thorough catalog of basic design patterns commonly used in software development (and arguably the beginning of the design pattern craze). This book will not only serve as a good reference and provide some interesting ideas on how to solve problems, but can also provide a good guide on how to think up new solutions/patterns.
Unfortunately, there is a major problem with how this book is applied—and here I must caution very strongly: Many see it as a Bible with the solutions, try to memorize all the patterns (often without truly understanding them), measure the ability of others by their degree of memorization, etc. (A similar caution applies to any book with a similarly religious following.)
The better use is to read it, think about the patterns, draw lessons, and as a consequence learn to build/find appropriate solutions in more generic contexts. The difference in meaning between “facade” and “adapter” is of comparatively little interest: the ability to recognize situations where some kind of wrapper could be beneficial, to consider pros and cons, choose an appropriate variation, ..., is much more important. The details, including example implementations of standard cases, can always be looked up when needed.
Refactoring: Improving the Design of Existing Code by Fowler et al deals (unsurprisingly) with the important topic of refactoring. A variety of techniques are given, as are a number of “bad smells”—heuristic signs that the current code is or will later be problematic. It is particularly useful to try to understand the principles of bad smells, and ideally learn to avoid the corresponding problems in the first place.
In my own case, this book brought comparatively little, as I caught it at a stage when I had already drawn similar lessons/come to similar conclusions based on prior thinking, experiences, and readings (on those points where the book found my approval). This is something to keep in mind when judging sources of ideas: The first source to bring a certain idea to someone’s attention might seem original and valuable; the fifth, hackneyed and pointless. A fair judgment, then, often requires a correction for order of exposure. (And, occasionally, for factors like who-draws-on-whom.)
Edsger W. Dijkstra has written a great number of texts of various lengths. While I have only read a minority, those that I have read have often been thought-worthy and valuable.
In his 1972 ACM Turing Lecture, The Humble Programmere, Dijkstra makes some very interesting points, including the problems from increasing complexity that already existed in 1972.
Of course, one of my own central claims is that complexity is the greatest single problem in modern software development—the situation is far, far worse today.
Here portions of his “EWD series” are published in book form. The relevance varies wildly, up-to-and-including “trip reports”, but the relevant parts can be quite interesting, in particular as they give insights into how he thinks, approaches problems, and similar, while also showing a world of computing that is, in parts, very different from today’s.
Note that the contents are often more academic or mathematical than in most other recommendations, and that it is more the meta-aspect (“how he thinks, [etc]”) than the pure contents that make me include it in the core curriculum. On a first reading, for the current purposes, it is enough to focus on that meta-aspect, while treating the math and whatnot very casually. (Generally, gaining insight into how others, especially the more experienced and/or accomplished, work can be very valuable. One way of doing so is by observing colleagues at work, e.g. during “pair programming”.)
The above deals with what to read. To give a better impression of why to read, a few examples.
A personal example that annoyed me horribly, once I knew better:
For my first master thesis, I needed to write a
semi-complex C program. At one point, I found
myself in a convoluted situation where a goto
statement would be a quick
solution—and in youthful naivete I relished the opportunity to use a
feature of C that I had previously never needed. Shee-eesh! Today, knowing much
better, I would have concluded that if goto
seemed the best option
(it was not), then my code was in severe need of
re-structuring—and I would have proceeded to clean it up.
Note that this refers to C: In some other languages and contexts, goto
might be
the best solution; however, typically because conditionals, loops, and/or
functions are not provided or incomplete—I recall, e.g., my first programming
experiences with C64-Basic, where I not only had to constantly resort to
goto
s and gosub
s, but even had to keep track of actual line
numbers myself...
A lesson from the above is that even someone highly intelligent might have the wrong ideas, if having done too little reading, thinking, experimenting, whatnot in relevant areas—and that what is learned in uni/college is not always helpful.
(I even have the suspicion that the highly intelligent can be more vulnerable in their early days, by getting carried away, by being too set on the “clever” solution, even when it leads to poor maintainability, by overestimating what code others can understand, etc.)
In my case, at the time, I was neither a very experienced hobby programmer nor a computer-science student, but I had spent some programming (as opposed to gaming) time with that C64, had had a minor computer class in high school, had taken at least two relevant college courses, and had been regularly exposed to minor programming tasks elsewhere, e.g. in a course on numerical methods.
The same thesis also gave me one of my first exposures to a poor attitude in others:
In earlier years, a professor had expounded on the benefits of e.g. automatic checks for memory leaks—and I had actually been saved from several bugs by applying, as a matter of course and without having yet noticed any leaks, a checker on previous programs. Writing my thesis as an exchange student at another university, I inquired about a corresponding leak checker. One was provided with a comment of (roughly) “Hope you find your leak! Those buggers can be nasty.”—indicating that the sender only used such tools when he actually had problems with a leak.
This is entirely the wrong attitude: A professional uses any automatic tools to his disposal in a pre-emptive manner, to catch the problem in advance. (Such tools include leak checkers, lint-like tools, bad-smell checkers, checkers for style-guide compliance, and whatever else might be available—and he works with strict compiler settings, takes pride in getting rid of warnings, etc. Courtesy of continuous integration, fortunately, team-wide policies are easy to enforce.)
Other examples are plentiful.
Consider
premature optimization, lack of interest in readability, code duplication,
failure to use constructs (e.g. this
in
Java) that can give the compiler valuable hints, ... (My other writings in
this category often give more information.)
To take another more specific example, I once had a colleague who positively took pride in doing multiplication/division by two with bit-shift operators—in a web-centric and database-heavy Java application in 2002. Firstly, this was premature optimization (and optimization virtually guaranteed to be pointless, at that). Secondly, it severely reduced readability and maintainability through using a trick that far from everyone would be familiar with, that reduced the ability to find examples of division through a text search, that made division by two asymmetric with e.g. division by three, etc.
This also potentially illustrates the need to go with the times and circumstances, as what constitutes premature (or pointless) optimization can vary considerably. It is, for instance, conceivable that the same bit-shifting would been a foreseeably necessary optimization in some arithmetic-heavy 1970s C-program with a 1970s compiler and on some specific set of 1970s hardware.
(Also note an addendum below.)
Knuth’s multi-volume magnum opus is a very valuable work in its own right (at least the one volume that I have read), but deals more with algorithms, mathematics, assembler programming, etc., than with what is relevant for a typical modern software developer. If the former is something that interests you privately, if you develop or research algorithms, do a lot of assembler, look to go from very good to extremely good, ..., then by all means study it.
However, for the average beginner and the core curriculum, this adventure does not provide anywhere near the right cost/benefit ratio to justify a work-related study over the books mentioned above.
Make no mistake: We are not talking “Java in 24 Hours”, but a very long academic work requiring much thinking and problem solving.
But is not every program an algorithm? It is (or at least an implementation or representation of an algorithm); however, there is a difference in what skills and knowledge needs to be developed to write a run-of-the-mill (sub-)program, on the one hand, and e.g. a highly optimized sorting algorithm, on the other.
In particular, for the latter, developers in most languages will have a number of ready made alternatives, which have been very thoroughly tested and tweaked, and which he only very rarely has a valid reason to compete with. What he needs is a very basic understanding of complexities (e.g. the behaviour of an O(Nlog(N)) algorithm vs. an O(N^2) algorithm) and trade-offs , a reference with pros and cons of various algorithms, and a few common-sensical tidbits (file access is slower than memory access, caching can save time, not caching can save memory, swapping is slow/thrashing is snail-pace, ...)—first semester computer science.
Looking back from 2023, not all the above “tidbits” need hold or hold to the degree that they once did, most notably due to the prevalence of SSDs, which have radically different performance characteristics than the old hard drives. Such developments need to be kept in mind both when it comes to staying sharp over time and when first learning the basics.
The priority of a good software developer should be in writing high quality, easy to read, easy to maintain code, creating sensible designs, and so on. Writing sorting algorithms in assembler can actually be detrimental to this purpose—shot-put and high-jump is another combination that can be problematic, even if both are perfectly valid occupations on their own.
Works intended for the masses tend to be very low in understanding, highly over-focused on doing a small range of specific tasks, and generally useless for anything but a superficial overview of whatever the topic. Typical warning signs include names along patterns like “X in 24 hours” (“X in 7 days”, “X for beginners”, etc.), too flashy covers, authors who are journalists, language that over-uses the full stop and the word “you”, and a back-cover that makes promises of ease or speed.
For those looking for a similar coverage: The rubbish taught in these books can typically be learned over the Internet—free of charge. Often noticeably deeper skills can be learned too; in particular, where open-source technologies are concerned.
For those living in Germany: Stay clear of anything from “Data Becker”. This publisher has a decades long record of publishing poorly written, simplistic, and (more often than not) factually faulty books.
A few years after the original text, “Data Becker” met its well-deserved demise. However, as of 2023, chances are that books can still be found in libraries, second-hand stores, and similar sources.
Looking at other publishers, I have the increasing impression that
quality is dropping and dumbing down progressing, implying that ever greater
care must be taken. (Then again, I might
have grown more critical.) As a for instance, I recently (again, 2023)
read/skimmed two books on tmux
, hoping to move beyond the contents of
the man
page and get some idea of advanced use. The first repeatedly
claimed that this-and-that was an advanced topic—and referred to the man
page instead of giving an own treatment. The second concluded with the
congratulatory claim that the reader would now have all the tools needed
to ... read the man
page. Now, the man
page for tmux
is unusually
long and informative, but it is far from book length and what these books did turns
reasonable expectations on their head. (And, no, neither had a name like “... in 24 hours”.
And, yes, a complete beginner would have been better off skipping these books and going straight
to the man
page.)
(Here “read” implies reading for the purpose of becoming a better developer, as opposed to the reading that might be needed for other purposes, notably the daily work.)
A common recommendation is that a developer should read as much code as possible. This is something that I disagree with: The vast majority of all code written is so third-rate that it is likely to have a negative effect on the reader’s own habits. Further, reading code is often boring, which makes it a more “expensive” investment than reading other kinds of information. Further yet, the daily work of a software developer already requires considerable exposure to code, which lowers the value of additional code reading relative the same effort invested in a good and relevant book.
Reading code does have a valuable place, but it must be done correctly. My suggestion is to only rarely read code, but, when it is read, to read it very actively, with a focus on understanding intentions, finding errors, pondering what could be done better, etc. This goes in particular when a problem is detected: Why was this code too slow? Why did this bug happen? Why is this particular function hard to understand? How could it have been done better? Etc. Similarly, if you find something someone else has done that is unexpected, goes down another road than you would have chosen, is unusually elegant, ..., particular attention might be beneficial. (As a bonus, this type of reading can often, and legitimately, be done in the process of daily work, reducing the need for an after-hours effort.)
In short: Quality reading over quantity reading.
The above presumes that the developer has already reached a state where he can read quality code in the language at hand fluently and effortlessly—if not, he should very obviously prioritize that basic skill. (With non-quality code, even a very strong reader can have problems.)
However, code differs from e.g. an entertainment novel in that, once fluency has been reached, further improvements in reading speed will only be of marginal use: It is so much more important to understand what happens and why. A similar case can be made when comparing a math textbook with an entertainment novel, “high literature” with “chick lit”, and similar.
As I noted concerning source code above: Quality reading over quantity reading. This applies equally when it comes to regular texts, including books. Do not, e.g., rush out to buy all the aforementioned recommendations in order to speed read them. Instead, take your time and focus on gaining value from each book.
Never, regardless of topic, read with a mentality leading to a spongelike absorption of someone else’s ideas, opinions, arguments, whatnot. Even when the author seems to be right, more can usually be gained by applying own thought to his words, especially with an eye at factors like why he is right or wrong, what the consequences of something are/would be, what counterarguments and disadvantages might be present but unmentioned, whether something can be applied in another situation than the one given by the author, etc. (Hint: if you agree with everything or almost everything that the author writes, there is a very great chance that you are not thinking hard enough.) When the author seems to be wrong, this applies the more so, but with the additional item of which of the two, reader and author, is wrong. (With sub-issues like whether the reader has understood the author’s argumentation, whether author and reader might be dealing with two different sets of circumstances, each being right in one set and wrong in the other, whether a faulty claim can be rescued by some modification, etc.)
Try to read as much as possible on advocacy for and against this and that, best practices, various design and architecture decisions, etc. The Internet is a good source for such information.
In addition, it makes great sense to read up on the tools and languages that you currently use or expect to use—at least to the point where you know what features are available, what can and can not be done, etc. (The exact details of the “how” can always be looked up later.) This page has a strong focus on bigger-picture issues, and I maintain that tools and languages are less important than these; however, “less important” and “unimportant” are far from synonymous.
Re-visiting this page in 2023, I notice that the recommendations are all in English. This is natural in as far as (a) this is an English page on a predominantly English site, (b) this reflects my own readings. However, it does raise an important issue for non-native speakers—that learning English sufficiently well must be a priority. At least in the current world of software development, the dominance of English in literature, websites, whatnot is strong, and most major programming languages are based on the English language. Not knowing enough English is a serious handicap and one that will close many doors of learning.
(Unfortunately, those with weak or absent English skills are unlikely to read this in the first place, while learning a language takes so long that the advice might come too late.)
What about translations? These can often provide a workaround, but most works remain untranslated and translations do not necessarily receive follow-ups for new editions, tend to lag the originals considerably in date of publication, and often contain errors not present in the originals.
Even when translations are available, I recommend the untranslated version to those who have sufficient mastery of the original language.
What to study in college is a natural companion topic. However, here I have to pass in light of (a) the continual dumbing down of higher education, (b) the great differences from country to country, (c) the still somewhat rudimentary state of related education in my youth when there were degrees in computer science but not in e.g. software development.
However, on a general level, keep in mind that:
The right head and the right attitude are more important than the degree/major/whatnot. (I, e.g., began with a degree in engineering physics.)
It does pay to pick as many relevant courses as possible.
While a degree in computer science gives a very good background for software development, and might still be the most reasonable default for a future software developer, “computer science” and “software development” are not synonymous. (Just like, e.g., “biology” and “medicine” are not.)
No degree for any field of work signifies the end of education.
Then there is the issue of whether college is actually needed or whether e.g. a boot camp might be a better starting point. I am very open to alternative routes, especially in high-fee countries like the U.S., provided that sufficient private time is spent on learning the basic theory in addition to the above.
(The value of specifically boot camps, I cannot judge, but I suspect that the quality can vary wildly and that the value, or lack thereof, of one boot camp says little about the value of any other. The above claim that “No degree for any field of work signifies the end of education.” holds the more strongly for a boot camp, however.)
The following is an automatically generated list of other pages linking to this one. These may or may not contain further content relevant to this topic.
Home
Job application paradoxes
Theoretical and practical knowledge in skill-sheets
Sitemap