Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: What books had the greatest effect on how you structure your code?
332 points by rufugee on July 24, 2017 | hide | past | favorite | 158 comments
It'd be interesting to know what books have had the greatest effect on how you structure your code and projects?

Code Complete, Steve McConnell. http://www.stevemcconnell.com/cc.htm

One of the best books on programming style and function, backed up with actual research for the recommendations.

Same opinion. I always recommend it to my programming students.

Also, The Pragmatic Programmer is good, for topics both about programming and beyond programming per se.

Ditto for both books, though I prefer The Effective Engineer to The Pragmatic Programmer. I found that quite a bit of the tactical advice in TPP is already standard practice (e.g. "always use source control"). Comparatively, TEE has more of an emphasis on project planning and team/company health, and I found that more useful for my work.

Thanks for the tip.

A lot if the advice in these books has become widely accepted, so if you might not find that much new in them if you've already worked at a tech company for a bit.

True, but it's likely some junior folks will be reading this thread, so it was worth mentioning, IMO.

I second this. I read this book in my teens after I had taught myself programming and just been hacking away for a little while. It had never occurred to me to even think about the issues this book was discussing. It really made me think about and appreciate design quality in software.


"Patterns of Enterprise Application Architecture" by Martin Fowler made a big difference for me at the time. Also, "Domain Driven Design" by Eric Evans. Both have a focus on high level architecture.

Of books that are more on the craft of programming, "Refactoring" (Also Fowler) is good. And I enjoyed "Practical Common Lisp" by Peter Seibel too.

In general, I suspect that the value of a book has more to do with where the reader is, than where the book is.

Thinking Forth; Software Tools; SICP; Abstraction and Specification in Program Development; Essentials of Programming Languages; Paradigms of AI Programming; I'd like to list something about OO too, but no book I know really measures up to learning from other programmers. But there's Mark S. Miller's thesis Robust Composition.

(in roughly chronological order. As you can see from the chronology, it took me a long time to start to like OOP.)

Here I was thinking, "I was never really influenced by books. I just read and wrote a lot of code." But you've reminded me that it's just wrong. Thinking Forth. Nearly 35 years later it still probably dominates my approach.

Edit: Just realised that it is available under a CC license: http://thinking-forth.sourceforge.net/

>Thinking Forth. Nearly 35 years later it still probably dominates my approach.

That's interesting. Can you elaborate on why you think it dominates your approach? I'm interested because I had read a good amount of Starting Forth by Leo Brodie some years ago, and liked the language, and had played around with it a bit (only got access to a Forth system for a short time, so could not go deeply into it, on the practical side). But I remember liking the language. I don't think I have read Thinking Forth.

For me the biggest influences underemphasized in other sources are to be alert for relentless simplification, for 'mechanical sympathy', for ways changing your problem could make it much simpler to solve -- and that there's an agility in using a simpler programming system like a Forth which you grok and can change as you need to, which can sometimes outweigh the leverage of a big one that comes with lots of stuff already done for you. These are useful points of view even with Forth being almost never the most practical tool now. Its day was already passing when I learned it in the 80s.

Also, though I don't think this was quite in the book, it helped lead me to the idea of refactoring and its synergy with automated testing almost a decade before the idea started spreading.

Yes, this is pretty much the same as me. The focus of Thinking Forth (as much as I remember it -- I should re-read it now that I can download it) is about creating small words that you can compose. The idea was that you could interactively tests the words as you were writing the code. FORTH is a bit like Smalltalk in that the environment is always loaded, so you're always jumping in and out of the editor to try things.

Later when I learned about refactoring and TDD, it felt really natural. Until now I hadn't made the connection, but it was exactly the same thing I was doing when I was writing FORTH code.

I also program everything bottom up, rather than top down. I'm pretty sure this is from the influence Thinking Forth had on me as a programmer. I'll make a mental sketch of how things will fit together overall, pick a spot, drill down and then start building the pieces I'm going to need. I refactor as I go. Finally I plug everything together. This also leads me to do subsystem decomposition very, very late in the process. Only after I've built the pieces do I start thinking about where they should ultimately end up. This makes refactoring a lot easier: first when you are building the pieces because you don't have to jump through hoops to get at things, and second when you are assembling because the pieces will naturally fall into places based on what data they interact with. If you have problems understanding where a piece should go, then it's a good indication that you have more refactoring to do.

Some people hate the way I do evolutionary design :-) It works for me, though.

Good points. Another influence I think it had: an attitude like "what one fool can do, another can". While this doesn't directly affect how you organize programs, it can arm you against cargo-culting what you read elsewhere. You do have to watch out for culting up Forth!

Thanks, abecedarius and mikekchar, for the replies.

What I wouldn't give for 5 more books of equal quality to PAIP...

Came here to mention PAIP as well. A lot of Peter Norvig's stuff is good. He makes it all look so beautiful and simple.

