For more than a year now, in MyStore, we have been creating functionality that helps our users buy and sell labeled goods. The news about labeling has slipped many times on Habré, so briefly: since 2019, goods are marked necessarily. Not all at once, but now you need to label cigarettes, shoes, perfumes and car tires. At the same time, we work in a situation of uncertainty, when the API of government systems is constantly changing.
Therefore, we have only two ways to integrate with government agencies: wait until everything settles down and miss market leadership or develop a system in a world of changing requirements.
We have chosen the second - just in the spirit of flexible methodologies. I think Agile can really help in solving applied problems. And life in a world of ever-changing demands is the field in which you can turn around.
My name is Maxim Sukharenko, I’m team lead of the MySklad service platform. And today I will tell you how we work with changing requirements.
The initial deadline for the development of the system by the regulatory authorities was planned for April 2019, but you understand everything. As a result, the deadlines were moved to October. And where there are new deadlines, there are new requirements and new formats. We could not wait for the API to be fixed - then we would not have time to implement the integration. Therefore, they decided to "hesitate with the party line."
We thought about the classic approach that offers us to make an adapter for an external system. Supplement it with a plug with a switch and from time to time pull up the functionality of the plug and adapter to the current state of the external system.
Logic suggests that in order to develop a system with a constantly changing interface, you need to fix the requirements at some point in time. But only on what? Until we develop some part of the functionality?
Agree, it’s unpleasant to leave unfinished functionality on which tests do not pass, and also start breaking it on a new one. You can fix the requirements for the feature development period. But what to do with big features that can live in development for several months? It is simply impossible to fix requirements for such a long period.
So we turned to Scrum. He tells us that by the end of the sprint we must provide a working product with new functionality. But how does this fight with fixing requirements and big features? There is a good joke on this subject:
“Doctor, when I’m doing it like this, it hurts me.”
- And you just do not do it like that.
Who does not understand, do not make big features . You can fix the requirements for one or two weeks in order to coincide with the sprint, and set the implementation of the functional according to the requirements for the purpose of the sprint. And the functionality must be beaten into pieces so that they fit into one or two sprints. Think too tricky? And then! You just don’t know how to cut tickets. Saw, Shura, saw, they are golden.
Of course, this is not so simple, we need to purposefully move towards this - in order to leave the development from "we will do it for six months, then we will roll out a new version for the test."
The secret is to have a stable version at the end of each sprint . Of course, it might seem that this will increase the development time due to the growth of overhead costs for stabilization of the branch and that the development will be suboptimal. But here the situation is almost the same as with the tests, let's talk about it below.
And now for real situations. Our task was to configure encryption between our system and the API so that the user signs all requests with a key. I think many of you have come across CryptoPro, so we had to.
If you use the standard approach, one isolated task will be formed - setting up cryptography for the service. We will hang it on one person and for a month we will remove the status and resent why it will not end in any way. And the developer will gradually turn into a wild creature from the other world, with foam at the mouth and wild eyes.
We cut it into small tasks and scattered throughout the team. I compare cryptography with alcohol: in company and in a moderate dose, it’s much better than a lot and alone.
Since we are developing a cloud service, it was easier for us to use the CryptoPro EDS Browser plug-in (this is not advertising, this is hopelessness). It allows CryptoPro CSP to extend keys and encryption methods to a web page.
First, the user chooses which key he will use in working with the service, then authentication takes place to obtain a token. And only then with the help of encryption and token API calls are made. It seems everything is simple (no).
First of all, we did MVP in isolation from our service - to configure the interaction of the plugin with the API. Do you know how much documentation there is about the cryptoPro browser plugin from the manufacturer? Not at all! Only reverse engineering examples, only hardcore. Sleepless nights and attempts to determine what these or other parameters influence.
And only then we were able to try to build it into our ecosystem. One person brings the appearance of the prototype component into a human view, the second connects it to the business logic, the third writes instructions for posterity to configure it. Each task has a specific goal and is relatively isolated from the others. And people have something to discuss and with whom to share the pain.
So the situation has become quite stable. In small iterations, we add new functionality, from time to time we update the external interface and spread the changes deep into our system. But the question arises: live in a separate branch until the last, or try to constantly keep small features in the master?
I propose to figure out what to do with testing and what to do if we add integration to a live project.
Let's start with testing. To begin with, we will determine what we can call the tested functionality. I propose to name the functional that passed the acceptance tests, including regression ones. We’ll only agree that we will not resort to the life hack "no tests - that means everything is tested." We need to maintain our code in the product, and the more test coverage, the better. The greater the percentage of automation of such tests, the cheaper each iteration of testing the functionality and the more often it can be done.
We have a certain set of acceptance and regression tests, some of them are automated, some are held by hand.
If we approach formally, we should conduct testing after each code change. For example, they broke your feature into six tickets and found ten errors during the testing process. And let each test take four hours: automatic does not count, but manual takes just four hours. It turns out that with the basic and formal way of working, we will spend 64 hours.
Now let's try to test not after each code change, but after one. Logic suggests that we spend only 32 hours. And if you conduct testing only after developing the functionality and after fixing all the defects. But this is provided that all defects are isolated from each other, which in life does not happen.
In life, it turns out that after the development of the functionality, testing is carried out and the first wave of bugs is carried. Then the bugs are repaired, the functionality is broken, they are tested by the bug bug. They conduct the second global test, and a new wave of bugs appears. These bugs were hidden and appeared when they corrected the first wave and changed the functionality. And so several times.
Usually you get three to five full runs of tests - depending on the complexity of the functionality and directness of the hands. But the process can be accelerated even more - if you beat on small features.
For example, if you break one feature with testing at four hours into two with testing at three and a half and a half hours, you get five hours instead of four. There seems to be no profit. But it manifests itself in a decrease in the complexity of the released functionality and a decrease in the likelihood of chains of related defects. As a result, iterations of full testing become less.
Now we will analyze the situation when you make a feature in a project on which developers are still working. Suppose we use the relatively standard Branch Per Feature (Branch Per Ticket) concept of source code.
Obviously, the amount of cost to maintain the brunch's relevance is proportional to its lifetime. The fewer code intersections in current tickets, the less problems there are on their merger. This indirectly depends on the lifetime: the more time has passed, the higher the chances that related tickets will appear. Accordingly, the less brunch lives, the less effort we spend on its relevance.
It remains to understand how to roll out partially functional functionality on the prod. The answer is quite simple: it should not be accessible to the user. If you want to ask why you should do this, you can return a few paragraphs above.
It seems that keeping the dead code in the wizard is not very good. That's right, but he's not dead. You can make a secret page with pens that will include such functionality. Or a special sequence of actions, which, like Easter eggs in games, will bring you to this functionality. In addition, you can test it right away.
Of course, there are other ways to deal with the variability of requirements. And this approach has its own limitations and conditions of use. As you know, there is still no silver bullet.
Source: https://habr.com/ru/post/460883/
All Articles