“An investment in knowledge always pays the best interest.” – Benjamin Franklin
Many of the best software developers have T-Shaped Skills: Deep expertise in programming and software development, and broad knowledge of diverse areas including testing, DevOps, UX design, team organization, customer interaction, and their domain areas. While there is unfortunately no substitute for experience, reading is probably the next best thing. Over the past 10 years I’ve read a lot in an effort to deepen and broaden my knowledge as a software developer. Along the way I’ve been organizing books and concepts into the reading list I share below. I have been trying to design a core curriculum for “modern” software development by asking myself:
- What core concepts are required to be a world class software developer?
- What is the best book for introducing and teaching each concept?
The result is a list of 16 essential software development concepts presented by 16 excellent books…
Component Design is one of the concepts on my software developer’s reading list. There are a lot of component design principles (heuristics), so to help me keep them straight, I’ve organized them into this (imperfect) mental model for how they relate to each other:
- Graph: Minimize complexity of the static component graph
- Graph Nodes: Minimize complexity within components (nodes)
- Responsibility: Have a single responsibility by separating: concerns, command/query, option/operand, creation/usage, policy/mechanism, domain model/persistence, domain model/distribution, and facets (who, what, when, where, why, how)
- Inputs / Outputs: Generally try to minimize the number of inputs/outputs. Similar concepts include imports/exports, dependencies/clients, fan-in/fan-out, efferent coupling/afferent coupling
- Implementation: Design code to communicate intent (see the Code Design concept on the reading list)
- Graph Arcs: Minimize complexity in dependencies & relationships between components (nodes)
- Interface Size
- Dependency Binding Mechanism
- Dependency Inversion is Good: Statically depend on interfaces, not implementations (Dependency Inversion Principle). Implementations are provided at runtime.
- Dependency Pyramid is Bad: Statically import implementations, so implementations cannot be changed at runtime.
- Relationship to Testability: Dependencies must be provided at runtime before you’ll be able to use Test Doubles (for more details, see the Test-Driven Development notes in the reading list).
If I had to summarize component design into one sentence, I might say something like: Manage complexity by using interfaces (information hiding and abstraction) to decouple the components in your system.