Hands-down, Practical Object-Oriented Design in Ruby (http://www.poodr.com/) - some of it I don't agree with but it's all wonderfully put-together: clear and concise, with wonderful examples. Much more about OO design than Ruby, non-Rubyists will get 98% of the value out of it that Rubyists would.

I'm glad to see someone posted this book. I think it's far undervalued. Definitely a must read. The Ruby written could be completely replaced by pseudo code, and it wouldn't change the lessons taught.

And there is another book by Sandi, released this year. https://www.sandimetz.com/99bottles/

Her books and her talks had the biggest influence on me regarding code design. At least influenced to think a lot more about it.

Of lesser-known books, Avdi Grimm's "Confident Ruby": http://www.confidentruby.com/

Learning Ruby itself was a huge influence to me; hadn't considered that a language should be designed to make programmers "happy", as Matz said. "Confident Ruby" was one of several books that had this human-happiness focus. "Confident" is broken down into patterns, many of which can be found in books like Sandi Metz's POODR, but as a semi-experienced programmer, Grimm's way of writing really appealed to me.

Even the title of the book itself was revelatory to me. The idea that the functions and methods and conventions we create should be rooted in a "confident" mentality (such as the old adage of being promiscuous in what a function accepts, and strict in what it returns) really improved the way I designed code. Not just in terms of technical proficiency, but with less cognitive burden, which ultimately leads to the elegant simplicity we desire in our work.

Ruby makes programmers happy while they're writing their sexy new code base. It makes them cry when it's time to refactor it.

I don't know why you're being voted down.. even Avdi Grimm supports that:


I didn't downvote but I suspect whoever did saw the GP comment as bashing Ruby and being off-topic, even though some of my comment talked about Ruby's philosophy as a whole (though I felt it was important to include the context in which Grimm's philosophy is grounded).

FWIW, I almost never use Ruby today, having switched to Python for both teaching and development purposes. That doesn't mean that learning Ruby didn't influence me. For starters, it taught me the dangers of giving programmers too much syntactic freedom :).

I would not equate refactoring an active project with returning to a dormant project.

Avdi is a phenomenal teacher in my opinion and doesn't get the airtime he deserves. If you use ruby in any capacity, I highly recommend subscribing to his Ruby Tapas.

On LISP, by Paul Graham. LISP was the second language I learned in college, but only after 6 years programming Java and C# that I came back and really learned LISP. It was when I realized that I was doing everything wrong. For example design patterns exists because OO has serious problems that we don't find in a functional programming language and you only see this when you understand both paradigms.

Design patterns exist because common solutions to problems exist, not because OO has serious problems. I understand both paradigms and Lisp is hardly free of design patterns. Every time you pass a lambda to a higher order function you're using a strategy pattern. If you only see design patterns as problems with OO, you're missing the point of design patterns. All languages have design patterns. Common design pattern in Lisp, with-X macros to deal with scoped resource cleanup.

Here's some old school thought leadership on dynamic languages and design patterns using actual data. Specifically Peter Norvig looked at all the examples in books like Design Patterns and found that:

16 of 23 patterns are either invisible or simpler, due to [...]


He's talking about the original design patterns book which is a book of OO patterns, it's hardly surprising that many OO patterns are either invisible or simpler in a functional language, functional languages have different design patterns. And yes, I know who Norvig is and I've read that article before.

Design Patterns is about typing out boilerplate code by hand to solve a problem, because the language doesn't have the expressivity to encode the same solution directly.

When it does have the expressivity, the underlying rendering of the pattern hasn't gone away; just the manual boiler-plate for producing it.

For instance, if you're working in assembly language, then you might sometimes benefit from a "while loop design pattern" to keep your code clean. The while loop design pattern calls for some test code before a block which jumps past the block when the test is negative, and an unconditional backward branch at the end of the block back to that test expression.

The while loop doesn't go away when you use a higher level language. You just indicate that you would like that pattern, and the compiler spits it out for you, invisibly.

> Every time you pass a lambda to a higher order function you're using a strategy pattern.

Every time you pass a "naked" lambda somewhere, you're potentially missing the opportunity to have a macro there do that for you.

> Every time you pass a "naked" lambda somewhere, you're potentially missing the opportunity to have a macro there do that for you.

True but equally sad that you must use Lisp to hide ugly Lisp because naked lambda's are so ugly. Yet the point remains, whether hidden behind a macro or not, design patterns still exist in Lisp and every other language and always will.

Lambdas aren't "ugly"; they are sometimes just a mechanism that is not directly relevant to the problem domain: the "how" part of the solution, rather than "what".

The point isn't to hide the lambdas, but to hide the "how", which might or might not use lambdas.

The "how" could instead open-code the procedure that would have otherwise called the lambdas; then the material just becomes embedded forms in the inline code.

Lets not kid ourselves, hiding lambda's is one of the primary uses of macros in Lisp (obviously not the only). Yes, you're hiding the "how", we don't disagree there, but my point was that such syntactic abstraction is only necessary in Lisp because it's so ugly to directly use lambda. Compare to Smalltalk which uses naked lambda's everywhere because they're nice looking as is and don't need to be hidden away by special forms in order to feel idiomatic.

> Lets not kid ourselves, hiding lambda's is one of the primary uses of macros in Lisp (obviously not the only). Yes, you're hiding the "how", we don't disagree there, but my point was that such syntactic abstraction is only necessary in Lisp because it's so ugly to directly use lambda. Compare to Smalltalk which uses naked lambda's everywhere because they're nice looking as is and don't need to be hidden away by special forms in order to feel idiomatic.

Hiding lambdas with macros isn't really that common in my experience. The "how" being hidden is much more often (ultimately) IF, LET, BLOCK, and GO. Of course, higher order functions could often be used instead, but they're generally not.

Are Design Patterns really so out of fashion with the youth of today that nobody here mentions Design Patterns as their answer?

Because it is mine, for better or worse. It took me from understanding OO to understanding how to build large systems with OO, writing maintainable code and using proper encapsulation. A much deeper work than Code Complete (though the latter is worth reading too).

I have used functional languages as well (ML, Lisp; not Haskell yet though I think I get what monads are; plus I write a lot of Python, C++, JS in functional style) but I have yet to use them for any project as big. I hope I get the chance to one day. But for now Design Patterns gets my vote.

I'm nearing 50 so I'm hardly a youth.

http://www.perlmonks.org/?node_id=133399 does a very good job of explaining why Design Patterns is a book to be careful with. By contrast Code Complete won't steer people wrong.

See https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo... for a cautionary tale about how a pursuit of OO purity can result in great verbosity, hiding intent behind a barrage of patterns. Resulting in code that I, for one, would rather not work with.

Agree on both those fronts, if you're not careful. Still dp influenced my coding more than any other book to date, which was the original question.

As to that, Design Patterns did not influence my coding style very much. By contrast Code Complete was the first book that I read about how to structure code, and I've gained more from it on multiple rereads. I consider it a much better book.

But then again I'm hardly enthusiastic about OO. See http://www.perlmonks.org/?node_id=318257 for more on my perspective.

That second link is great btw. I've always felt that aspect of java was odd, and I do like modern fables :-)

Yegge critiquing OO for verbosity, that's a hoot; talk about the pot calling the kettle black.

But only a person as verbose as Yegge can really on about Java at sufficient length to do the topic justice.

You mean misrepresent the topic and play off bad programming as if OO were the problem; you can write shitty code in any language in any paradigm. Java was verbose because it was Java and it lacked many features other language had to remove that verbosity; it wasn't verbose because it was OO. Yegge was spewing shit for traffic at the time, nothing more, his critiques were baseless.

I don't think design patterns went out of fashion. I think the opposite happened -- they gained such mindshare that they became a solution looking for a problem for many people. That is, they forgot that design patterns are meant to solve problems, and that you should also only solve problems that actually exist. Instead we started getting things that resembled "Hello World Enterprise Edition" [0], which has been dubbed "lasagna code" -- lots of layers with a little bit of filling in each one.

[0] https://gist.github.com/lolzballs/2152bc0f31ee0286b722

The person I replied to, sideshowb, explicitly asked about both design patterns and Design Patterns. Regardless, that book was what elevated design patterns into common knowledge within the object-oriented world. Their fate is tied together; the book and the patterns described within are practically synonymous.

> build large systems

DP are oft misused, but I think the main idea is a pattern language - a way to communicate to other developers what the architecture is. This is especially important on larger, more complex projects with more developers and developer turnover. Understanding the architecture of a project is crucial for grokking it, but usually not well documented. Even just "DPs are naming conventions" is helpful in this regard.

I really disagree with the idea that most DPs are particularly clever or helpful - they are just the way you'd end up solving those kinds of problems yourself. If you look at your own pre-DP code, you'll see design patterns. What they give is a standardized terminology ("pattern language").

Sometimes an architecture seems more complex than necessary, but makes sense as you better understand the whole problem. e.g. how curl handles http. e.g. youtube-dl. (for me, when I reverse-engineered mini-versions of them, then studied their source)

tl;dr "DPs are docs"

I've heard a lot of people saying they were already using these patterns before they read the gang of four book, it just put a label on those patterns for them.

I think there are two reasons Design Patterns have disappeared:

1. Being able to use functional programming concepts in OO languages made a lot of design patterns irrelevant (Python, Ruby, C# 3.5, C++11, Java 8). I feel Crockford's The Good Parts really introduced these concepts to the OO crowd with javascript acting as the bridging language for the concepts.

2. Many design patterns were terrible ideas in practice. Factory patterns were a disaster, as were many of the others. Good in theory, utterly useless in practice. There was a blog here recently about TDD that argued TDD is useless because you need to be a great architect to do TDD correctly, and most programmers aren't great architects. Design patterns had the same problem, it was very easy to get them wrong and create an utter mess. Ultimately it's more maintainable to have a complicated monolith method than some crazy pattern no-one understands or dare touch.

> Are Design Patterns really so out of fashion with the youth of today that nobody here mentions Design Patterns as their answer?

Apparently so; sad.

That's true. I think the GP's point was probably that some GoF patterns, specifically, just exist to work around language deficiencies. My go-to example for that is the visitor pattern; something that's trivial in Haskell or Lisp is ridiculously heavyweight in C++ or Java.

That is true, some of "those" particular patterns work around language deficiencies, however OP misunderstand and think that means all patterns do that, they don't.

The parent comment is a variation of the well-known claim from http://wiki.c2.com/?DesignPatternsInDynamicProgramming which says that in a more powerful language, a variety of design patterns go away and become invisible. And Lisp in particular renders many common patterns irrelevant.

The irony is that the languages which are the targets of design patterns already encode considerable design patterns from a previous generation of language research.

Those languages are higher-level languages. A considerable number of design patterns disappears in them already.

In Java or C++, you have functions with parameters and local variables.

In assembly language, there is a "design pattern" of pushing words onto a stack to generate arguments, and moving the stack pointer to reserve a frame for local variables, and then unwinding these actions when the function returns. Also, saving certain registers and restoring them may be part of the pattern. This pattern can be supported with macros and pseudo-ops to varying degrees.

In these higher level language, this pattern disappears. The concept of parameters and local variables doesn't disappear, of course. Those things are just declared in a simple way.

The assembly language pattern for calling conventions is in fact a back-formation from higher level languages. It won't even occur to assembly language programmers until they think of their programs at a higher level, and translate from that to machine code.

Or: let's look at C++ vs C. In C++, there are declarative mechanisms which generate code that can be understood in terms of C. It captures coding patterns, such as ensuring that a structure is properly initialized when instantiated, and cleaned up on every exit path from a function.

And yet Lisp, the language that can abstract anything, still has design patterns because there's no such thing as a language without patterns that are useful to solve common problems.

> Design patterns exist because common solutions to problems exist, not because OO has serious problems

Implementation-recipe design patterns only need to exist (or, rather, only need to be part of the active, day-to-day awareness of more than a small number of programmers per language) when the target language does not support implementing the common solutions as reusable code modules because doing so would require a kind of abstraction the language does not support.

This has nothing to do with the OOP paradigm particularly; there's no reason OOP languages can't support powerful abstractions that reduce the need for that type of design patterns; the GoF book just emerged when the most popular industrial languages were both OOP and lacking in those facilities.

> Implementation-recipe design patterns only need to exist (or, rather, only need to be part of the active, day-to-day awareness of more than a small number of programmers per language) when the target language does not support implementing the common solutions as reusable code modules because doing so would require a kind of abstraction the language does not support.

False. Lisp has macro's and can abstract anything syntactically, yet still has commonly recurring design patterns such as with-X macros. You misunderstand what a design pattern is if you think new language features can remove the need for them.

Can you provide an example of how a language like C++ or Java would require a "design pattern" when another language wouldn't?

In early Java versions, functions were not objects, and therefore could not be passed as parameters. So you needed patterns such as Command or Observer. In Javascript, for example, you wouldn't need them. More recent versions of Java, with the introduction of lambdas for example, are also reducing the need for these patterns.

https://drewverlee.github.io/ i cover some (most) of the Design Patterns from the GOF book though the lens of Clojure. I stopped largely because i concluded the same thing Peter Norvig did many years ago, most DP in that book are work arounds for language limitations.

The Visitor pattern mostly addresses the shortcomings of the type system (i.e no "match" statement).

The Command pattern, in its most basic form, could also be considered as implementing 1st class functions by wrapping them into objects.

So if you're using a functional langage, there's a good chance you're wondering why consider both of the above as "design patterns", as you're seing them as basic things, and the compiler implements them for you.

> The Visitor pattern mostly addresses the shortcomings of the type system (i.e no "match" statement).

The visitor pattern addresses doing multiple dispatch in a single dispatch language.

> The Command pattern, in its most basic form, could also be considered as implementing 1st class functions by wrapping them into objects.

BTW, that's not really true; first class functions are not a replacement for commands. Commands are about turning behavior into state so it can be stored and executed, potentially replayed for example, at a later time. Commands aren't just functions, even languages that have first class functions still use the OO command pattern when doing things that benefit from command patterns.

> > The Command pattern, in its most basic form, could also be considered as implementing 1st class functions by wrapping them into objects.

> BTW, that's not really true; first class functions are not a replacement for commands.

Please note that you're extrapolating from my post: this isn't what I said.

We still need this pattern: there's no way you're going to implement "undo" using only first-class functions, and I'm well aware of that.

In some specific situations, though, we don't need it anymore, like when connecting a GUI "save" button to a "document.save" function.

> Commands are about turning behavior into state so it can be stored and executed, potentially replayed for example, at a later time.

First-class functions plus partial application do that trivially. (First-class functions provide the ability to store a generic command, partial application let's you get a specific instance with local state that can be “replayed” against a different external state object.)

Again, you're missing the point of the command pattern. It is not replaceable by a first class function. I'm well aware of what first class functions are, what they can and can't do, and yes in the most trivial cases at run-time only where only a single action is required of the command, they can be used instead of a command pattern.

Many times commands come with multiple actions, to allow execution and rollback, require being able to be serialized to disk to database and replayed later, or are used to better organize and plug in extension commands to large code bases. None of these cases are amenable to first class functions.

The command pattern isn't just a hack around not having first class functions, we still use command even when first class functions are available. You aren't going to extend Photoshop with a new filter using a first class function; you're going to write a plugin which is a command pattern for filter execution.

Stop trying to tell me what first class functions can do, I'm fully aware, I use them every day and have for nearly 20 years. You aren't talking to some kid who never heard of functional programming.

Functional languages have a different set of patterns.

I'm not the person you asked, and not a patterns expert, but will try to answer, using the Template Method Pattern (a GoF book pattern) [1] as an example:

- In Java [2] or C++, to implement that pattern, you may have to define an abstract class, or a class with some abstract and some concrete methods, and subclass it, and in the subclass, redefine some of the abstract methods (of the superclass) concretely. (Maybe interfaces can be used here instead of abstract classes/methods - in Java. Haven't used Java for a while, so not sure right now.)

- In Python, you can just do:

def transform(input, output):

    # Code here to read data from input, transform it, and 
    # write the transformed data to output.
Now let's say the code in the transform function does input.read() and output.write() (with maybe some way to indicate when the end of input is reached, like the null string is used in Python). In other words, methods like those of Python file objects are used for reading and writing. Given all this, you can now pass objects of any types as arguments for the input and output parameters, as long as they implement a read and a write method respectively, and the code will just work.

This has been described as "smooth seamless polymorphism" in Python. It's also known as duck typing.


[1] The Template Method Pattern is one of the ways in which frameworks (as opposed to libraries) are implemented. The abstract methods of the framework are implemented concretely by your code that the framework then calls, using the Hollywood principle - Don't call me, I'll call you, a.k.a. Inversion of Control (IoC).



[2] Note that what I wrote is based on knowledge of older versions of Java - newer versions may have features that make the pattern simpler to implement, maybe without abstract classes/methods.

Here's what I think is the classic illustration from Peter Norvig in 1996:


OO doesn't have serious problems. Crap implementation of OO bolted onto Algol derivatives has problems.

That was also the impression I got on this issue; however nowadays I'm usually less frustrated with what the respective languages provide and more with what people use them for.

C++ since 11, C# since ~5 and Java 9 for example have actually introduced very useful tools¹ that would allow people to write much more readable code - yet there's always someone who insists that anything beyond the C++98-style is somehow evil and inherently unreliable.

¹Yes, they're by no means perfect, but they introduce are a lot of low-hanging fruit when it comes to improving code quality - if one decides to use them...

Yeah, OO as commonly used is not the OO as originally envisioned and implemented: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...

Speak of the devil; I just got +10 points an a "necromancer badge" for this:


Refactoring: Improving the Design of Existing Code

After reading I began to think about programming as an algebraic transformation from one system to another, in doing so radically reduced the amount of errors I made.

I got this when it first came out, I was kind of doing it in a "gut feeling" kind of way, but this made me think of it more as a discipline, and solidified the idea of micro improvements and let me drop the idea of trying to get it exactly right the first time.

It surprised me just how huge the impact was of reading about Flow-based Programming years ago (J Paul Morrison's book, though, his site is probably a sufficient substitute http://www.jpaulmorrison.com/fbp/)

Of course, in recent times, it has become all that much more relevant to me as I began working in data science/engineering space. Even though it's not specifically about code structure for a particular language, it addresses a common flaw in most programming approaches that seem to treat all functionality as a servant of the current context which is strange and silly and not how anything works in physical information processing so why do it in code?

A somewhat common pattern that maps well to FBP is "Railway Oriented Programming." Though FBP in full takes this well beyond simply shooting errors along in sideband to the happy path.

Literate Programming, Knuth. My methods have evolved since I first read and started toying with the ideas here, but this is where it started.


Essays, not books, but ... everything Ken Iverson wrote:

The Description of Finite Sequential Processes http://www.jsoftware.com/papers/DFSP.htm (see e.g. the concrete implementation of the Simplex algorithm , the hamming code corrector). It shows that, with the right primitives and notation, a lot of things are simple and elegant. It's not an necessarily an easy read (depending on your math level and background), but it is a very rewarding one.

Notation as a tool of thought http://www.jsoftware.com/papers/tot.htm - a longer introduction.

Bottom line: a different take on abstraction. It makes a huge difference when you peel most abstraction layers.

The Art of Unix Programming, Eric S. Raymond. http://www.catb.org/esr/writings/taoup/html/

I'm still trying to achieve everything he advocates, but what I've managed so far has been extremely beneficial.

Refactoring. If I could shove that book down every coder's throat in the world I would. Or, if I could even just get them to use extract method like ... half the times they should I'd consider that to be a major historical achievement.

Clean Code by Robert C. Martin

This helped me break my analysis paralysis when it came to figuring out how to organize my code.

Excellent recommendation.

The examples are written in Java but it doesn't matter too much as most concepts covered in the book are language agnostic.

The Little Lisper by Dan Friedman. It is probably 30 years out of publication but I pulled it off of my shelf the other day, reread it, and the next day I noticed that my code had improved. It is a quick read, nothing earth shattering, but you will be amazed by what you have forgotten.

Game Programming Patterns - without a doubt. Own it in print, but usually only read it online. To me it's a more exciting read than the GoF book


Thanks for providing online version. Where I am staying I cannot purchase most of these books mentioned in this thread.

For C#, Framework Design Guidelines by Cwalina & Abrams. Very clear and concise pointers for well structured and easy to read code. It's a little out of date now though, wish they'd update it to a third edition.

A lot of the design guidelines in that book carry forward many other technologies. A really great read.

thanks for mentioning the programming language

SICP made the biggest difference; it really changed the way I approach mutability and state management in everything I write (To name just this).

How To Write Unmaintainable Code by Roedy Green.

The best way to write better code is to avoid writing it badly in the first place. But you need to know how to write bad code to write code better than it. Definitely a different way of learning how to write good code, also a good laugh for anyone in industry.

I am almost tempted to bring it into work and frighten my manager with it :)

Writing Basic Adventure Programs for the TRS-80. [1]

It contains this gem [2], which is pretty much how every program works. I occasionally riff off this diagram for work as an inside joke with myself.

1: http://www.trs-80.org/writing-basic-adventure-programs-for-t...

2: https://imgur.com/gallery/Vz63D

Not a book, but the game spacechem [1] made all my code start being more influenced by the actor model [2] without me really realising it.

[1] https://en.wikipedia.org/wiki/SpaceChem

[2] https://en.wikipedia.org/wiki/Actor_model

For a modern look at systems design i highly recommend "Designing Data-Intensive Applications (DDIA)" By Martin Kleppmann. Not really about structuring code, but I think stepping back and realizing your code is part of a larger system is very illuminating and influence how you write code.


A Mentoring Course in Smalltalk[0]. I was really surprised, after reading and really loving Design Patterns, that there was still so much to say about OO design.

I did love Design Patterns a lot though. Purely Functional Data Structures by Chris Okasaki was also really useful for Haskell, as was Real World Haskell.

Programming Prolog probably had a bigger influence on my Prolog than the other books, even though I read Art of Prolog and Prolog Programming in Depth first. Especially the latest edition, it's a really beautiful book.

[0]: http://www.lulu.com/us/en/shop/andres-valloud/a-mentoring-co...

Probably the book that surprised me the most, was "Designing Active Server Pages" by Scott Mitchell[1] - bought for next to nothing in a sale clearing out old titles. I don't really program in VBscript or on the .net platform - but the book demonstrates how much improvement it is possible to get in a server-side template language (eg: like PHP, ColdFusion) with a bit of mindfulness to how code is structured.

I'm not sure I would recommend it today, but at the time I read it, in the mid 2000s, it did change my view on these "unmaintainable" technology stacks. I later came across the fusebox architecture/pattern, originally from ColdFusion - and realized that many PHP programmers had skipped some history, ending up reinventing code structure, sometimes badly.

Note that fusebox has grown and changed, I'm mostly talking about the fundamental ideas, and I don't think the later "port" to using XML was a very elegant or good idea. For those interested, see:

https://en.wikipedia.org/wiki/Fusebox_(programming)#Fusebox_... and most of the rest of that page.

[1] http://shop.oreilly.com/product/9780596000448.do

Very old, but "C traps and pitfalls": http://www.goodreads.com/book/show/706807.C_Traps_and_Pitfal...

It's essentially a list of anti-patterns to avoid. But crucial to it is the idea of clarity and avoiding misinterpretation by either human or compiler.

SICP (w/ Brian Harvey's CS61A lectures) influenced how I approach abstraction _a lot_. Its "baby brother" _How to Design Programs_, with its design recipe approach, ingrained testing and iterative refinement in me early on.

_Head First Design Patterns_ is a great influence too when it comes to OO-abstraction, for good and bad.

I'll second HTDP. A close read of the preface is worth your time even if the deliberate pace of the rest of the book isn't your cup of tea. The data-first approach to design is a breath of fresh air.

At the risk of being labeled a dinosaur, this is still prob. the best book I ever read on structuring code:


Practical UML Statecharts in C/C++: Event-Driven Programming for Embedded Systems by Miro Samek.


Clojure Applied by Ben Vandergrift and Alex Miller. That and Rich Hickey's Greatest Hits (https://changelog.com/posts/rich-hickeys-greatest-hits).

I haven't read that many coding textbooks not specific to a language but the Joy of Clojure probably slapped me with the most ideas I'd never seen before. Not sure it made me a better developer in the short run, but I'd like to think that it will in the long run.

"The Practice of Programming" by Kernighan & Pike, especially chapters 3, 4, 8 and 9.


Alexandrescu's Modern C++ design taught me how to template properly.

Also, Scott Meyers's books were very helpful.

The design of the D standard library has also been very influential on my code (Mainly convincing me of the benefits of ranges over iterator)

Clean Code

Classic essay on unmaintainable-code.


Kent Beck'a Smalltalk book. I've never used Smalltalk but it transfers to any dynamic OOP language. (It also used to be $25, yikes what happened?)

* Douglas Hofstadter - Gödel, Escher, Bach: an Eternal Golden Braid

* Fred Brooks - Design Of Design

* Fred Brooks - The Mythical Man month

* Eric J. Evans - Domain Driven Design

* Design Patterns: Elements of Reusable Object-Oriented Software

* Kent Beck - Extreme Programming Explained

* Kent Beck - Planning Extreme Programming

* Michael C. Feathers - Working Effectively with Legacy Code

* Daniel Kahneman - Thinking, Fast and Slow

[EDIT] Correct the author for legacy code

I've just recently bought "Gödel, Escher, Bach" -- looking shortly to start it. Curious, how did it influenced you in connection to programming?

Just like the other non programming book on the list(Thinking Fast and Slow) GEB is a lot about perception to me.

Understanding that nothing is purely logical nor is it purely expressionistic; by pigeonholing your perception of any design whether it be Code, Math or Art to either Logic xor Expression you are blinding yourself

The other angle for GEB is design

I read GEB along with Design of Design years ago following my mentor at the times suggestion, I would highly recommend doing this. Think of it as the Gödel, Escher, Bach, Brooks (Although Brook's writing style can be laborious at times) Recognizing patterns in all design work helped my understand my own design process better.

That's fascinating. Just ordered Design of Design to supplement GEB. Thanks!

> * Bob Martin - Working Effectively with Legacy Code

Author is Michael C. Feathers

Nice catch, thanks :)

correction Working Effectively with Legacy Code is by Michael Feathers

Jon Bentley, _Writing Efficient Programs_ and Kernighan & Ritchie's _C Programming Language_.

Javascript: The Good Parts

The pragmatic programmer

The Life-Changing Magic of Tidying Up by Marie Kondo

Does anyone else here never read programming books?

They are a great ladder for when one wishes to climb up the shoulders of giants. Much can be learned in blogs, but long-form text is not that easily replaced.

That said, my books-to-read list is ever growing as well...

I think I've read fewer than 5 programming books in 30 years. For the most part, I don't read about programming. For certain specific problems, I'll read articles or watch talks by people in my field who I respect a lot. Mostly, I just write a lot of code, observe what I don't like aesthetically, and think about how to make it better.

Dependency Injection in .NET by Mark Seemann was the trigger that started my transition from someone basically writing "script spaghetti" in an OO language to someone that could actually decompose a conceptual set of processes into decoupled parts and assemble a software system from them.

It came along at just the right moment in my career, when I was struggling to understand how I could build things in a more elegant way. I picked up the book thinking I was going to learn about some esoteric design pattern, and came away with a much better understanding of the languages I was using and all of the other design patterns I thought I had learned about previously. It's clear, concise, and focuses on concepts over specific tools and libraries, but most importantly it's practical - it has real, practical code examples and explains how to actually build an object-oriented system. It's such a stark contrast with most presentations I've seen of the Gang of Four patterns and of SOLID, which usually come with really weak examples that aren't helpful or motivational.

Everything snowballed from there. I started using composition a lot more than inheritance, I started identifying problems with side effects and eliminating them, I started writing real unit tests, I was able to better critique other peoples' code. I felt like I was finally using the tools available to me in the way they were supposed to be used.

What's funny and satisfying to me is following the author's blog and seeing that he has since moved on to focus primarily on F# and functional programming, which I naturally started to do myself after more practice with the concepts in his book. Once you start decoupling things well, and you've built a few systems big enough that you have trouble finding the actual implementation of your IWhatever and an AbstractSingletonProxyFactoryBean actually does solve your problem pretty well even as you realize the insanity of it, the encapsulating borders of classes and the need to assign everything to a noun start to feel more like a hindrance rather than a guide.

That said, I still think that most of the world's code written in object-oriented languages would be better off if everyone using them had brief, practical training to understand the value of specifying the behavior of an object through the interfaces it depends on, giving it other objects that implement those interfaces right when you create it, and doing all that creation up front (or specifying other objects that can defer that creation to later). I still see so much C# code from developers at all levels who clearly create classes only because the language offers it and it seems like the right thing to do, randomly jamming methods and fields into classes with names vaguely related to the domain, calling static methods to access databases and external services, and proudly adding unit tests for their one loose little function that mushes strings together. I push this book as hard as I can on junior devs.

+1, This book is real gate opener for my career.


Head First Design Patterns by Eric Freeman Design patterns by GoF Code Complete by Steve McConnel

for me, there is no special book effect my code style, the greatest effect is read source code of projects I am interested in.

"Implementation Patterns"

Scott Meyers Effective C++

all modern programming books are footnotes to Kernighan and Ritchie

Joy Of Clojure

work effectively with legacy code

Digital Design and Computer Architecture (Harris & Harris)

Why? Because we software engineers can learn a lot from the hardware guys. Almost every piece of software I write these days contains some finite state machines (technically, every program is a finite state machine, where the binary string that makes up all your variables at a given point in time is one state, that insight alone is valuable, but I mean with explicit states in the code) - in fact, they often make up the core structure and uphold some strong invariants that make reasoning about the code simpler. And if one finite state machine does not do it, then you can nest them and keep all the benefits.

Smalltalk Best Practice Patterns by Kent Beck. Refactoring by Martin Fowler. Design Patterns by the Gang of Four. Domain Driven Design by Eric Evans.

As a programmer in training who has both Clean Code and Code Complete in a queue on the edge of my desk, I'm following this thread to decide which goes first, or if they both get sold in mint condition.

Code complete is a classic.

Clean code will teach you how writing 3 line functions is a best practice. It's an overhyped, overrated book which is more damaging than helpful and one of the very few I couldn't stand to read until the end due to the authors' dogmatic views.

That's interesting, I had the complete opposite reaction to clean code. As a person that recommends this book, with full heart, to every developer, I'd like to hear any details you could spare re: why you believe it's damaging? Maybe you can provide some examples of well-written codebases and why you believe they're such? And maybe you could comment on why you feel Clean Code's principles harm readability with some examples?

I find it painful working with codebases that don't use most of the principles re:

- Write code like good stories: Code should read like a story, methodically descending the call graph method by method; each working at one level of abstraction.

For example, why is string manipulation littered all over this function that's supposed to be dealing with consolidating reports? Ugh. I like when one function stays at one level of abstraction

- Factor out conditions into descriptive methods: I shouldn't have to sit and read through 5 logical operators, some arithmetic, and method calls in 1 'if' conditional, to understand why we're doing all this.

Once again, this goes back to having your code read like a story. Factor it out into a descriptive function, so that I can understand at a glance and deep dive if necessary.

- Comments are a failure: This applies 99.99% of the time. It's almost comical when someone leaves behind a comment that could be eliminated by factoring something out into a descriptive method call instead.

There's a lot of other great stuff in Clean Code that I'm forgetting off the top of my head. (It's been awhile since I last looked over it.)

I've come across very few code bases that are a literal pleasure to read. https://github.com/jekyll/jekyll is one of the cleanest codebases that comes to mind immediately. And on the other side of the coin, https://github.com/kubernetes/kubernetes is one of the dirtiest.

If anyone wants more details/expansion, let me know and I'll reloop. (On my phone right now.)

(Not parent)

Having read both a long time ago, I also feel like Code Complete is the one to recommend: it covers more ground, is pragmatic, and more importantly most of its recommendations are based on studies, i.e. they come from empirical evidences. Clean Code compares poorly to this, in that it's just one guy's opinion.

That doesn't necessarily mean Clean Code is a bad book, but since our work relies primarily on logic, I'd rather have arguments presented to me logically.

Since both books cover pretty much the same ground, it makes more sense to recommend Code Complete.

The problem with Clean Code is, just as the post you replied on said, it's very dogmatic. A problem I often see people who quote Clean Code do is that they take its principles to the extreme.

An example is tons of tiny methods of 1-3 lines each. Sure splitting up methods is good practice but it's painful to read code where you continuously have to "go to definition". There is an article written about this by John Carmack here http://number-none.com/blow/john_carmack_on_inlined_code.htm...

Another example is the "comment are a failure" mindset. Sure I don't need comments that say this code is doing an addition between two numbers, but a piece of code doing some complex business logic definitely benefits from a comment explaining what the hell it's doing. No amount of function-splitting and function-naming can replace such comments. Yet all too often people who read Clean Code treat comments as some sort of abominations that has to be avoided at all cost.

A dogmatic book does not make a dogmatic person. And if that's a "problem", then that's rather a problem with the person reading the book.

Please provide examples of such codebases you're talking about.

One justification of modularity is to hide details. Thus avoiding the need to "go to definition". If the tests pass and the name is descriptive, then you should understand the method without needing the details.

For example, Jekyll's Site#process [1] provides a high-level overview of how the site is built. It reads like simple, plain, easy-to-understand English. Now, I've chosen to dive into Site#write [2]; it tells me that for each site file, we're going to write it out to its destination, as long as it's supposed to be regenerated. Awesome, that's easy to understand.

Say I want to write another method that operates on all the site files. I don't need to know how Jekyll finds all the site files. Heck, they could be in specific directories... or it could pull configuration over the network... or they could be provided through command-line arguments. Who cares! I just use Site#each_site_file because it's an implementation detail.

And yes, Jekyll provides little 1-3 line helper methods like Site#incremetal? [3] all over the place to codify conditionals. These are extremely helpful.

On the other side of the coin, do you know what this conditional is for [4] in Kubernetes? I can't for the life of me understand its purpose without being forced to look into the details. It'd be much easier to read if it was extracted out into its own descriptive method such as activeMultiNodeInterface maybe? I don't know because I literally don't know the intention of that conditional. The original developer could've made their intention far more clear to subsequent developers had they extracted it out.

I find your citation of Carmack underwhelming for two reasons: (1) he's writing to a very specific target audience -- game developers, and (2) he admits himself, "The whole point of modularity is to hide details, while I am advocating increased awareness of details." Carmack is in no way supporting the idea that "it's painful to read code where you continuously have to 'go to definition'". In fact, quite the opposite, he's advocating a very specific recommendation to a very specific type of developer working on a very specific type of project. Simple as that.

You'd do best to provide some examples and empirical data supporting your assertions.

[1]: https://github.com/jekyll/jekyll/blob/master/lib/jekyll/site...

[2]: https://github.com/jekyll/jekyll/blob/master/lib/jekyll/site...

[3]: https://github.com/jekyll/jekyll/blob/master/lib/jekyll/site...

[4]: https://github.com/kubernetes/kubernetes/blob/master/pkg/kub...

Care to elaborate what bits of Clean Code are damaging?

I've read it and I thought it has some good ideas. It's the #3 most discussed book on StackOverflow. However, I've been downvoted on HN for recommending something the book talks about (comments are usually misused)

Maybe I'm missing something more experienced developers know.

Sandi Metz was already mentioned in this thread, so I present https://www.youtube.com/watch?v=8bZh5LMaSmE where she shows how the little things can help.

Slightly kidding ofc. I have already skimmed sections of each.

Go for Clean Code!

JavaScript, the Good Parts

Alice in Wonderland


Are books still state of the art in 2017?

I would assume all the best knowledge could be found online for free by now.

Books are useful for a couple of reasons.

1) It can do wonders for focus to disable your network connection, open a book, and work through examples (but maybe I just get distracted easily). This actually applies for most tasks that involve a computer and don't need the internet.

2) Not specific to print, but a book that covers a topic in a structured, beginning-to-end way can help reduce gaps in one's knowledge. I learned a bit of BASH by just googling around and reading Stack Overflow, but reading The Linux Command Line (https://www.nostarch.com/tlcl) taught me things I wouldn't have gotten in 10 years of stack overflow browsing.

Online resources are often shallow in content. I prefer to deep dive into a topic and truly learn it inside and out by reading a good book. Blogs and tutorials don't give you that deep insight.

Do you read enough books to have an informed opinion on that?

For me, the best way to absorb knowledge is text. Video makes it much harder to skip parts (you can jump forwards but you can't quickly skim to make sure you have a vague idea what you're skipping. You can speed up and slow down videos but it's far, far more clunky to me than varying between skim reading vs deep reading. I've spend literally decades now doing that every day when you think about it, varying digestion speed by orders of magnitude without thinking about it, no chipmonk voice involved.

Hyperlinked text might be better, so that you have a graph of knowledge not a searchable seq(book with chapters and an index), but there's a natural order to learning things, the teacher hopefully has an idea what order it makes sense to put the concepts in front of you in.

tldr; books are hard to beat, if something was going to beat it for me it would be a well designed, portable wiki of the book's contents, maybe with some interactive exercises. Definitely not the video lectures and homework of most online courses.

A book can be coherent and curated in a way that a collection of posts by different authors cannot be.

Many people prefer books to screen reading. Many people also learn better from reading.

There's only so much you can put in a blog or lecture series. Most college courses don't even cover a whole book (in STEM subjects).

Software design is relatively timeless. Your not learning technologies, your learning how build software in general.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact