Meu nome é Victor, sou analista de sistemas da Sportmaster. E hoje eu gostaria de falar sobre decompor tarefas e transferi-las para o desenvolvimento. Qualquer objeto consiste em peças, seja um carro ou um produto de software. E levará algum tempo para reunir qualquer um desses objetos em um único todo a partir de suas partes componentes. Às vezes, é até muito demorado. Especialmente se antes disso você não desmontou apenas a parte principal, mas decidiu ir ao fundo dela no nível atômico.

Onde está a linha entre uma configuração adequada de tarefas e o caos total? Vou compartilhar um exemplo de como nós da Sportmaster recebemos periodicamente tarefas de desenvolvimento de negócios.



Como você pode ver nos exemplos acima, a descrição de cada tarefa depende muito da imaginação e do bom senso do cliente. Em algum lugar é mais, em algum lugar é menos, mas os analistas precisam trabalhar com isso de alguma forma. Às vezes, eles também indicam os limites da funcionalidade e, às vezes, apenas enviam um tópico. Se transferirmos essa tarefa diretamente para o desenvolvimento, obteremos algo incompreensível na saída. O que você tem que fazer?
Ande até o cliente com os pés e pergunte todos os requisitos, esclareça o que exatamente deve estar na saída. É verdade que ainda existem clientes de ouro, dos quais, na verdade, a maioria - eles escrevem todos os requisitos em seu Confluence, então você pode ler a qualquer momento e fazer perguntas. E quando tudo já estiver claro com a estrutura do recurso, você pode começar a cortar a tarefa.
Por que decompor
O principal objetivo da decomposição é que o negócio possa implementar rapidamente todos os seus desejos, e que o usuário leve o mínimo de tempo possível desde a própria ideia até o surgimento da funcionalidade. Para fazer isso, você pode fazer tarefas menores, das quais, embora pequenas, mas ainda assim a funcionalidade de trabalho chegará ao usuário.
Além de atingir a meta global de atender às necessidades do usuário e do negócio, a decomposição de tarefas permite que você obtenha feedback mais rápido do cliente, esteja o desenvolvimento indo na direção certa ou não tenha resultado o que o cliente imaginou.
Se a tarefa é inicialmente grande e a assumimos inteiramente de uma vez, então gastaremos muito tempo nela e depois dos comentários do cliente teremos que jogar tudo fora. Bem, se a tarefa for pequena, um ou dois dias de trabalho no máximo, está tudo bem. O retrabalho levará aproximadamente a mesma quantidade. A segunda abordagem também é mais barata. Sem mencionar os nervos salvos de ambos os lados.
Se uma funcionalidade for dividida em várias partes, os desenvolvedores podem trabalhar nelas em paralelo. Assim, paralelizaremos o fluxo e aceleraremos a saída da funcionalidade no prod. Uma coisa importante é que as tarefas devem depender umas das outras o menos possível.
Além de testes rápidos e correções de bugs. Novamente, pequenas funcionalidades são muito mais fáceis e rápidas de testar do que um colosso monstruoso. E se algo der errado, o desenvolvedor gastará muito pouco em "consertar", e tudo funcionará mais rápido.
Na fase de desmembramento de tarefas, em conjunto com o cliente, você pode entender imediatamente qual funcionalidade é importante aqui e agora, para começar a ter lucro, o que pode ser deixado para depois e o que, talvez, será considerado desnecessário.
É importante para as empresas saber com que rapidez a funcionalidade de trabalho aparecerá. E quando divididos em tarefas, podemos prever e estimar com mais precisão o tempo que levará para concluí-las do que quando você tem uma grande frente de trabalho. Mas, além do fato de que pequenas tarefas são mais fáceis de avaliar em termos de tempo para resolvê-las, também é mais fácil para um desenvolvedor avaliar os riscos que podem surgir no processo.
Por exemplo, as estruturas foram atualizadas, alguns métodos foram desativados, problemas com o código, etc. Ao colocar pequenas tarefas no trabalho, minimizamos todos esses riscos e, mesmo que tal tarefa bloqueie algo no thread, não será tão crítico como se fosse uma peça pesada (o que bloquearia tudo). Se necessário, será possível fazer uma solução funcional e colocar uma dívida técnica no backlog, que pode ser resolvida um pouco mais tarde, quando os problemas forem resolvidos.
Abordagens básicas e regras de decomposição
Existem duas abordagens principais para a decomposição de tarefas - horizontal e vertical. Com a horizontal, as tarefas são divididas por tipo de trabalho, nível ou componente. Por exemplo, cada tarefa passa por vários estágios: front-end, back-end, bancos de dados. E com uma abordagem horizontal, uma tarefa vai para trás, a segunda para a frente e a terceira leva a mudanças no banco de dados.
Por que essa abordagem é ruim? Não obtemos a funcionalidade de trabalho após concluir cada tarefa individual. Somente coletando os resultados de três fontes, podemos obter algum tipo de resultado e feedback sobre ele. Por esse motivo, a decomposição horizontal geralmente não é usada.

