Big Design Up Front (BDUF) is considered a very bad practice in Agile software development. BDUF is problematic because it assumes frozen requirements. The Agile approach is that software must be developed iteratively since the requirements will change and the system will evolve. So we need some Design Up Front, but this must be Adaptable design that supports change: ADUF.
Adaptable Design Up Front
We should never work on the design of a specific system. Instead, we should generalize, designing for a family of systems. Applying object-oriented concepts, our design should represent a class of systems, and each version during the system evolution should be an instance of this class. Thus each new version may have modifications and extensions when compared to previous versions, but if it still fits the same generic design, then it is still an instance of the same class.
Adaptable Software Design: A generic software design for a family of systems which does not need to be changed to accommodate new requirements.
The challenge of creating an adaptable design is making it change-resilient. Adaptable designs should evolve and accommodate new requirements through specialization. When designing for a family of systems, there are two main such approaches: The framework approach and the platform approach.
Framework: “A software framework is an abstraction in which software providing generic functionality can be selectively changed by additional user-written code, thus providing application-specific software.”
The framework defines the system structure: The main system components and the relationships among them. Each one of these framework components should have an abstract interface, and several possible implementations for these interfaces. If the design is sound, the components implementation may evolve over time, but the relationships among them should be preserved.
Each component in a framework should be designed to be highly cohesive, and the framework itself should be designed so that components are weakly coupled. If the component interfaces are indeed abstract and if the coupling among these components is weak, each type of component in the framework can be seen as a kind of plug-in.
Plug-in: “A plug-in is a software component that adds a specific feature to an existing software application. When an application supports plug-ins, it enables customization.”
If we are able to define such framework with diverse plug-ins, the system evolution occurs through the implementation of new versions of components. Thus, instead of having a new version of the entire system, we may have new versions for each one of the plug-ins. Building the system means simply combining these plug-in versions in an appropriate configuration.
Platform: “Technology that enables the creation of products and processes that support present or future development.”
A platform should support the basic services which are required by the system to provide its functionality. When these basic services can be well-defined and implemented independently of the system which will use them, we have reached an ideal environment for adaptability:
- The platform may evolve independently of the systems that are built on top of it. If the interfaces are kept intact and if the behavior is preserved, the platform developers are free to improve its efficiency and other non-functional attributes.
- Several systems may be built on top of the same platform, using its services to provide diverse functionalities. These systems may each evolve independently of the others, and independently of the evolution of the platform itself.
Building platforms is a very effective approach for experimentation. If an essential part of the system behavior can be captured as basic services being provided by the platform, then each version of the system may be seen as an experiment and the cost of failure is low. The resources invested in the development of the platform are not lost in the case one of the system versions is not deployed.
Adaptable Software Design in Practice
Question: How do you identify the components in your framework or the services in your platform?
Answer: We want to think generally about a family of systems, and avoid defining requirements for a specific system. To identify the main entities that should compose any system in this family, we should apply domain modeling.
Domain Model: “A domain model is a conceptual model of all the topics related to a specific problem. It describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain.”
Question: How do you decouple the components in your framework?
Answer: To reduce coupling, one should look for appropriate Design Patterns. Most of these patterns are related to avoiding the direct links between concrete classes, through the introduction of interfaces and abstract classes.
For example, when instantiating an object, one should avoid creating an instance of some specific concrete class. This can be done using the Factory pattern.
Software systems must be able to evolve over time. This evolution should be planned and supported trough Adaptable Software Designs. There are two main approaches for creating such designs: Frameworks and Platforms. Both require the identification of domain entities and the usage of abstract interfaces to minimize coupling between components.
What do you think? What is your personal experience with software evolution, changing requirements and adaptable designs? Please share your thoughts in the comments below.