HACKER Q&A
📣 newsoul

How did you learn good program design?


Was it a specific language that caused the shift in mindset? Or was it a book, a project or a course at university?


  👤 junon Accepted Answer ✓
Via lots and lots of bad designs. Oh and then more bad designs.

Slowly, you start to realize what didn't work, so you try something different. Read about a new pattern, try it. Realize why it doesn't work in specific cases.

Slowly, you start to find things that do work. And you start to reuse them. And sooner or later, your code becomes more robust. You start seeing less spurious bugs. Your programs seem cleaner and faster.

You start to notice themes and patterns, some of which are a bit complex to even verbalize - almost like a muscle memory. You start seeing these themes across many types of systems.

Revisiting programs later on, you realize old mistakes you made, which further solidifies the "feeling" for what's good and bad design in which cases.

There's no real shortcut here. It's just a matter of experience. The only thing aside from simply writing more code is to read more code (whatever form this might take).

I highly suggest getting involved in OSS.


👤 eyelidlessness
0. Working with very talented people exposed me to a lot of ideas and concepts I probably wouldn’t have found. I’m self taught, most of my prior learning was unstructured trial and error.

1. Worked on distributed systems with externally imposed fault tolerance considerations. Designing around these constraints was painful, but forced me to learn how to learn the relevant technical terms and prior art.

2. Functional programming (in Clojure) taught me how to really think about data flow, and design around it. Limiting and isolating state is fundamental to how I work now even in imperative languages.

3. Static types (in TypeScript, which wasn’t my choice but which I love now) taught me how to really think about modeling data upfront, in ways I find more challenging in dynamic languages.

4. Working on several projects either focused on performance or with significant perf challenges has taught (and continues to teach) me how to anticipate where to focus either in initial design or how to approach performance-sensitive refactors.

Edit to add:

5. Reading a lot of code has helped immensely. Often when I find a project I’m interested in, I’ll read its source code in my free time. I’ve learned so much and been quite inspired, even reading through projects I’d never take on myself.


👤 rramadass
The only way to learn this is to study complete systems done by others (books, existing systems that you are working on etc.) As Bismarck said; “Only a fool learns from his own mistakes. The wise man learns from the mistakes of others.” The same is applicable on the positive side too. So read/study as much as you can and memorize/learn key design techniques. C/C++ is particularly rich in this regard since there is so much (of varying degrees of complexity) out there in these languages.

Two concrete examples;

1) When i started out with Computers in early 1990's, i read Al Williams' Turbo C.: Memory Resident Utilities, Screen Input/Output and Programming Techniques This book taught me how to layer abstractions and build systems one layer at a time. For example, the book defines a layer over the BIOS api, layer on top of that to design a windowing system, layer on top of that to design a window manager and use that to write a memory-resident text editor. Lots of different things coming together harmoniously to build a complete system.

2) A few years ago when i started to study Embedded Systems, as a Software Guy (no Electronics knowledge) i was at a loss to understand HW gobbledygook until i came across Michael Pont's Patterns for Time-Triggered Embedded Systems (large book freely available at https://www.safetty.net/publications/pttes) It is full of C code showing exactly how to interface with various HW in an Embedded System. That gave me everything i needed to understand how to implement a bare-metal Embedded System and eventually understand the HW (from other books).


👤 oxff
Writing a toy compiler, the entire pipeline end-to-end, for a language no matter how small. I can't think of another exercise that covers so many good issues to solve, and good practices for solving them.

The two I like to recommend are:

- PLAI: https://cs.brown.edu/courses/cs173/2012/book/

- Essentials of Compilation: https://wphomes.soic.indiana.edu/jsiek/

For general software design, this blog is quite golden: https://www.tedinski.com/archive/


👤 iex_xei
By iteration. I don't think the problem is solved, we still don't exactly know how to architect our software and the current mainstream of "OO-design" is flawed IMHO. There is no one size fits all architecture for software.

I think size matters, tools matter, technologies matter along with the domain. Our architects are usually too formal, too idealist and we really have difficulty in priorities. We know it requires experience to apply programming languages to problems, this is also true for patterns. In order to apply them, we need experience.