Muito mais conveniente é a abordagem vertical, em que a funcionalidade visual pode ser feita em cada tarefa - a tarefa passa por todas as etapas e a saída é um resultado que pode ser analisado, testado, mostrado ao cliente e corrigido, se necessário. E rapidamente inicialize e use.
Se falarmos sobre as regras, aqui identifiquei apenas três. Primeiro, a tarefa deve ser logicamente concluída, isto é, independente em si mesma. Ele não deve quebrar a lógica em torno de si mesmo e deve necessariamente conter pelo menos algum senso comercial que o usuário receberá como resultado. Ao mesmo tempo, você não deve dividir em partes as tarefas que não possuem senso comercial (de preferência, elas não deveriam existir).
Em segundo lugar, o resultado de completar uma pequena tarefa deve trazer pequenas mudanças. Quanto menores as alterações, mais rápido elas entram no código comum e, portanto, o código não se torna obsoleto. Além disso, pequenas tarefas ajudam a evitar conflito entre desenvolvedores durante a fusão.
Em terceiro lugar, você não deve dividir a tarefa em partes muito microscópicas. Se dividida muito pequena, levará muito tempo para gerenciar essas tarefas. Em cada estágio, eles podem ter que ser priorizados, dependências reafixadas e isso é tudo. Assim, a velocidade de desenvolvimento não aumentará, mas, ao contrário, cairá drasticamente. Portanto, você precisa procurar um meio-termo.
Métodos de decomposição
Dependendo da fonte, o número de métodos de decomposição varia muito: em algum lugar eles são indicados por apenas oito, em algum lugar dez, em algum lugar vinte. Gostaria de me concentrar nas maneiras que devo usar todos os dias no trabalho.
Múltiplas necessidades
Este método é mais conveniente de usar quando há conjunções "e", "ou" na história. Por exemplo, um consumidor deseja fazer um pedido e pagar com um cartão ou bônus. Essa tarefa pode ser dividida em três: a primeira, em que o usuário faz um pedido, a segunda, onde ele paga com cartão, e a terceira, onde são utilizados bônus.
Cenários de uso
Outra maneira comum é dividir as tarefas dependendo do caso de uso. Nesse caso, uma história é um caminho principal e vários caminhos alternativos. Digamos que um usuário queira comprar um item e esse seria o cenário principal. Mas ainda existem maneiras alternativas - ele pode imediatamente colocar o produto na cesta e pagar, ou pode querer comparar este produto com outros antes de comprar. E então fazemos a comparação de produtos uma tarefa separada.
Talvez ele não queira comprar agora, mas adie em algum lugar, adicione aos favoritos, para que ele possa voltar depois. Ou o usuário gostou do produto e está pronto para comprá-lo, mas está esgotado. Portanto, você precisa avisá-lo quando a mercadoria aparecerá. E é assim que obtemos quatro cenários.
Do simples ao complexo
A página principal do site "Sportmaster" é composta por banners. E a coisa mais simples que podemos fazer é tirar uma foto e mostrá-la ao usuário. Esta é a maneira mais fácil e rápida de transmitir as informações certas. Então podemos aumentar a funcionalidade e adicionar não uma imagem, mas três ou quatro, que são combinadas em uma grade. Esta é uma tarefa separada.

