There is an ongoing debate about how best to design software and how to communicate it to others. Through my career I’ve taken different approaches.
- Trying to make the best of things when the rest of the team wasn’t interested in doing good design and instead, just hack code together.
- Designing very detailed design docs with lots of UML and details — to the point where you could write code directly from it without thinking.
- White-boarding out the high-level design and then figuring out the details as the code is written.
- Designing the code iteratively, rapid prototyping, and alike.
- Other variations too.
But over time I’ve settled on this as being a really good approach. Rather than writing up a 30 page design doc with various UML diagrams that in the end will be uploaded to a server and forgotten — it is far better to include the design in the code, where it will continue to be useful.
This is how I approach design and early implementation
- I read through the “spec/description” a couple of times and take notes.
- What are the major blocks?
- For UI heavy projects, I mainly focus on the visible screens. For infrastructure/core projects, I’m trying to identify the main classes and modules. In both cases I’m trying to identify and define the main players.
- I will then (again on paper) draw out class diagrams; add the main relationships; and start adding the public interfaces (just the main methods) for each class. Then I play with it; work through how the classes interact; change the design; play with it more; fuss with the names of the classes and methods; play with it more — defining and redefining. Basically until I feel the classes and the interactions are simple and clear (a high school student should be able to easily use it). Notice that I’m highly focused on getting the design and interface right. This is of the highest importance.
- Now that I’m settled with the design, this is where getting outside feedback is important. I find it best to do this one on one with a couple of people. In companies that require a formal design review (which we don’t); I would do this before the formal review. If you’re smart, you’ll do this with the key people that will be in the review.
- At this point I’m ready for precoding. I take the notes, diagrams, and docs (everything that I designed above); and make skeletons for the modules and classes. For modules/packages, this simply means to create the directory structure for the code. For classes, I add the classes and each of their public methods — no code, no member variables, just the method prototypes. I then add design level comments to each class and method. The comments for the classes should answer what it does, and how it does it (very briefly). I capture the main design considerations — to aid comprehension. For methods, the comment should focus on what it does and how to use it (sdk-like documentation). All of these ought to be brief.
- Once done, I’ve taken my design from paper to code.
- Now it’s time to start focusing on implementation. I document the “logical blocks” within each of the methods (no code, just a one or two line comment for each of the “logical steps” within a method). This point is really important. This is where I will work out the detailed design. So rather than doing this in a 30 page design doc that will be forgotten, the detailed design will aid me in coding and live on to help others understand the code. This is also a good time to create skeletal versions of the private methods. Its fine to add for-loops and other trivial code — especially if it aids in working out the detailed-design, but this isn’t the coding step, so this should be minimal.
- Now that the detailed design is coming together I often find that that things need to change — so I do it. This is fine, because I haven’t wasted a bunch of time banging out code, and comments are easy to rewrite…
- Now given the above, its easy to walk through the comments and fill in the code.
The end result of this is that you have a well-documented, well designed, code base. This naturally leads to comprehension, proper use, easier maintenance, long-term robustness, and long-term survival of the integrity of the design.
What about non-technical people?
Honestly speaking, non-technical people don’t want the details. Its best to develop short high-level presentations which helps explains the main concepts.
Isn’t excess commenting bad?
Some argue that commenting ought to be done lightly — that if you use self-documenting names and consistent conventions you don’t need many comments. They may even push the point and say that they degrade readability and that they decrease maintainability because the code and comments can get out of sync.
Yes, you should always use self-documenting names and consistent conventions. The question is whether or not integrating the design and code is a good idea. To me, it’s clear. I should be able to open a source file, and with a glance identify its purpose, determine how to use it, and quickly orient myself, and then safely and quickly augment it. Integrating the code and the design allows this.