Behavior patterns

    (This note marks the end of a series of posts that included Technical Duty , Refactoring Syndrome, and Second System Effect. )

    What are the benefits of design patterns? (*) This is, first of all, the reuse of proven architectural solutions, as well as the simplification of communication between developers. But besides design patternsthere are many other patterns: there are patterns of coding, testing, code modification (aka refactoring), there are architectural patterns, and many others. Since we, in fact, rarely do anything truly new, proven standard solutions exist for a huge number of areas. And since most of the problems faced by the development teams are also not diverse, the behavior of these people is also very uniform.

    “Technical debt” , “refactoring syndrome” and “effect of the second system”- These are typical situations that the development team periodically encounters. And the main benefit from them is precisely to see the problem and prove its existence to the right people. If you yourself realized that the technical debt of the project is too great, then using the money metaphor it will be much easier to prove the importance of this problem to the manager or customer. And then carefully show him alternative ways of developing events: (1) to leave everything as it is; (2) reduce technical debt through reasonable refactoring; or (3) rewrite everything.


    Similarly, if you know about the problems of your developers, such as the desire for excessive rewriting or a tendency toward over-sophisticated solutions, then it will be much easier for you to use the available resources in the most optimal way. By combining developers in such a way that the advantages of one developer compensate for the shortcomings of another, you can reduce the risk of going to extremes.

    In addition to using the right people for the right tasks and reasonable pragmatism, there are a few thoughts that you should think about to reduce all kinds of risks and make inappropriate technical decisions.

    Risk Management and Encapsulation

    John Robbinsif you did not know before becoming a famous bugslayer served as a green beret in the United States Army; and although, as he himself admits, it’s difficult to recognize a good warrior in him, he learned to use some “tricks” from planning military operations in developing software products and managing risk.

    Yes, questions like “What will happen if Vasily gives the oak before he finishes the phase of analysis or collecting requirements?” (**) may be too tough and will hardly be met with applause from colleagues, but questions like “What if our current solution turns out to be wrong? ”can be very useful.

    For example, you choose a model of interaction with the database, an interprocess communication library, a DBMS or a library for creating a user interface. Each time, making such a decision, it’s not out of place to ask oneself, “What will happen if this decision turns out to be erroneous and we have to abandon it?” How to minimize the impact of such an error and what will result if it does not take off?

    The main idea of ​​minimizing the risk of such errors is to encapsulate such solutions in a minimum number of modules. This will minimize the number of modules that will have to be changed in the event of an error and will reduce both technical debt and the cost of subsequent changes.

    For example, if you decide to use WCF for interprocess communication, this does not mean that services should stick out of all holes and business logic should be located directly in service classes. Or, if you use a custom interaction protocol, whether it is a protocol for working with third-party equipment, manual implementation of RPC, etc., then you do not need to smear this information with a thin layer throughout the application. Or maybe you still like COM (although Don Box can't even look at it), you shouldn’t wrap every object in a COM object.

    It may seem so banal that it’s not worth talking about (well, or writing too), but, unfortunately, such examples come across too often to think that everyone knows about it.

    Инкапсуляция – это не только закрытые поля класса (т.е. сокрытие данных), но это и сокрытие информации и в более широком смысле этого слова. Так что инкапсуляция использования WCF в отдельном модуле является точно такой же инкапсуляцией с архитектурной точки зрения, как и закрытое поле с типом intв вашем классе, с точки зрения дизайна.

    Higher-level abstractions (such as assemblies / modules / any other crap of a higher level than classes) also consist of a public interface (abstraction) and an implementation (hidden details). The concept of abstraction and encapsulation, which describe the tip of the iceberg - abstraction, and its underwater part - encapsulating implementation details, apply to an assembly or module as well as to a separate class, even if they are usually applied less formally.

    Returning again to the interprocess communication module, we can single out the open interface of this interaction and hide WCF as much as possible as an implementation detail. I have no doubt that nothing will come out of this venture for 100% anyway, and according to the "law of leaky abstractions"information on low-level implementation details will be leaked to other levels or modules. But even an attempt to answer the simple question “What will happen if this crap does not take off and you have to give up the current solution?”, Should reduce the consequences if this crap really does not take off.

    One head is good, and two is a mutation

    There are tons of decisions that one person must make. It is unlikely that something traveling will happen if the designer runs around and asks all fifteen developers for an opinion and tries to please each of them. At the same time, it will turn out such crap that Windows 3.11 these days seems to be the height of design art and ergonomics. And if the application architect asks for the advice of everyone and everyone who has their own opinion on the architecture of the system (and, as you know, each project participant has it), the result of this work will also not take long.

    A collective farm management model, when a collective meeting decides which ORM to use, has many shortcomings; but the usefulness of the management model, in which technical decisions are made by one person without discussion, snot, drooling and appeals, is also very doubtful.

    It often happens that the team has the most experienced “pepper”, which alone takes all the important architectural (and not so) decisions. This situation is useful for this “pepper” itself (FAC, it is FAC), but not so useful for the whole project, especially if the “pepper” is not eager to ask others for advice.

    The first problem is that people are slowly developing a habit that the “pepper” is right, while the “pepper” itself also has such a habit. As a result, everyone goes with the flow, no one disputes important decisions, because his decisions are trusted. As a result of such an attitude, the quality of these decisions decreases.

    The second problem is that there are a number of decisions that simply cannot be taken on their own. I personally do not know more than one person to whom I would entrust the design of a corporate library (I would not trust this to myself alone) (***). The problem is that the concept of reuse, although it has been procrastinating for decades, is still one of the most difficult tasks in our field. When designing libraries that even a dozen people will use, you need to make completely different compromises and make decisions that you would never have taken when designing a regular application. Here, without “corridor testing” it is simply impossible, because what you think is simple and understandable to use will certainly not be so for a person whose brain is not in your head.

    If the team does not have full number 2, then this will certainly lead to a decrease in the quality of solutions for a very simple reason. Number one (our “pepper”) is stupid to talk to someone to check their decisions for lice, as a result of which the team can go the wrong direction for a long time, however, since the team is boiling in its own juice, it is very difficult to notice.

    In addition, an experienced “pepper” can convince the team and make a frankly wrong decision, simply thanks to their experience. If the interlocutors are in different weight categories, then it will not be difficult for a more experienced person to convince the interlocutor in their point of view, regardless of how adequate it is. In any dispute, such as what is better than C ++ or C #, there are a lot of arguments on each side and a more experienced “pepper” you can simply call one argument and hold back the others. As a result, a decision will be made in the interests of Pepper, and not in the interests of the project.

    In most teams, even reviewing someone else’s code is extremely rare, and talking about a review of a specification, architecture or design is generally not necessary. But it’s even intuitively clear that the sooner an error is found, the cheaper it is to fix it. The very idea that the look of an outsider falls on "my creation" further disciplines and improves the quality of the decision.

    Finita

    There are surely a ton of other ways that can mitigate the problems described in previous notes, but it seems to me that a few tips, although they will not solve these problems, can greatly reduce the risk of their occurrence. So, here they are in short form:

    1) Pragmatism rules (no extremes are needed); this advice applies always and everywhere, and it also helps to avoid problems such as “refactoring syndrome” and “second system effect”

    2) Do not cut down your decisions in stone (there is a chance that you have screwed up somewhere, and then you will have to tackle the chisel again)

    3) Use reasonable advice from reasonable colleagues, for a glance from the outside is always useful

    --------- -----------------------------

    (*) In addition to the benefit, design patterns can be harmful. The thoughtless and unreasonable use of any even the most useful tool or technology will lead to "absurdity and corruption." How many times have you encountered the “overengineering” problem when a dozen design patterns have been used to implement a simple concept? In general, this is another example of the fact that pragmatism and common sense, as always, are the best choice, and design patterns are no exception.

    (**) According to John himself, he asked precisely this question at one of his first rallies. Literally, his question was: “What if Bob dies before we finish the requirements collection phase?”, Boldly refer to the original for details: “Debugging Applications .NET 2.0 Applications” , section “The“ Code First, Think Later ” Approach. "

    (***) I'm not talking about the libraries of al MiscUtils by John Skeet . Firstly, his library "grew" in solving real problems, and secondly, I'm just sure that John made a lot of changes to it based on the feedback of colleagues and users. If you want to learn at least a little about the complexities of designing large libraries, browse through the “Framework Design Guidelines” Abrams and Kvalina, there are a lot of interesting thoughts on this subject.

    Also popular now: