Programming is a fight against the complexity
When a program is being developed its complexity may increase or remain the same. We can define the program complexity as the complexity of its most complex component (the component decomposition can be seen as a separate component itself). It would be an interesting question what kind of program modifications increase the complexity and what do not.
Complexity multiplies but capacity does not sum up
A number of people cannot create a program which is more complex than at least one of these people can understand. From the other hand, linearly adding a new component of lesser complexity that already exists does not increase the overall complexity. Keyword here is “linearly”, which means that complexity of the decomposition (referring to the level on which the component added) does not increase as well, or increase so as not to outweigh the overall complexity. But making the new component interdependent with existing components opens new dimensions in internal states of the program (non-linearity) that is multiplying the complexity.
Each character of the code has its own path to the top level concept of the program. Top level concept of the program lies on the abstract layers braking up the program architecture into independent components, which are lying on lower level layers and so on until we reach each separate character of the code.
Understanding this path from the bottom level to the top level is architectural awareness. It is important that the programmer has one. Otherwise the complexity of the product can grow incredibly fast.
Push, pop, and pull requirements
The requirements evolve from the customer to the final coder. It is not possible to imagine that the programmer can develop a program targeting each customer requirement individually.
The requirements can be generally grouped into 3 categories.
- Customer requirements (Pull). Something which defines the product.
- Architect requirements (Pop). The model which can be implemented in software and is proven to mimic the customer definition of the product. These requirements are conditions to components and their interaction.
- Code requirements (Push). Testable functionality of the program.
Push requirements are satisfied when the program undergoes a number of scenarios defined the requirements. This can be done by auto scripts validating expected output.
Pop requirements are satisfied when the program conforms to the defined component interfaces. This can be done by documented and frozen APIs.
Pull requirements are satisfied automatically given that the model is adequate to the required product. These can be customer acceptance tests, but they would not be a part of the product development.
Failure to break the requirements into these 3 categories and treat the customer requirements as the requirements to the software leads to ad hoc style programming. Programs written in this way are difficult to modify and they are sources of plentiful hard tracking bugs. This can work only for small programs.