In this article I will talk about my own developments, which I tried out in practice, in order to bring more clarity to the development and operation of the code.
For the last couple of years I have been working in a large project and came across a pattern that leads to total code obfuscation. A code that has been developing for twenty years by a team of about a hundred people can hardly be called a code without epithets. Rather, it is a mountain for ten million lines of all sorts of right and wrong techniques, clever ideas, tricks, patches, quick-and-easy copy-paste, etc., etc.

In the organization where I work, they automate business processes, and this is usually associated with database maintenance. It’s customary to work according to the canons, to first carry out business analysis, make smart TK, write code, conduct testing and a lot of other activities with a lack of time. The primary motivation, like a reasonable one, is “let's divide the duties”, “let's do it so that it is safe” and so on and so on. All these management techniques with enthusiasm teach at various courses, promising a lot of hyip and okhmuryanta. I hope that the reader is already familiar with some fashionable words, which are often neither touched nor poured. But the question is not about them, but how to programmer to live with them.
')
Next, I will try to explain the difference between “business logic” and “strict logic”, which for some reason many people do not pay enough attention to. As a result of the frantic use of business, logic simply suffers from the logic for which mathematicians and philosophers have fought for hundreds of years. And when real logic suffers, then at the same time technicians-performers suffer first, who can begin to sculpt the absurd from hopelessness, so that the bosses can get rid of them. And then a boomerang of suffering returned to the source of “bright super business ideas”, forcing them to come up with other even more “bright super business ideas” or in the end wear a false beard and dark glasses so that no one else would recognize them on the street and did not point fingers.
What special effects I constantly meet at work:
- The subject area is thoroughly known to few and even less can clearly articulate for the programmer.
- The customer may make conflicting demands, and bringing him to clean water leads to loss of patience and confrontation.
- The contractor seeks to perform the TK, pass the tests and send the code to the battle with a lack of time and pressure from the business. The beauty of the decision after a while, few people care about, so how to break the head break.
The result of using the correct logic is the absence of contradictions or the timely detection of these. At least my technical mind believes it. But it is difficult to make the customer think and speak as the programmer would like, therefore we will look for a constructive compromise at the program level.
Let us turn to the specifics and analyze a simplified example. Let's turn on the imagination and suppose we maintain a base with contracts. Suppose that in the database are conducted contracts type 1 and type 2 for physical. individuals and for legal entities. On top of the programmer, the requirements of TK 1 are dumped with the words
“ 1 1”
at the time the contract enters into force.
It does not matter, the programmer makes this code number 1:
if( doctype==1 ){ do_something1; }
After some time, another requirement from TK 2 comes down:
“ . 2”
also at the time it enters into force. But the trouble is - the old programmer has already spread to another direction and TZ 2 has already been assigned to another programmer, who did not have time to penetrate into all the nuances, so he, too, is simply writing code number 2:
if( clienttype==ORGANIZATION ){ do_something2; }
Now we move from business logic to real logic and draw a combination matrix that clarifies what is happening.
Table 1
| legal faces
| physical faces |
contract type 1 | do_something1 do_something2 | do_something1 |
contract type 2 | do_something2 | |
We get 4 cells and see that in ¾ cases the program will work without questions. And only in one case there is a collision - both actions are performed. Good or bad depends on the conditions. Sometimes it can be good, and sometimes it is bad, but I would like to check.
In a more complicated case, when there are more types (see table 2), the notorious generalizations “for all types of contracts” or “for all types of clients” look like lines or columns in a combination table. And collisions are formed at the intersection. In Table 2, a collision occurs in one of nine cases. The larger the project, the more these lists, and the more difficult it is to find a needle in a haystack. I remember a joke about a gopher in the field: “See the conflict? No, I do not see. And she is! ”.
table 2
| customer type 1 | customer type 2 | customer type 3 |
contract type 1 | do_something1 do_something2 | do_something1 | do_something1 |
contract type 2 | do_something2 | | |
contract type 3 | do_something2 | | |
The more complex the project, the larger this matrix, and with time it grows and grows. Therefore, for clarity and honesty of the analysis, one more type should be added - the “new type” unknown at the moment. The future generation will definitely face it, and what it will mean - most likely, no telepath will tell.
Table 3
| customer type 1 | customer type 2 | customer type 3 | new type customer, possible in future |
contract type 1 | do_something1 do_something2 | do_something1 | do_something1 | ? |
contract type 2 | do_something2 | | | ? |
contract type 3 | do_something2 | | | ? |
new type contract, possible in future | ? | ? | ? | ? |
After competent drawing of combinations, the methodological flaw in the naive approach to writing code will become clear. You can figure out for yourself how a program of two lines above will interfere with business in difficult situations.
In the naive code there are no answers the following questions:
- What to do for new types? What is the guarantee that they will fit into the old algorithms? How should the program respond to an unfamiliar situation?
- What if action1 and action2 are mutually exclusive?
- How to keep knowledge of the subject area and how to formulate TK so that there are no contradictions?
The first question I tried to cover in the article [link]. Briefly, I proposed to introduce type lists into the code, which it has the right to process and signal the appearance of a new type, should a new type suddenly fall into the subroutine. By the way, I also tried this technique on the project, and it gives its results, adding clarity to the process of operation in uncertain situations. (update: reception present in defensive programming)
Now I want to highlight the second and third questions - how to determine the contradictions in a timely manner automatically. After all, when a project crosses several million lines, it is difficult to add a new code so that it does not spoil the old one. In fact, I propose to attach to a business object a kind of smart container, where to enter all the requirements. And this container itself will check, how much the new TK items get along with the old ones. That is, when a new TK item arrives, the programmer stupidly shifts it “as it came to” to the programming language, while preserving the underlying business logic. The effect should be such that if you look at the resulting code and the TK, you should understand without much effort, how they are related. He then puts the code in a smart container, which is the link between business logic and strict logic. As far as possible programmatically - I bring to the readers. Next, I describe my decision.
To do this, first I propose to separate the calculation of the conditions and the actions themselves. That is to space apart if-s separately from do_something. When calculating conditions, it is prohibited to make any changes to the database or to global variables. This is closer to functional programming and pure logic. Nothing changes in the external context, but only logical expressions are evaluated. Softer links are being introduced between the conditions of the TK and the actions of the program. At the first stage, the container forms the text - easily interpreted by both a person and any program.
At the exit at the first stage, the container issues:
- The main miniscenary in the form of a small text or a simple data structure.
- Additional warnings and recommendations in the form of text.
- Error in the form of text or excludes exceptions depending on the wishes.
The formation of the miniscript looks like this:

