Fight the difficulty
As you know, the human brain can simultaneously consider 7 ± 2 elements. Therefore, it is very important to strive to reduce software complexity. Here are some specific guidelines:
- Divide the system into subsystems at the architecture level to concentrate at any given time on a smaller part of the system.
- Define class interfaces carefully so that you can ignore the
internal structure of classes.
- Maintain the abstraction formed by the class interface so that you don’t
remember unnecessary details.
- Avoid global data because using it significantly increases the percentage of code that you need to keep in mind at any given
- Avoid deep inheritance hierarchies because they place
high demands on intelligence.
- Avoid deep nesting of loops and conditional statements, as
they can be replaced with simpler control structures that allow you
to spend more mental resources.
- Avoid goto statements as they introduce non-linearity into the program,
which is difficult for most people to follow.
- Carefully determine the approach to error handling, instead of using an arbitrary combination of different techniques.
- Systematically use the built-in exception mechanism, as it
can become a non-linear control structure, which, with unruly use, is almost as difficult to understand as goto statements.
- Do not let classes turn into monsters, reaching the size of entire
- Keep your methods short.
- Use clear, obvious variable names to avoid recalling details like “xx is the account index, and yy is the customer index or vice versa?”.
- Minimize the number of parameters passed to the method, or, more importantly, pass only those parameters that are needed to maintain the abstraction formed by the method interface.
- Use conventions not to remember arbitrary, inconsequential differences between different pieces of code.
Analyze the development process
In projects implemented with the participation of more than one programmer,
organizational characteristics rather than individual skills play a more important role. Even
if you have an excellent group, its collective ability is not equivalent to the sum of the
abilities of the individual members. The process used by the group is what determines whether the work of a particular programmer will increase or decrease the efficiency of the remaining members of the group.
The development process is important primarily because quality must be built into the software from the very beginning. This contradicts the long-standing “wisdom” according to which you can write arbitrarily bad code and eliminate all errors at the testing stage. It's a delusion. Testing can only point to individual
defective areas of the program - it will not make your program more convenient to use, faster, more compact, readable or expandable.
Premature optimization is another flaw in the development process. An effective process implies that you do rough work at the beginning and fine work
at the end. If you were a sculptor, you would give the composition a general shape
and only then would you begin to work on individual details. Performing optimization prematurely, you spend time polishing pieces of code that
you don’t need to polish. Always ask yourself: “Am I doing this in the right order? What would change if the order was changed? ”
Write programs primarily for people and only secondly for computers
Computers don't care how readable your code is. They generally read binary commands better than high-level language operators. Human-readable code needs to be written so that it can be understood by people. Readability has a
positive effect on such aspects of the program as:
- ease of review;
- error rate;
- debugging convenience;
- ease of change;
- development time is a consequence of all of the above;
- external quality is a consequence of all of the above.
Human-readable code is no longer written than confusing - at least in the distant future.
Program using a language, not a language
Do not limit your thinking to only those concepts that are directly supported by the language. The best programmers think about what they want to do, and then determine how to achieve these goals with the programming tools at their disposal.
Concentrate with Agreements
General benefits of agreements:
- Agreements save programmers from having to answer the same questions again and again and make all the same arbitrary decisions.
- The Convention succinctly provides important information.
- Agreements protect against known dangers.
- Agreements make low-level tasks more predictable.
- Agreements can compensate for language flaws.
Program in terms of the problem area
Another specific method of dealing with complexity is working at the highest level of abstraction. One way to achieve this goal is to work in terms of a programming problem, not a computer solution to it.
High-level code should not include detailed information about files, stacks, queues, arrays, characters, and similar objects that have names like
i, j, and k. High-level code should describe the problem being solved. It should
be filled with descriptive class names and method calls that clearly describe the actions to be performed, and not detailed information that the file is
opened in read-only mode. The high-level code should not be
cluttered with comments stating that “here the variable i represents
the index of the record from the file about employees, and a little later it is used to index the file of customer accounts.
This is a clumsy programming technique. At the highest level of the program, you
do not need to know that data about employees is presented in the form of records or stored in a file. Information related to this level of detail must be hidden. At the highest level, you should not have a clue about how data is stored. You should not read comments explaining the role of the variable / and that it is used for a double purpose. Instead, you should see two variables with expressive names, such as employeelndex and clientlndex.
Division of the program into levels of abstraction
Obviously, at a certain level it is necessary to work in terms of implementation, but
you can isolate these parts of the program from parts developed in terms of the problem area. When designing a program, consider levels of abstraction:
- features of the operating system and machine instructions;
- structures and means of a programming language;
- low-level implementation structures;
- low-level elements of the problem area;
- high-level elements of the problem area.
Beware of falling stones
Whatever programming is - art, craft,
or engineering discipline, creating a working program requires a fair amount
of prudence. And for this you need to pay attention to a wide range of warning signs - subtle hints of problems in your program. Warning signs in programming indicate possible problems, but usually they are not as obvious as a road sign warning of rockfalls.
If you want to take full advantage of the warning signs, program to create your own warnings. This is useful because, even if you know the warning signs, they are surprisingly easy to overlook.
Iterate, iterate and iterate
Iteration is useful at many stages of software development. When developing the initial specification of the system, you make several versions of the requirements with the customer until you reach an agreement. This is an iterative process. A flexible development process involving the creation and delivery of a system in parts is also iterative. Prototyping, with the goal of quickly and cheaply developing preliminary solutions, is another form of iteration. Iterative development of requirements is probably no less important than any other aspect of the software development process. Projects fail because developers proceed to solve the problem without exploring alternatives. Iteration allows you to better know the system before creating it.
Estimates of the timing of initial project planning can vary greatly depending on the evaluation methodology used. An iterative approach provides a more accurate estimate than a single technique.
Software design is a heuristic process and, like all heuristic processes, allows iterative revisions and improvements. The correctness of the software is usually checked, but not proved, which means that it is tested and developed iteratively
until all questions are answered correctly. Both high-level and
low-level design attempts should be repeated. The first attempt may lead to a workable solution, but it is unlikely to be the best.
Repeated application of different approaches allows you to learn a lot about the problem
one that eludes when using a single approach.
The entire development process is covered by reviews, which embeds iteration in any
stage at which they are conducted. The purpose of the review is to verify the quality of work performed at the moment. If the system does not pass the review, it is returned for processing. If the review succeeds, no further iteration is needed.
They say engineering is the ability to do for 10 cents what anyone can do for a dollar. Iterating at later stages is a waste of two dollars on what anyone can do for one. Fred Brooks advises "plan to throw away one version of the program, because it will have to be done anyway." The trick of software development is to create discarded parts as quickly and cheaply as possible - this is the essence of iteration in the early stages of development.