Design for the Ages
The design phase of a software system, assuming it is done at all, is your major opportunity to ensure that you translate the system needs into a product that can adequately address all of those needs in a predictable fashion. If this phase is neglected, the likelihood of achieving the projects goals diminish dramatically – there is no cohesive architecture on which to place all the required functionality, there is no coordination in design that simplifies and streamlines the development, and there will be surprises downstream that threaten the completion of the project itself. Done well, a design takes into account the past, present, and future of the product.
Design with the past in mind. History is the best teacher of what works well and what doesn’t in your products. Learning from that experience is critical for identifying the elements of your design that require shoring up – don’t continue to place band-aids on that pesky element that keeps breaking, but step back and understand the design flaw that allows the problems to continue, and fix that design flaw. The opportunity to learn from the past is a major driver for the adoption of an iterative lifecycle, as it provides several chances for you to exercise design elements, often without the embarrassment of having your customer find your mistakes for you. Don’t simply iterate to break functionality into chunks; iterate to remove design risks from your system.
Design with the present in mind. If any design is done, it usually centers on the functionality of the current version of the system, but there is more to consider here. Many of the quality attributes need to be designed into the system to be reasonably implemented, such as portability and security – it is far too painful to allow these to be built into the system in an ad-hoc manner. If testability is an important attribute of your system, the test infrastructure (both within the system and the external supporting elements) should be identified and scheduled in for development at this design time. It is at this point that you can best leverage existing patterns and frameworks to streamline the development of the system, and if done correctly, a well thought out design will support strong reuse, even across product lines.
Design with the future in mind. Many products live beyond their current incarnation, and they grow in ways that can be quite predictable in some areas and unfathomable in others. Certainly consider the anticipated future functionality of the system as you design the current version by building in the requisite hooks and common mechanisms that will facilitate the inclusion of new scope more easily. Following standard design concepts such as abstraction, modularity, encapsulation and cohesion will create a system that more naturally accepts new functionality that has not been anticipated as well. The future is supported with a system that is more maintainable, reusable and more easily modified as well.
Good design is rarely as simple as the translation of specified functional requirements into a logical architecture, communicated through a series of UML diagrams, which is then further translated into code, tested, and shipped. A good design is structured to improve upon the past, implement the present, and anticipate the future of the product. – JB