How to deal with task decomposition and not overdo it

Hello!



My name is Victor, I am a systems analyst at Sportmaster. And today I would like to talk about decomposing tasks and transferring them to development. Any object consists of parts, be it a car or a software product. And it will take time to assemble any of these objects into a single whole from its component parts. Sometimes it is even very time consuming. Especially if before that you didn't just disassemble the main part, but decided to get to the bottom of it at the atomic level.





Where is the line between an adequate setting of tasks and total chaos? I will share an example of how we at Sportmaster periodically receive development tasks from business.















As you can see from the above examples, the description of each task depends largely on the imagination and common sense of the customer. Somewhere it is more, somewhere it is less, but analysts need to work with it somehow. Sometimes they also indicate the boundaries of the functionality, and sometimes they just send a topic. If we transfer such a task directly to development, we will get something incomprehensible at the end. What do you have to do?



Walk to the customer with your feet and ask all the requirements, clarify what exactly should be at the exit. True, there are still gold customers, of whom, in fact, the majority - they write all the requirements in their Confluence, so you can go and read it at any time and ask questions. And when everything is clear with the framework of the feature, you can start cutting the task.



Why decompose



The main goal of decomposition is so that the business can quickly implement all its wishes, and so that the user takes as little time as possible from the idea itself to the appearance of functionality. To do this, you can do smaller tasks, from which, albeit small, but still the working functionality will reach the user.



In addition to achieving the global goal of meeting user and business needs, task decomposition allows you to get faster feedback from the customer, whether the development is digging in the right direction, or it turned out not at all what the customer imagined.



If the task is initially large and we took on it entirely at once, then we will spend a lot of time on it and after the customer's comments we will have to throw everything away. Well, if the task is small, a day or two of work at most, it's okay. The rework will take about the same amount. The second approach is also cheaper. Not to mention the saved nerves on both sides.



If one functionality is split into several pieces, developers can work on them in parallel. Thus, we will parallelize the flow and speed up the output of the functionality in prod. The important thing is that tasks should depend on each other as little as possible.



Plus quick testing and bug fixes. Again, small functionality is much easier and faster to test than a monstrous colossus. And if something goes wrong, the developer will spend very little on "fixing", and everything will work faster.



At the stage of breaking down tasks, together with the customer, you can immediately understand which functionality is important right here and now, in order to start making a profit, what can be left for later, and what, perhaps, will fall off as unnecessary.



It is important for business to know how quickly the working functionality will appear. And when broken down into tasks, we can predict and more accurately estimate the time it will take to complete them than when you have one large front of work. But besides the fact that small tasks are easier to assess in terms of the time to work them out, it is also easier for a developer to assess the risks that may arise in the process.



For example, frameworks were updated, some methods were taken out of service, problems with the code, and so on. By taking small tasks into work, we minimize all these risks, and even if such a task blocks something in the thread, it will not be as critical as if it were a hefty piece (which would block everything at all). If necessary, it will be possible to make a working solution and put a technical debt in the backlog, which can be dealt with a little later, when the problems are resolved.



Basic approaches and rules of decomposition



There are two main approaches to task decomposition - horizontal and vertical. With horizontal, tasks are divided by type of work, by level, or by component. For example, we have each task goes through several stages: front-end, back-end, databases. And with a horizontal approach, one task goes to the back, the second to the front, and the third leads to changes in the database.



Why is this approach bad? We do not get working functionality after completing each individual task. Only by collecting the results from three sources, we can get some kind of result and feedback on it. For this reason, horizontal decomposition is most often not used.





Much more convenient is the vertical approach, in which visual functionality can be made in each task - the task goes through all stages and the output is a result that can be analyzed, tested, shown to the customer and corrected, if necessary. And quickly start up and use.



If we talk about the rules, here I have identified only three. First, the task must be logically complete, that is, independent in itself. It should not break the logic around it and must necessarily carry at least some business sense that the user will receive as a result. At the same time, you should not break down into parts those tasks that do not carry business sense (ideally, they should not exist at all).



Secondly, the result of completing one small task should bring small changes. The smaller the changes, the faster they get into the general code, and thus the code does not become obsolete. In addition, small tasks help to avoid conflict between developers when merging.



Thirdly, you shouldn't break the task down into very microscopic parts. If broken down too small, it will take a very long time to manage these tasks. At each stage, they may have to be re-prioritized, re-affixed dependencies, and that's all. Thus, the development speed will not increase, but, on the contrary, will drop sharply. Therefore, you need to look for a middle ground.



Decomposition methods



Depending on the source, the number of decomposition methods varies greatly: somewhere they are indicated by only eight, somewhere ten, somewhere twenty. I would like to highlight the ways that I have to use every day at work.



Multiple needs



This method is most convenient to use when there are conjunctions "and", "or" in the story. For example, a consumer wants to place an order and pay with a card or bonuses. This task can be divided into three: the first, in which the user places an order, the second, where he pays for it with a card, and the third, where bonuses are used.



Usage scenarios



Another common way is to divide tasks depending on the use case. In this case, one story is one main path and several alternative ones. Let's say a user wants to buy an item, and that would be the main scenario. But there are still alternative ways - he may immediately put the product in the basket and pay, or he may want to compare this product with others before buying. And then we make product comparison a separate task.



Perhaps he does not want to buy right now, but put it off somewhere, add to favorites, so that he can return to it later. Or the user liked the product and is ready to buy it, but it is out of stock. So, you need to let him know when the goods will appear. And this is how four scenarios turn out.



From simple to complex



The main page of the "Sportmaster" site consists of banners. And the simplest thing we can do is take one picture and show it to the user. This is the easiest and fastest way to convey the right information. Then we can increase the functionality and add not one picture, but three or four, which are combined into a grid. This is a separate task.