👤 rohitpaulk
Mostly practice. I'd build toy versions of tools like Redis, Docker, Git etc. from scratch. I now help others do the same with CodeCrafters.io.

One underrated book that I found very helpful: "A Philosophy of Software Design" by John Ousterhout.


👤 mjepronk
I like the book Domain Driven Design by Eric Evans.

Also, pure functional programming and immutability have greatly influenced my design even in languages other than Haskell (my main language is Python).

Eric Evans also stresses the importance of immutability of what he calls Value Objects.


👤 Froedlich
Back in the mid-1980s PC world, it would have been hard to avoid at least one of the many competing One True Ways. But the majority of what became my personal ideas of good design came from Phil Burns of Northwestern University, who posted the sources for commercial-grade programs as freeware.

Pib's style was probably evolved from his time teaching programming, and was seriously old-school even then.

A) define the problem set

B) outline a solution set

C) break the solution down into logical, preferably standalone or reuseable, modules

D) write detailed interfaces between every module

E) wrap it all in a main execution loop

This not only broke what might be an overwhelmingly complex program into smaller pieces, but was practically a self-hosting test framework to start with. And being highly modular, it was suited for splitting modules across different programmers.

I've watched many-many styles go by since then, but what I learned from Pib and NWU has always worked well for me, even when programs evolved to where most of my actual "programming" was just calling outside APIs, frameworks, or classes.


👤 egberts1
Study Mother Nature for all her glory and its design.

While trout-fishing (eons ago), I devised fastpath for network packet based on geological trend of meandering brooks and their lovely S-shaped curved when I notice “these bends do eventually connect”.


👤 ipsocannibal
What I've learned of "good" program design in my career has come mostly from watching people maintain systems after the original authors have left the project. Compositional systems with well defined abstractions and ownership boundaries seem to fair better over time. They are generally easier to extend while maintaining the abstractions. Unfortunately, most systems in production aren't designed this way. The majority of systems designed at the companies I've worked for are done so by entry or junior level developers. This usually has do with how the promo process works at these companies but thats a separate discussion. Dealing with the pain that poorly designed systems cause really highlights the properties that make certain systems well designed. To gain the experience here get on a project team that has an oncall rotation, preferably a 24hr rotation. If the team has a group of dedicated support engineers even better. This to me is an indication that the team's systems have a high operational load which is usually caused by poor design choices. You'll quickly start to identify the pain points in the system. Next, try to address those pain points. Rinse and repeat for years and you'll understand "good" design.

👤 ReaLNero
I might be late to the show, but here goes:

It's tough to follow other's advice on how to design programs. Each program is different, and applying a paint-by-numbers approach like making everything an object just adds unnecessary bloat to the code. The code should always be crystal-clear, so following design frameworks is not going to be helpful. You are pretty much on your own in terms of design.

A good rule of thumb for designing programs is to prioritize code simplicity. It's fine if it's too simple -- once a piece of code no longer works, iterate and redesign it. Generally, simple code is most flexible. Puting off design decisions until later => more knowledgeable decisions => less technical debt => more flexibility later on.

Be very wary of making abstractions early on. Spaghetti codebases usually result because of bad abstractions. Don't be ashamed of duplicating code many times, as long as it's simpler to read than interfaces and implementations.


👤 rufius
Read a lot of code. Write a lot of code.

I read a lot of books too but most were not especially enlightening.

Probably the most influential thing in how I write software was what I learned in English in high school about structuring essays (I.e. classic rhetoric). A lot of analogies there in my opinion.


👤 drdunce
"Head first design patterns"

I also read: - Beautiful code - Code complete - Design patterns - Refactoring - Object oriented analysis and design and many others, but honestly, the first one was a quick and easy read and gave me 80% of it


👤 bsaul
Good design can be performed with any language. However, many language distract you from what good design is all about : thinking deeply about what your particular problem is about. The go programming language, because it is so minimalist, will encourage you to spend time on that, instead of which language feature you should use for representing one part of your data.

👤 eimrine
"The Inmates are Running the Asylum" by Alan Cooper.