Com essa abordagem, a cada tarefa subsequente, a funcionalidade deve crescer. Por exemplo, podemos fazer um carrossel fora da grade e, em seguida, adicionar alguns links, textos, botões e o resto. Em geral, primeiro implementamos a versão mais simples e de desempenho mais rápido e, em seguida, passamos para uma mais complexa.
Recentemente, eu estava envolvido em uma tarefa semelhante de implementação de um banner. O banner deveria ficar pendurado no principal e ser controlado pelo CMS. Se você perguntar ao cliente o que exatamente ele gostaria de gerenciar, ele responderá com prazer sem piscar - todos. Portanto, foi importante aqui mergulhar um pouco no assunto e destacar o que precisa ser gerenciado agora, o que é apenas frequente e o que quase nunca é usado. E assim priorizar a implementação e dividir em tarefas.
Operações (CRUD)
Esta é provavelmente a forma mais comum de decomposição. Aqui, as tarefas são divididas em operações de criação, leitura, atualização e exclusão. É adequado para tarefas em que você precisa gerenciar ou configurar algo. Por exemplo, a tarefa de fazer um pedido é dividida em quatro tarefas menores: criar um pedido, visualizá-lo, editá-lo e excluí-lo.
Opções de interface
Usado quando várias opções de interface precisam ser suportadas. Por exemplo, um site deve oferecer suporte a vários idiomas. Primeiro, fazemos a versão russa. Então, ao lançar em outros países, adicionamos o inglês. Se o país usar um idioma diferente do inglês, podemos adicioná-lo também. Nesse caso, é mais fácil fazer tudo primeiro em um idioma e, em seguida, adicionar traduções gradualmente.

Mais recentemente, concluímos um projeto de conta pessoal para pessoas jurídicas, que precisava de suporte multilíngue. Os prazos eram curtos, então eles inicialmente fizeram tudo em um idioma, mas lançaram as bases para futuras traduções. Agora, adicionar suporte para um novo idioma exige apenas uma pequena tarefa. Se você precisar adicionar vários idiomas de uma vez, uma tarefa separada será criada para cada um deles.
Separação por papel
Adequado para situações em que a funcionalidade implica o funcionamento de várias funções e grupos de usuários. No site Sportmaster, um usuário pode ter diferentes funções. Por exemplo, os usuários são divididos por funções em um usuário autorizado, um usuário anônimo e, digamos, um usuário do call center. A última função também pode ser dividida em duas - pode ser um usuário comum ou um administrador.
Para cada função, podemos fazer uma tarefa separada. Comece exibindo para um usuário anônimo e, em seguida, adicionando algumas funcionalidades avançadas como parte de uma tarefa para um usuário autorizado. E não se esqueça das funções técnicas dos operadores de call center e da funcionalidade para eles.
Processamento de erro
Se os prazos forem curtos e a funcionalidade mínima for necessária o mais rápido possível, você pode colocar o tratamento de erros em uma tarefa separada. Aqui não estamos falando de escrever testes, mas sim de lidar com erros de usuários e sistemas com os quais o site está integrado. Imagine que estamos processando uma página de catálogo que contém um bloco de produto. Cada cartão possui uma descrição, foto e informações adicionais.
Acontece que parte das informações não provém das bases de dados.
Talvez, se estamos falando de uma marca ou material, isso possa ser negligenciado e simplesmente não mostrado informação. Mas se o preço ou o nome não subir, vale a pena mostrar este cartão?
O que fazer em tal situação? Esta pergunta pode ser retirada em uma tarefa separada e, em seguida, processar cada campo separado. Ou seja, se o preço não veio, então realizamos uma ação, a descrição do produto se perde - outra. O mesmo ocorre com os erros do usuário. Se ele digitou algo incorretamente e um erro foi exibido, por exemplo, "Página não encontrada" ou erro 500, devemos mostrar a ele informações específicas sobre o que aconteceu e oferecer a ele um script o que ele pode fazer a seguir.
Esse método também é adequado para situações em que você precisa obter rapidamente feedback sobre a funcionalidade para decidir se deseja mantê-la ou não.
Estático e depois dinâmico
Esta é uma das minhas formas favoritas. Adequado em situações em que é possível implementar a funcionalidade "em stubs", ou seja, os sistemas externos não estão prontos para suportar a funcionalidade. Por exemplo, alguns blocos na página principal não podem ser controlados a partir do CMS. Ou um menu, quando o colocamos em nosso código e o exibimos para o usuário, mas ao mesmo tempo não pode ser controlado pelo negócio. E para fazer mudanças, a empresa precisa constantemente ir ao desenvolvimento e pedir para fazê-lo.
Aqui, priorizamos as necessidades e o lucro do usuário. O usuário obtém a funcionalidade pronta imediatamente, embora possamos experimentar alguns inconvenientes internos. Portanto, dividimos a tarefa em várias e primeiro disponibilizamos o novo bloco para o usuário, mas a empresa ainda não pode gerenciá-lo diretamente. Mas então podemos integrar com algum sistema ou banco de dados, onde o próprio negócio será capaz de trocar itens e adicionar novos por conta própria, e os desenharemos sem a participação do desenvolvimento.
Costumamos usar este método: primeiro, criamos funcionalidade em nossos próprios dados, que não podem ser controlados de fora, e então adicionamos dinâmica e começamos a receber dados de sistemas de terceiros.
atuação
Se a tarefa como um todo é complexa e volumosa, não está claro de que lado abordá-la, então o desempenho pode ser negligenciado. A primeira tarefa é trazer a funcionalidade pronta, que de alguma forma, embora lentamente, mas funcionou. E a próxima tarefa é acelerar o trabalho. Por exemplo, pode ser um trabalho lento de busca de produtos, aplicação de filtros, obtenção de qualquer informação.Às
vezes, a maior parte do esforço é investido na criação rápida de uma função - a implementação inicial não é tão difícil. Mas você pode aprender muito com a implementação lenta, além de ter algum valor para o usuário que, de outra forma, não seria capaz de realizar alguma ação importante. Em todos esses casos, as tarefas são divididas em “fazer funcionar” e “torná-lo rápido”.
Possíveis dificuldades
Se você decidir usar a decomposição de tarefas em seus projetos, provavelmente a primeira dificuldade que encontrará será a dependência das tarefas umas das outras. Na minha experiência, esse é o problema de bloqueio de thread mais comum. Para evitar isso, a decomposição deve ser abordada de forma responsável e com tempo suficiente.

