Undesired complexity give undesired deliveries
There are many aspects to consider related to DevOps and delivery, many strategies and (in the right context and the right approach) there are also some very useful tools. It is a complex issue and last thing that we want is to make that even harder because of undesired complexity. A poor and an un-appropriate design is the main cause of the problems.
Suppose that the main aspects of a product functionalities and design are the followings:
- Functionalities: f1, f2, f3 …
- Business rules: r1, r2 …
- External frameworks, technologies, and drivers (APIs, hardware interfaces, others) e1, e2…
- External interfaces: i1, i2,
- Technical mechanisms: t1, t2, t3..
Should stay together only what it changes and should be deliver together.
As generic rule, if one of this aspects it is changed, we do not want to affect, change and deliver others aspects. Below are some examples of anti-patterns.
Anti-pattern: Lack of functional cohesion
Just a start example: If was requested to deliver a change for the functionality f1, it is highly undesired to change also functionalities f3 and f4 only because f1, f3 and f4 are unnecessary coupled in the implementation. This coupling could mean: same function, same class or same package as opposite of using distinct design elements.
Anti-pattern: Non-DRY business rules
If the business rule r1 it is used in more functionalities (that have dedicated components) and that rule implementation is duplicated (multiplied) in every functionality, then a change in r1 will require change and delivery for all those functionalities and components.
Anti-pattern: Mixing functionalities with external communication
Suppose that f1 and f2 are using TCP sockets for communication with some external systems and we need to replace this type of communication, if the f1, f2 implementation is mixed with socket management aspects, then we need to change and deliver also f1 and f2.
Anti-pattern: Mixing functionalities with external interfaces
External interfaces could suppose handling of some specific external data structures and/or of some specific communication protocol. If we are mixing these parts with internal functionalities, with any change in the interface, we should change and deliver also some not related internal parts, .
Anti-pattern: Mixing functionalities with technical aspects
You need to protect the representation of the business inside the product from the changes related to technology. Where is the technology involved? Any I/O aspect that wrap the hardware: GUI, network, databases, file systems is strongly related to technologies and platforms. If, for example, you have a lot of functionality and business representation in the design of the GUI elements, any change in GUI technologies will affect the business representation inside the product, that should also massively changed and then delivered.
A symptom of a poor design that could cause undesired supplementary work on development and delivery are the “utils” packages, especially without clearly dedicated sub-packages. Examples:
- “utils” package that mix together classes belong to different functional aspects
- “utils” package that mix various technical aspects without having dedicated sub-packages
This kind of design suppose that we need to change, test and deliver the “utils” almost with any change of the product.
Anti-pattern: Dirty code
Breaking simplest Clean Code rules (similar with the ones that are used in refactoring) could cause undesired coupling and undesired increase of delivery scope. Some examples that could induce such problems:
- Any duplicate/multiplied code
- Breaking SRP principle
- Global context data, global variables (breaking Law of Demeter)
Minimizing the need of change and delivery
We need to reduce the need of change and delivery only to the required ones. There are several practices and approaches that could avoid that problem (the examples presented above or similar ones):
- Keep the code clean by writhing Clean Code first and refactor and pay the technical debt whenever it is necessary
- Use functional cohesion as the main criteria of creating components and packages
- Use Clean Architecture that propose a default, strategic separation of concerns
- Extend XP engineering practices (Simple Design, Refactoring, TDD) with the ones from Agile Modeling and DAD
- Respect Law of Demeter on any level of the design – do not use any global context
- Make the products adaptive by keeping up-to-date with target business by often and early injection of feedback from that business.
You can argue that I already write about all these in a previous post “Roadmap to an Agile Design”. Yes, indeed, in order to deliver just what it is needed and avoid waste you must have an Adaptive Design, the main characteristic of an Agile Design.
In order to that you should be open to all outstanding agile practices for design and do not be closed in the smaller universe of some very lightweight Agile methods. And remember that those lightweight methods are not created as full process methodologies, but as indications to build a customized process.
Imagine that all these problems are far worse if we have multiple variations and variants of the same product. The massive effort for any change and delivery will block the overall agility and responsiveness of the development team and massively reduce the overall economics for both development and customer side.
Use a strategic separation of concerns
Do not use any reference to any global context