With this approach, with each subsequent task, the functionality should grow. For example, we can make a carousel out of the grid, and then add some links, texts, buttons and the rest. In general, first we implement the simplest and fastest-performing version, and then move on to a more complex one.



Just recently I was engaged in a similar task of implementing a banner. The banner was supposed to hang on the main one and be controlled from the CMS. If you ask the customer what exactly he would like to manage, he will happily answer without blinking - everyone. Therefore, it was important here to dive into the topic a little and highlight what needs to be managed right now, what is just often, and what is almost never used at all. And thus, prioritize implementation and divide into tasks.



Operations (CRUD)



This is probably the most common way of decomposition. Here, tasks are divided into Create, Read, Update and Delete operations. It is suitable for tasks where you need to manage something or configure something. For example, the task of placing an order is divided into four smaller ones: creating an order, viewing it, editing it, and deleting it.



Interface options



Used when multiple interface options need to be supported. For example, a site must support multiple languages. First, we make the Russian version. Then, when launching in other countries, we add English. If the country uses a language other than English, we can add that as well. In this case, it is easier to do everything first in one language, and then gradually add translations.





More recently, we completed a project of a personal account for legal entities, which needed multilingual support. The deadlines were tight, so they initially did everything in one language, but laid the foundation for further translation. Now, adding support for a new language takes just one small task. If you need to add several languages โ€‹โ€‹at once, a separate task is created for each of them.



Separation by role



Suitable for situations in which the functionality implies the operation of several roles and user groups. On the Sportmaster site, a user can have different roles. For example, users are divided by roles into an authorized user, an anonymous user, and, say, a call center user. The latter role can also be divided into two - it can be either an ordinary user or an administrator.



For each role, we can make a separate task. Start by displaying for an anonymous user and then adding some advanced functionality as part of a task for an authorized user. And don't forget about the technical roles of call center operators and the functionality for them.



Error processing



If the deadlines are tight, and the minimum functionality is needed as quickly as possible, you can take error handling into a separate task. Here we are not talking about writing tests, but about handling errors of users and systems with which the site is integrated. Imagine we are processing a catalog page that contains a product tile. Each card has a description, photo and additional information.



It so happened that some of the information does not come from the databases.

Perhaps, if we are talking about a brand or material, then this can be neglected and simply not shown information. But if the price or name does not come up, is it worth showing this card?



What to do in such a situation? This question can be taken out in a separate task and then process each separate field. That is, if the price did not come, then we perform one action, the description of the product is lost - another. It's the same with user errors. If he entered something incorrectly and an error was displayed, for example, "Page not found" or error 500, we must show him specific information about what happened and offer him a script what he can do next.



This method is also suitable for situations where you need to quickly get feedback on functionality in order to decide whether to keep it or not.



Static then dynamic



This is one of my favorite ways. Suitable in situations where it is possible to implement functionality "on stubs", that is, external systems are not ready to support the functionality. For example, some blocks on the main page cannot be controlled from the CMS. Or a menu, when we create it in our code and display it to the user, but at the same time it cannot be controlled by the business. And in order to make changes, the business needs to constantly go to development and ask to do it.



Here, we prioritize user needs and profit. The user gets the ready-made functionality immediately, even though we may experience some inconvenience inside. Therefore, we divide the task into several and first make the new block available to the user, but the business cannot yet directly manage it. But then we can integrate with some system or database, where the business itself will be able to swap items and add new ones on their own, and we will draw them without development participation.



We often use this method: first, we make functionality on our own data, which cannot be controlled from outside, and then we add dynamics and start receiving data from third-party systems.



Performance



If the task as a whole is complex and voluminous, it is not clear from which end to tackle it, then performance can be neglected. The first task is to bring out the ready-made functionality, which somehow, albeit slowly, but worked. And the next task is to speed up the work. For example, it can be a slow work of searching for products, applying filters, obtaining any information.



Sometimes most of the effort is invested in quickly creating a function - the initial implementation is not so difficult. But you can learn a lot from the slow implementation, plus it has some value for the user who otherwise would not be able to perform some important action. In all these cases, tasks are broken down into โ€œmake it workโ€ and โ€œmake it fastโ€.



Possible difficulties



If you decide to use task decomposition in your projects, then most likely the first difficulty you will encounter will be the dependence of tasks on each other. In my experience, this is the most common thread blocking problem. To avoid this, decomposition should be approached responsibly and given sufficient time.





Another difficulty is determining how finely you need to decompose the task. And here only common sense acts as boundaries. For example, we take the city selection component. It has buttons, some text, an input field. How small should you beat this task?



We have deduced a rule for ourselves that the task should take place along the entire flow in no more than one week (about 40 hours). We are talking about all stages: the stage of analytics, development, testing. Plus, two stages of backend and frontend development are taken into account, including review and testing at each.



We also had a problem with the fact that the boundaries of the epic are not always clear. Recently we were given the task of placing an order. Where are her borders? What should be the output? It was not clear to us whether we needed to do all the functionality to the very end, or choose some part. Does this epic include payment, or is it already a separate epic.



There are tasks that are difficult to understand how to decompose and when. We decompose most of the tasks at the stage of receiving the epic, but there are situations when this needs to be done, for example, at the stage of analytics. We take the task to work and believe that all the necessary data is already in our integration systems, but during the analysis it turns out that either we are not satisfied with the data format, or the problem is in the quality of the data itself, or improvement is required from other systems with which we are connected. Then we have to do the task "on stubs" and add another item to the backlog, which we will start after we have solved the main problems.



That, it seems, is all. It will be great if you share stories in the comments about which approach to task decomposition you use and why.



Thanks for reading!



All Articles