Building Maintainable Software
Maintainability is not an afterthought, but should be addressed from the very beginning of a development project. Every individual contribution counts.
Some violations are worse than others. The more a software system complies with the guidelines, the more maintainable it is.
Write short units of code
Shorter units (that is, methods and constructors) are easier to analyze, test, and reuse.
REFACTORING TECHNIQUE:
EXTRACT METHOD
REPLACE METHOD WITH METHOD OBJECT
Write simple units of code
Units with fewer decision points are easier to analyze and test.
Dealing with Conditional Chains
IF-THEN-ELSE -> switch
-> map<> -> Replace Conditional with Polymorphism
Dealing with Nesting
Write code once
Duplication of source code should be avoided at all times, since changes will need to be made in each copy. Duplication is also a source of regression bugs.
The Extract Superclass Refactoring Technique
Keep unit interfaces small
Units (methods and constructors) with fewer parameters are easier to test and reuse.
Limit the number of parameters per unit to at most 4.
Do this by extracting parameters into objects.
This improves maintainability because keeping the number of parameters low makes units easier to understand and reuse.
Separate concerns in modules
Modules (classes) that are loosely coupled are easier to modify and lead to a more modular system.
Avoid large modules in order to achieve loose coupling between them.
Do this by assigning responsibilities to separate modules and hiding implementation details behind interfaces.
This improves maintainability because changes in a loosely coupled codebase are much easier to oversee and execute than changes in a tightly coupled codebase.
Split Classes to Separate Concerns
Hide Specialized Implementations Behind Interfaces
Couple architecture components loosely
Top-level components of a system that are more loosely coupled are easier to modify and lead to a more modular system.
Achieve loose coupling between top-level components.
Do this by minimizing the relative amount of code within modules that is exposed to (i.e., can receive calls from) modules in other components.
This improves maintainability because independent components ease isolated maintenance.
Limit the size of modules that are the component’s interface.
Define component interfaces on a high level of abstraction. This limits the types of requests that cross component borders. That avoids requests that “know too much” about the implementation details.
Avoid throughput code, because it has the most serious effect on testing functionality. In other words, avoid interface modules that put through calls to other components. If throughput code exists, analyze the concerned modules in order to solve calls that are put through to other components.
Abstract Factory Design Pattern
Keep architecture components balanced
A well-balanced architecture, with not too many and not too few components, of uniform size, is the most modular and enables easy modification through separation of concerns.
Balance the number and relative size of top-level components in your code.
Do this by organizing source code in a way that the number of components is close to 9 (i.e., between 6 and 12) and that the components are of approximately equal size.
This improves maintainability because balanced components ease locating code and allow for isolated maintenance.
Keep your codebase small
A large system is difficult to maintain, because more code needs to be analyzed, changed, and tested. Also, maintenance productivity per line of code is lower in a large system than in a small system.
Do this by avoiding codebase growth and actively reducing system size.
Automate development pipeline and tests
Automated tests (that is, tests that can be executed without manual intervention) enable near-instantaneous feedback on the effectiveness of modifications. Manual tests do not scale.
Write clean code
Having irrelevant artifacts such as TODOs and dead code in your codebase makes it more difficult for new team members to become productive. Therefore, it makes maintenance less efficient.
Rule 1: Leave No-Unit Level Code Smells Behind
Rule 2: Leave No Bad Comments Behind
Rule 3: Leave No Code in Comments Behind
Rule 4: Leave No Dead Code Behind
Rule 5: Leave No Long Identifiers Behind
Identifiers that express multiple responsibilities or contain too many technical terms are always a violation of this rule.
Rule 6: Leave No Magic Constants Behind
Rule 7: Leave No Badly Handled Exception Behind
Always catch exceptions.
Catch specific exceptions.
Translate specific exceptions to general messages before showing them to end users.
Building Maintainable Software
Maintainability is not an afterthought, but should be addressed from the very beginning of a development project. Every individual contribution counts.
Some violations are worse than others. The more a software system complies with the guidelines, the more maintainable it is.
Write short units of code
Shorter units (that is, methods and constructors) are easier to analyze, test, and reuse.
REFACTORING TECHNIQUE:
EXTRACT METHOD
REPLACE METHOD WITH METHOD OBJECT
Write simple units of code
Units with fewer decision points are easier to analyze and test.
Dealing with Conditional Chains
IF-THEN-ELSE -> switch
-> map<> -> Replace Conditional with Polymorphism
Dealing with Nesting
Write code once
Duplication of source code should be avoided at all times, since changes will need to be made in each copy. Duplication is also a source of regression bugs.
The Extract Superclass Refactoring Technique
Keep unit interfaces small
Units (methods and constructors) with fewer parameters are easier to test and reuse.
Limit the number of parameters per unit to at most 4.
Do this by extracting parameters into objects.
This improves maintainability because keeping the number of parameters low makes units easier to understand and reuse.
Separate concerns in modules
Modules (classes) that are loosely coupled are easier to modify and lead to a more modular system.
Avoid large modules in order to achieve loose coupling between them.
Do this by assigning responsibilities to separate modules and hiding implementation details behind interfaces.
This improves maintainability because changes in a loosely coupled codebase are much easier to oversee and execute than changes in a tightly coupled codebase.
Split Classes to Separate Concerns
Hide Specialized Implementations Behind Interfaces
Couple architecture components loosely
Top-level components of a system that are more loosely coupled are easier to modify and lead to a more modular system.
Achieve loose coupling between top-level components.
Do this by minimizing the relative amount of code within modules that is exposed to (i.e., can receive calls from) modules in other components.
This improves maintainability because independent components ease isolated maintenance.
Limit the size of modules that are the component’s interface.
Define component interfaces on a high level of abstraction. This limits the types of requests that cross component borders. That avoids requests that “know too much” about the implementation details.
Avoid throughput code, because it has the most serious effect on testing functionality. In other words, avoid interface modules that put through calls to other components. If throughput code exists, analyze the concerned modules in order to solve calls that are put through to other components.
Abstract Factory Design Pattern
Keep architecture components balanced
A well-balanced architecture, with not too many and not too few components, of uniform size, is the most modular and enables easy modification through separation of concerns.
Balance the number and relative size of top-level components in your code.
Do this by organizing source code in a way that the number of components is close to 9 (i.e., between 6 and 12) and that the components are of approximately equal size.
This improves maintainability because balanced components ease locating code and allow for isolated maintenance.
Keep your codebase small
A large system is difficult to maintain, because more code needs to be analyzed, changed, and tested. Also, maintenance productivity per line of code is lower in a large system than in a small system.
Do this by avoiding codebase growth and actively reducing system size.
Automate development pipeline and tests
Automated tests (that is, tests that can be executed without manual intervention) enable near-instantaneous feedback on the effectiveness of modifications. Manual tests do not scale.
Write clean code
Having irrelevant artifacts such as TODOs and dead code in your codebase makes it more difficult for new team members to become productive. Therefore, it makes maintenance less efficient.
Rule 1: Leave No-Unit Level Code Smells Behind
Rule 2: Leave No Bad Comments Behind
Rule 3: Leave No Code in Comments Behind
Rule 4: Leave No Dead Code Behind
Rule 5: Leave No Long Identifiers Behind
Identifiers that express multiple responsibilities or contain too many technical terms are always a violation of this rule.
Rule 6: Leave No Magic Constants Behind
Rule 7: Leave No Badly Handled Exception Behind
Always catch exceptions.
Catch specific exceptions.
Translate specific exceptions to general messages before showing them to end users.
Building Maintainable Software