Figure 1. Diagram of the first stage
After the mini-scenario has been formed, it turns out that the large generalized texts of the TK are reduced to some kind of compact readable text that is suitable for a particular business object (contract, client, document, etc.) in its current state. This text is closer to concrete actions in the database and elementary operations, but nevertheless it is rather abstract and does not depend on the base or environment. Further, in the next steps, you can do a lot of useful things with it before the script makes changes to the database.
Which of these pros are expected:
- You can play with this container as you like, since it does not change the data, but returns only text. You can drive it all over the base, without fear of changing anything. You can do regression testing by comparing the new and old texts of miniscripts. You can make a prediction - what the program will do in this or that case.
- A useful principle of weak communication is realized, when pure logic and pure changes are distributed to different subprograms.
- If to draw a functional, then through the configuration you can turn on or off some blocks. Particularly relevant when there is no access to the fight, but you need to disable some buggy pieces.
- Texting can be done in the native language, which increases understanding for specialists of other specialties - testers, business analysts, users, and so on.
- The most important thing I would like to tell is the automatic control of contradictions, which I will describe below.
What are the cons:
- It is necessary to calculate all the conditions - both new and old, that is, over time it will work slower, but it is correct. We move away from premature optimization and recall the words of Donald Knut, "Premature."
- In programming languages, there are still no concise and beautiful constructions that would force a programmer to write correctly on a pattern. Potentially, it is still possible to go against the pattern and introduce inconsistent logic where it should not be. That is, it requires some discipline from the developer to add new TK conditions according to certain rules in a certain place.
Go to the description of the implementation. Input parameters are (Figure 1):
- The data identifying the business object. In the simplest case, it may simply be a link to a java object or an object ID in the database. But since this function can be called from very different places, including from where there is no direct link to the object, in practice I made a universal parameter, and in the first block (Fig. 1) I searched for the object.
- The second parameter is the command. For example, “set the number of boo. accounts "or" set the next status "and TP. The team is designed to optimize and increase flexibility. I didn’t need to practice it, as I always gave out a complete minicript on the current object, and then the minicript handler decided what it needed to execute depending on the situation.
In the following blocks, all conditions are calculated for all TK without optimization and logging. I only used logs on the testing server to quickly respond to tester comments. In the logs I wrote the phrases sent to me from the TZ, so that there was something to refer to when analyzing flights. That is, I used this approach:
bool1 = contract.type_id == 1; log(“ 1 =1 ” + bool1.toString()); if( bool1 ){ add_miniscript(“ ”,“50”); }
How exactly to detect contradictions and collisions - probably many have already guessed. The associative array key = value comes to the rescue. Key is a command, and value is its parameters. Setting an attribute value more than once, especially if the values are different, is an infamous case of contradiction. The code below has more details.
Application infrastructure has the following form:

Figure 2
I do not reveal all the nuances that I managed to face. I implemented the very first version of this behavioral pattern in Oracle PL / SQL without involving OOP. That is, the pattern can be considered universal and applicable to both the PLO and the procedural style. At the very first hard TZ, which consisted of 15 items, I identified 4 contradictions in the first week and a couple more in six months, which surprised the analysts who did not even think that their words could turn out in certain cases.
For those who are not yet tired of reading, the following is a Java implementation that can be run, and it will issue a detected collision to the console. The mini-script is implemented in the form of an ordered list, where the elements of the script are added - the {key, value} and comment pairs. The list is selected due to the fact that in practice it is necessary to allow conflicts for some keys. For this, there is a piece in the code that can turn on and off the handling of collisions. Detailed comments in the code. Should work if copied to the development environment and run.
file: testMiniscript.java
package test; import java.util.ArrayList; import java.util.List; import java.util.Optional;
At this I would like to finish the post, I will be happy with the comments and own developments in this area. Hope the material was helpful.
PS: Some readers may wonder, why did I call a pattern, not a technique, not a method, not a pattern, not a technology or something else? I think that there is no strict difference in the names, so we need to stop at something and somehow call it. Let's evaluate the practical benefits.
Additional information from Wikipedia:
Bussiness rule management systemIoCRybakov D.A.
Ph.D., November 2017