Books I recommend to my software engineering students
web.eecs.utk.edu
web.eecs.utk.edu
I'm going to try an experiment, posting five books I'd put on the list, one per comment. Downvote, upvote, add your own if you like the idea.
Fisher's Fundamental Theorem states—in terms appropriate to
the present context—that the better adapted a system is to a
particular environment, the less adaptable it is to new
environments.
The theme of that quote kept coming up, I presume deliberately, in many of the later chapters.[0] https://en.wikipedia.org/wiki/Fisher%27s_fundamental_theorem...
I think it's good that it introduces the overall concept of Design patterns, but the book itself isn't really about the general concept of Design patterns.
The book is focused on OOP techniques and tricks. It makes the assumption that OOP is the most general way to modularize things and think about computation and design.
Additionally a lot of the "patterns" aren't actually good, they're actually really bad. Many of the patterns introduced by this book actually increase complexity of a program while giving only an illusory sense of increased modularity and reuse-ability.
Perhaps one needs to have been in industry before and after this book to appreciate how it introduced a common vocabulary, a mechanism for documenting and sharing experience, and a framework for thinking about common problems.
By introduced I mean, introduced to the world for the first time. Invented is debatable concept as in was math invented or discovered? I avoid that debate by using the word "introduced."
>Perhaps one needs to have been in industry before and after this book to appreciate how it introduced a common vocabulary, a mechanism for documenting and sharing experience, and a framework for thinking about common problems.
Humans invent vocabulary for everything that they do, giving patterns names is inevitable. I would hardly call it revolutionary. This book just gave the concept of naming patterns a meta name in itself: "Design patterns." I think it's good that this was given a name but its importance is largely exaggerated.
Case in point: Other disciplines of programming don't use the term "Design patterns" or formal pattern names that frequently. In fact, technically, there are tons and tons of "Design patterns" in procedural programming, declarative programming and functional programming, yet experts in these respective fields don't feel the need to give these concepts over-inflated and complicated nomenclature.
Take currying for example. Functional programmers don't call it the "Curry pattern" nor do they refer to it as a design pattern (even though it technically is). A name just arose naturally. No need to give the concept of naming concepts another word.
To take the illustration even further, imagine driving techniques. Should I give the concept of naming driving techniques some name to exaggerate its importance? Perhaps I can, I'll call it: "Vehicular Translation patterns." And instead of talking about cars using regular English like a normal person I'll just communicate like this: "Execute the drift pattern in composition with the slide pattern and inverse throttle pattern to maneuver through that turn." Talking like this helps me sound smarter while obfuscating communication to the point where it can only be understood by a select few. The book design patterns introduced to the programming world what could potentially be done for driving as well. Needless to say, if it's pointless to do this for driving then does that mean it's pointless to do for programming? In my opinion the answer to that question is, "Yes."
In short, use english for most concepts and name things only where it matters and as it comes naturally. No need to invent an entire discipline and inflate it with made up epistemology.
I realize race car driving does have it's own nomenclature but it's not over used to the extent of design patterns nor did they feel the need to give the nomenclature it's own nomenclature (vehicle translation patterns, VTS theory for short)
Or I could just call it english vocabulary within the field of mathematics.
Every single thing you said could be done without the usage of the word "design patterns" and is already done without meta awareness of itself in all fields of engineering, science and anything.
If I were to build a house is there not a language for "architectures, modules, and interfaces" used to make houses more efficient and maintainable? Also it would be (in a way creating vocabulary upon a specific domain)
The difference is for physical architecture it's missing the pompous self importance. It does not need a name for itself. Imagine if it was called "Structural Language Patterns," and every concepts was suffixed with the word "Structual pattern."
You will note "design patterns" is actually derived from physical architecture but physical architecture does not go overboard and give itself a name other than "English Vocabulary" and attempt to communicate with big words where English will make more sense.
What I think is missing is the idea that design patters are often language smell. They're common patterns to compensate for deficiencies in the language. The GoF is most applicable to C++ code that uses a lot of OOP. Like the previous reply said, reaching for these patterns too quickly complicates things. Like using unnecessarily large words when simpler language would suffice.
Source: started design patterns study group, still active today.
But older me now understands every generation goes thru a phase where they think they newly discovered sex.
When I asked my then teenaged son the difference between "emo" and "goth", he informed me that "goth" is for old people.
English is shared vocabulary. What is the point of "design patterns" when you can already define a word for a pattern in english.
My argument is design patterns is 100% the abuse of words.
Given a concept, I name that concept "Gloop" and make it part of a defined word in the "English Language."
Given a concept, I name that concept "Gloop pattern" and make it part of a defined word in "Design patterns."
Both actions have the potential to fulfill either goal you describe above depending on the definition(s) of gloop and "gloop pattern."Independent of the definitions or assuming both mean the same thing, the two actions are one in the same. There is no benefit of using one technique over the other.
Let's give you another angle: Defining a term under the umbrella of "Design patterns" doesn't make that definition any more precise than if you defined that term under the umbrella of the english language. There is no difference period.
Design patterns is shared vocabulary. English is also shared vocabulary. But the words "Design Patterns" is part of the "English Language." By creating the term "Design patterns" in the "English Language" you are essentially recursively adding complexity to the english language by defining a redundant concept in the same concept.
Just use english. Define the pattern in english, there is no need to define the pattern in "Design patterns."
Additionally, many patterns are better described with existing english words. Why use Facade pattern, when you can just say Object wrapper. The word "Design patterns" inserts a sort of false formalism and elitism into what is essentially just creating new vocabulary in the english language. The claim I'm making in this paragraph is that while yes it's good to have some formal nomenclature, it's excessive to give all of these patterns their own names. Let the naming and the definitions flow naturally.
Other fields of programming have patterns but they don't try to turn these patterns into some kind of theoretical field with it's own nomenclature. Currying is just currying nobody calls it the "Curry pattern". Recursion is just recursion, nobody calls it the "Recursion pattern."
My personal position on the value of the art of namings, eg Facade, Proxy, Wrapper, Adapter, was somehow capturing the author's original intent. As distinct from the implementation. How it's meant to be used.
Lofty sentiment coming from someone condemned to decades of code maintenance.
Professionally, methinks design patterns, and their misuse, has been mostly detrimental. Maybe because the notions were taken too literally, treated prescriptively rather than descriptively.
Calling everything a Decorator. When it's actually a Chain of Command. And when you try to patiently explain the evils of silent failures, buried deep in the levels of indirection, to the "senior architect" author, that same architect insults your intelligence and walks off.
The mere utterance of Factory and Singleton in public somehow empowering legions of noobs littering entire organizations (and libraries) with innumerable implementations.
Trying to debug something called a "Write thru Cache" when its anything but.
Etc.
E.g. explaining an interesting or significant software problem and it's surrounding environment from each decade for the last 7 or so decades.
Also detailed histories of the devdlopment of large and complex software needs to be written and studied, but I just don't see it happening.
It's available entirely online here http://www.ankn.uaf.edu/curriculum/AxeHandleAcademy/rc/50pat...
It was written in 1986 by the linguist Ron Scollon and his wife Suzie.
I found a paper copy on a free-book shelf outside a thrift store, and consider myself blessed to have stumbled upon it.
This seems to be a common falsehood propagated by Agile consultants and swallowed whole by an industry that doesn't know any better.
It doesn't look like the waterfall model was ever used on a significant scale; not as far as I remember, anyway.
Well - yes, it was used, and still is: it's the default if you're not careful or if you don't think very hard or realistically, or are very naive. The confusion is that is was only given a name to disparage it: W. Winston Royce observed that the way most people managed software projects was completely unrealistic and didn't take into account changing requirements. He called it "waterfall" as a way to underscore how inflexible the default "write down all the requirements, then write down how long they're going to take, then do them in that amount of time" approach was.
Unfortunately, most people who adopt what they refer to as "agile" processes are still stuck in that same mindset; they think that, by having daily standups, putting in JIRA tickets, and referring to every two weeks as a "sprint", they'll somehow meet the project manager's pipe dream of 100% predictable software development schedules.
- Feedback loops (because problems or deficiencies will arise)
- Involving the customer (because you don't want to spend 12-60 months building the wrong thing)
- Build a prototype (good advice)
- Document, document, document (he thinks 1500 pages is a good target)
The only thing practitioners seem to have taken away is that last bullet. He still made a strong distinction in his model between analysis, design, and implementation. Though, to be fair, at the time "programmer" was more of a technician level and design often involved making detailed designs like flowcharts and such that could be more easily translated into code. These days, design and implementation are really tangled up, and the documentation is awful an non-actionable (I've been on those teams, it's nightmare inducing). People think prose can replace a diagram, so they fill out their 1500 page quota with lots of words but no clarity.
The name came later, and was enshrined in a DOD standard by people who couldn't read past the first few pages. So they entirely missed the lessons learned he was trying to apply to it.
I think books on software management and process are mostly lost on someone who hasn't been involved in the process. (Hell many of those books are lost on project managers)
I think the most important subjects to teach students are javascript, css, & sql (or mongo). Before you teach a beginner wood worker the "Zen of wood" you teach them how to cut a piece of wood without sawing off a finger.
The rest will come later.
I agree that you should teach the practice. My ideal education would be co-ops combined with a reading course or two on the canon.
I think that's true and fair. However, once some experience has been acquired, I believe it's helpful to evaluate whether it's good experience or bad experience. Comparisons against the literature can be helpful at that point. Case in point: I tried reading 'The Psychology of Computer Programming' in college, but it didn't make much sense to me. Coming to it 15 years later, I realize it'd have been the perfect companion to my first five years in industry, naming and describing problems I'd faced and solutions that only came through experience more painful than reading.
Yeah, that pretty much describes all of software development in one stroke. But you should read it anyway (if only to experience the feeling of someone predicting your future before (most of) you were born).
So, adding new bodies does incur cost, but the belief that always adding new bodies always adds more cost than adds impetus to the outcome is not always true.
The classic example is "the hump" which was the cost in fuel terms to ship fuel to China, to be able to fly from China to bomb Japan. Economically ruinous, people forget that missions were nonetheless successfully conducted: it was ruinous but the outcome was achieved within limits.
So the MMM does not say "never add a body" it says "know what your incurring in overhead, choosing to add a body"
Unlike in war, the marginal curve of adding an extra body goes negative in software. And right quick unless circumstances are favorable.
(If you doubt this, imagine a thousand Arthurs charged with implementing a CRUD app.)
First is that adding people slows things down in the short term.
The second is that due to overhead, adding people won't give linear speed up in completion time.
And third, adding people won't help a project that is linear in nature. The common example is using 9 women to get a baby in 1 month.
My years suggest that he wasn't nearly pessimistic enough.
Now, you might say, "I will just bench those other 27 people" and have them play cards or something. In most organizations, though, you cannot do this. Instead, they must be seen to be useful somehow. And if you have 30 people with their hands in a three-person project, no matter how peripherally, failure is all but guaranteed.
I have seen this many times.
Last time I worked at a big company, there was lots of dead wood, but one guy in particular was just a massive liability. Nice guy, tried hard, but utterly incompetent, and (I think there is a term for this?) he was unaware / ignorant of how incompetent he was. He would often check in code that would break the build (team of 300 engineers, large telecom system), he would write and run scripts that would bring computing clusters to their knees (this was late 90s, there are probably ways to mitigate that now), he would consume lots of high-quality talent's time with basic questions, etc.
I literally asked my manager if we could pay Leo to sit home and play video games. OF course, as you said, everyone's gotta look busy.
Here's the punchline: I learned a new term (to me, at the time... not sure I've heard it again) from a greybeard/wizard there -- this guy was a genius. He had a very appropriate term for Leo: negative producer.
Bingo!
We started a project 5 years ago, after a few months of failed attempts. From the very onset of the project, we tried to adhere to the key lessons of the book. Examples are: recognizing the importance of minimizing communication overhead (the most important assets are not people but time), following the surgical model (key decisions should be made by a single individual), practicing effect-free programming whenever possible, allocating enough time for testing, and so on. I would definitely attribute the success of our project to the teachings of the MMM.
Like many books on software engineering (and self-help books in general), just reading a book and learning its contents may not make any difference in practice. Only when you seriously make conscious efforts to practice its teachings do you realize what the book is really about. This is also the reason why many university courses on software engineering are boring.
- Antifragile: Things That Gain from Disorder by Nassim Nicholas Taleb[1]
- Skunk Works: A Personal Memoir of My Years at Lockheed by Ben R. Rich[2]
[1] https://www.goodreads.com/book/show/13530973-antifragile
And of course The Elements of Programming Style is a classic.. The lessons seem like clichés now, but there was a time when things that seem obvious now were hotly debated.
https://en.wikipedia.org/wiki/The_Elements_of_Programming_St...
Code and Other Laws of Cyberspace
and
Free Software Free Society: Selected Essays of Richard M. Stallman
https://shop.fsf.org/books-docs/free-software-free-society-s...
I would recommend watching this talk [0] from Kevlin Henney to anyone who likes that book.
"For lack of a bibliography, I offer neither explanation nor apology."
I've been teaching a software engineering class for a few years. Being conscious of the high cost of textbooks, I find that Applying UML and Patterns by Larman achieves the best balance of practice, design, and process.
For students who want to dig into agile methodologies I would recommend Extreme Programming Explained by Beck and the Poppendiecks on Lean.
In my opinion, a philosophical grounding is helpful for the programmer. Accordingly, I'd want to suggest something like Locke's Essay Concerning Human Understanding or a secondary source on Aristotelian categories. Moreover, a broad knowledge of different types of ethical systems will benefit a developer.
Lastly, as a kind of curve ball, I would recommend any STEM student to work through an LSAT preparation book. Technologies will come and go, but, throughout a career, the problems a developer encounters will likely have dimensions that require general critical thinking.
Large software systems fail continuously in countless ways all the time. Google search will still throw 404 or 5xx occasionally. We're not building airplanes that either fly or crash (although even the software running airplanes is now increasingly fault tolerant). We are effectively getting from point A to B through a variety of logical operators, increasing the set of valid A through trial and error. We find an input that throws error, our theorem was faulty and we update our proof. A seemingly valid input can't produce the results we want, we double check to make sure our proof is still valid. Dilettante engineers will begin to introduce unbound complexity at this point for making fixes without understanding root causes.
For the above reasons, we should abandon any attempt at comprehensive software engineering curriculum or canons. We are not architecting a skyscraper to be delegated to a construction company to be built one and done for all of time. We are continually exploring and staking out a specific problem space. Our greatest compass in these new journeys will always be foundational computer science. We must take the specific and make it general. Careful application of elementary data structures, algorithms and discrete analysis will move and has moved us continents further than any convention to "best practices" ever will.
In writing a proof, you provide an argument that justifies some claim is true. Your argument has to be airtight, so that no matter what kind of counter example or ugly scenario is proposed, your argument still holds.
The same seems true for an airplane. It needs to do its job (fly) and do so on the face of a myriad of different external factors. If you build a plane, it should "never" fail.
Software on the other hand, can be built without thinking about ALL of these edge cases. Obvious it's better if it always works, but it's usually okay if there is some bizarre scenario that causes an error. For some pieces of software it might even be okay if these errors never get fixed (ie: a very small number of users ever experience them). Software can work, even when it (kinda) doesn't.
The Curry-Howard correspondence might be technically true, but we're just as bad at writing specifications for programs as we are at writing the programs themselves. It could be good to acknowledge that and just write the software that solves most, but maybe not all, of our problems.
The irony is airplanes live in a unideal universe where no theory can predict anything to a perfect degree and testing must take precedence. However when building an airplane, aerospace engineers use far more theory than software engineers even when testing and engineering best practices is an absolute requirement for the unpredictable nature of real world physics acting on an airplane.
I feel the reason why the world is the way it is is due to necessity. A buggy program is (usually) an annoyance. A buggy airplane is (always) a disaster. It is necessary to prevent disasters but it is not necessary to eliminate an annoyance.
Maybe "How to read a book" or "The Alchemist" or I don't know, something that's not just so totally typical.
:)
He's written a lot of bestselling pop social science books. IIRC, he's a good storyteller, but is criticized for cherry-picking stuff to create the impression that you've just learned something profound and counter-intuitive when maybe you actually haven't.
Here's a review of one that makes that point: https://www.newstatesman.com/2013/10/malcolm-gladwell-backla...
Disclaimer, I actually enjoy his books.
Absolutely agree with history being useful, though. Any particular periods of history you think are essential to know about, and any particular great books on them?
History has many themes. These appear in every time and place, sometimes in the forefront, sometimes in the background. I believe the most "essential" period is the one that answers your questions.
So, I'd recommend picking any period that you have a vague curiosity for. I'd go a half step further and recommend avoiding recent periods (late 20th Century).
IMO, it's too recent for there to be consensus on what constitutes good scholarship. There are obviously exceptions to this, but if you're new to history reading, it's difficult to disambiguate the good from the bad.
Examples of periods and geographies that are particularly well-studied, with good accessible literature:
- Late antiquity in the mediterranean (fall of the roman empire)
- Inter-war period in continental europe (Weimar, etc)
- Revolutionary period in France, United States
- Antebellum period in the United States
- Early Russian Revolution (there aren't many good syntheses imo, because this period was incredibly complicated)
- Europe during the reign of Louis XIV (1643-1715)
- Napoleonic wars and aftermath
This list is pretty euro-centric. IMO, these are the safest place to start, as the plurality of english-language scholarship is in these places and periods. After developing a good nose here, you'll feel comfortable reading in areas where the scholarship isn't as deep.
(Side-note: I usually multitask a number of books and many months can pass until reading resumes. Yes, it's weird, and yes, if someone has a nice trick for this, please help, I'm running out of bookmarks)
By analogy, creativity, perhaps even reality distortion. I don't think we should solely rely on non-fiction and proven methods to teach or grow. Indeed, if we did that, we wouldn't grow at all.
I so enjoyed the pragmatism of Crockford's "Javascript: the good parts" that I gave it away for a while too.
Sometimes I would print out great tracts like "big ball of mud" and leave them at appropriate places in SF.
Eventually I realized I was either preaching to the converted or preaching to the wilderness. So I stopped. But you've inspired me to reconsider anonymous viral software evangelism, at least for a brief moment.
That is likely the best complement I've gotten this week.
Outliers is kind of entrepreneurship junk food and 10,000 hours has already been debunked.
I would also say the lessons around keeping teams small have been taken to heart in industry somewhat, e.g. in the notion of the two-pizza team at Amazon.
That said, some of Brooks' suggestions, such as the "surgical team" concept, are more products of their era and haven't aged well.
Design of Everyday Things Founders at Work Antifragile
I legit think that there is only one way of engineering complicated software and that is Entity-Component-System (https://en.wikipedia.org/wiki/Entity_component_system).