Outra dificuldade é determinar quão finamente você precisa para decompor a tarefa. E aqui apenas o bom senso atua como limites. Por exemplo, pegamos o componente de seleção de cidade. Possui botões, algum texto e um campo de entrada. Quão pequeno você deve vencer esta tarefa?
Deduzimos uma regra para nós mesmos que a tarefa deve ser realizada ao longo de todo o fluxo em no máximo uma semana (cerca de 40 horas). Estamos falando sobre todos os estágios: o estágio de análise, desenvolvimento, teste. Além disso, dois estágios de desenvolvimento de back-end e front-end são levados em consideração, incluindo revisão e teste em cada um.
Também tivemos um problema com o fato de que os limites do épico nem sempre são claros. Recentemente, recebemos a tarefa de fazer um pedido. Onde estão suas fronteiras? Qual deve ser a saída? Não estava claro para nós se precisávamos fazer todas as funcionalidades até o fim ou escolher alguma parte. Este épico inclui pagamento ou já é um épico separado.
Existem tarefas que são difíceis de entender como decompor e quando. Decompomos a maioria das tarefas na fase de recebimento da epopeia, mas há situações em que isso precisa ser feito, por exemplo, na fase de análise. Assumimos a tarefa para trabalhar e acreditamos que todos os dados necessários já estão em nossos sistemas de integração, mas durante a análise, verifica-se que ou não estamos satisfeitos com o formato dos dados, ou o problema está na qualidade dos próprios dados, ou é necessário melhorar outros sistemas com os quais estamos conectados. Em seguida, temos que fazer a tarefa "em stubs" e adicionar outro item ao backlog, que iniciaremos depois de resolver os principais problemas.
Isso, ao que parece, é tudo. Será ótimo se você compartilhar histórias nos comentários sobre qual abordagem de decomposição de tarefas você usa e por quê.
Obrigado por ler!