Becoming an Accomplished Software Designer
I know one of the secrets to becoming an accomplished designer, and I’d like to share that with you today.
Both in my teaching and my work, I approach design two distinct ways: intuitively and mechanically. I find these approaches complement each other well, and I wouldn’t design nearly as effectively as I do without knowing about both. I see programmers live and die by their intuition, and that causes a handful of problems:
- It appears to create two kinds of programmers: those who know how to design and those who don’t.
- It encourages programmers to advocate for specific designs that have shown signs of no longer fitting the current need.
- It infuriates programmers who have accepted that they need to change their design, but don’t yet see a more appropriate solution.
When I teach programmers about modular design I introduce them to a much more systematic and mechanical style of design than they tend to practise, and this makes them uncomfortable. Some worry that I want them to unlearn everything they know about design, but on the contrary, I want them to feel comfortable using two complementary approaches to design that will help them avoid becoming stuck when they encounter a design puzzle.
When I look at a problem and form an early picture of a potential solution, I rely on my intuition. You can tell that I’ve invoked my intuitive side when, as we work together, you hear me talk about code not “feeling right”, or mention “seeing” the need for a specific pattern, or even say “my Spidey senses are telling me…” On some days, my intuition leads me to a good solution, and on others, it leads me astray. Approaching design intuitively provides a short cut to a solution, but not always an appropriate solution.
When my intuition fails to guide me, I fall back on my mechanics. You can tell that I’ve done this when, as we work together, you see me take impossibly tiny steps rather quickly. Even when we both seem to know where the design has started to go, I continue to work in tiny steps, and probably insist that you do the same. It takes longer to reach a solution, but much more often than not, I feel good about the solution we reach.
I have learned to trust my intuition most days, but remain confident that when my intuition lets me down, my mechanics will take me the rest of the way. This relates directly to how I think about design, as the following lists indicate which techniques and principles I categorize as intuitive or mechanical.
- Dependency Inversion Principle
- Design by Contract / Programming to Roles / Programming to Interfaces
- Isolating domain behavior from technology integration
- Coupling and cohesion
- Abstractions in code; Details in metadata
- Design patterns
- Presenter- or Controller-first Design
- Remove Duplication and Improve Names in small cycles
- Test-Driven Development
- Write integrated tests only at the boundaries
- Check one thing at a time
- Write tests by starting with what you want to check
- Inject dependencies through the constructor
Only by practising the mechanics diligently did I begin to develop my intuition about design. Now, when my intuition starts to guide my design, while I might not manage to articulate my thinking at that moment, don’t think that I haven’t taken leave of my rational senses. Instead, I’ve tapped into the part of my brain that can see several dozen, or even hundred, tiny steps in advance, because I’ve practised those steps hundreds, even thousands, of times before. This explains why I emphasize mechanics in both my teaching and my work: intuition emerges and crystallizes by repeatedly practising the mechanics.
J. B. Rainsberger and friends, “Understanding Coupling and Cohesion”. 57 minutes of video. A discussion among eight programmers comparing their understanding of and approach to teaching about coupling and cohesion.
Several articles at blog.thecodewhisperer.com on the topics of simple design, higher-level design principles, and the perils of integrated tests.