I had attended a session on design and architecture recently. One of the topics of discussion was on how design cannot be implemented strictly during the coding stage and so is it really worth spending too much effort on design. This got me thinking. I come from a civil engineering background where nothing gets done right including the estimate, contract terms and execution unless the design is thorough.
I mean you can’t build a new and complex machine/bridge/railway system without a design in place. Design is where the requirements become tangible. The real weakness is that we don't come out with clear design and the shortcomings usually show up while coding. It should be clear to the developers who have to churn out the code. Just drawing some UML diagrams to explain how issues are handled in generic terms is not enough. 'A picture says a thousand words'. But somebody has to now clearly understand it and write those words to recreate the picture in script, plus some.
So I decided to focus on how best to improve the design phase of software development rather than treat it as a necessary evil.
It's true that, in many projects by the time of coding, there is usually a lot of disparity with the design. This happens mainly because we focus only on providing a high level design but never focus on what the developer needs to achieve the expected output. To solve this issue, what we need to do is put more details into the design and not less. The fine detailing (ask a building architect what this means) should be done.
To achieve a pukka design, the real need would be to find a way to test the design and prove that it implements the application requirements and the coding requirements. After all, we test our code so why not the design too. Perhaps the low level design could be compared against the test cases.
To ensure a more complete design, try some of the following in a suitable sequence:
- Put in the pseudo code.
By doing so, the designer is actually trying to implement the design. During this process, shortcomings in the design from the coding point of view could be realized. Even programming bits like class fields, properties, constants, enumerators, helper/private functions to do some specific tasks all could be identified while doing the pseudo code. In fact, during coding stage the developer would mostly write language specific code to implement the pseudo code.
This means we now have a two stage design process. First, do a high level design to implement the general architecture. So design patterns, identifying domain entities, relationship between domain entities and all the normal design work is done first. Then an experienced developer would take the design further by trying to implement the pseudo code for it. This can be considered as a kind of unit testing.
- Create a traceability matrix
Map each requirement/specific rules to our design classes/methods. This will help ensure that most issues have been considered in the design. It will also help identify what was left out. Create flow charts and map each node in the chart to your design elements. Again this could be a two stage process. First, the high level business related flow charts are created and the high level design (abstract classes or interfaces) is mapped to it. Then the lower level flow charts are created and specific concrete classes/methods are mapped to it. This can be thought of as a kind of integration testing.
Yes, this is a cumbersome process but it does clear lot of confusion when writing the code for them. So every class, variable, property, method and event's existence/usage is explained so that the coder knows exactly what to do with it. In this way even developers new to the project could get going without having to know in detail the business process and project specific nuances involved.
- White box testing
Explain the classes support the workflow/process to your peers and developers involved in the project. This way the designer gets a better understanding of the developer's expectations.
- Black box testing
Have people who haven’t got much information about the project to read your design, explain what they have understood from it and how they would implement it.
- And last but not least, make sure that the coding does not include anything significant that isn’t reflected in the design.
The design document should be the only reference for coding. Even usage of other coding components, DLLs that are part of the project must be mentioned in the design by referring to the specific design document.
Of course, to ensure that the code does what it was required to do can only be proved during testing. A test driven development process would ensure that your output is right (it doesn’t ensure that you developed it the right way). To take that further, if test driven code was the best way to do things then why not code driven design, design driven requirements and requirements driven customers.
Changes to requirements and design do happen all the time and have to be accepted (within limits). Use UML to explain the design and put in the pseudo code. And pair designing would give greater value than pair coding.