Há mais de um ano, na MyStore, criamos funcionalidades que ajudam nossos usuários a comprar e vender mercadorias rotuladas. As notícias sobre rotulagem escorregaram muitas vezes em Habré, de forma resumida: desde 2019, as mercadorias são marcadas necessariamente. Nem tudo de uma vez, mas agora você precisa rotular cigarros, sapatos, perfumes e pneus de carro. Ao mesmo tempo, estamos trabalhando em uma situação de incerteza quando a API dos sistemas governamentais está mudando constantemente.
Portanto, temos apenas duas maneiras de nos integrar com agências governamentais: espere até que tudo se acalme e perca a liderança de mercado ou desenvolva um sistema em um mundo de requisitos em mudança.
Escolhemos o segundo - apenas no espírito de metodologias flexíveis. Acho que o Agile pode realmente ajudar na solução de problemas aplicados. E a vida em um mundo de demandas em constante mudança é o campo em que você pode se virar.

Meu nome é Maxim Sukharenko, sou o líder da equipe da plataforma de serviços MySklad. E hoje vou lhe contar como trabalhamos com requisitos variáveis.
Do clássico ao Scrum
O prazo inicial para o desenvolvimento do sistema pelas autoridades reguladoras foi planejado para abril de 2019, mas você entende tudo. Como resultado, os prazos foram transferidos para outubro. E onde há novos prazos, há novos requisitos e novos formatos. Não podíamos esperar a correção da API - não teríamos tempo para implementar a integração. Portanto, eles decidiram "hesitar com a linha do partido".
Pensamos na abordagem clássica que nos oferece para fazer um adaptador para um sistema externo. Complemente com um plugue com uma chave e, de tempos em tempos, eleve a funcionalidade do plugue e do adaptador ao estado atual do sistema externo.

A lógica sugere que, para desenvolver um sistema com uma interface em constante mudança, é necessário corrigir os requisitos em algum momento. Mas apenas em que? Até desenvolvermos alguma parte da funcionalidade?
Você deve admitir que é desagradável deixar funcionalidades inacabadas nas quais os testes não passam e também começar a quebrá-las em uma nova. Você pode corrigir os requisitos para o período de desenvolvimento do recurso. Mas o que fazer com os grandes recursos que podem permanecer em desenvolvimento por vários meses? É simplesmente impossível corrigir requisitos por um período tão longo.
Então nos voltamos para o Scrum. Ele nos diz que, no final do sprint, devemos fornecer um produto funcional com novas funcionalidades. Mas como isso lida com a fixação de requisitos e grandes recursos? Há uma boa piada sobre esse assunto:
"Doutor, quando eu faço assim, me dói."
"E você simplesmente não faz assim."
Quem não entende, não faça grandes recursos . Você pode corrigir os requisitos por uma ou duas semanas para coincidir com o sprint e definir a implementação do funcional de acordo com os requisitos para o sprint. E a funcionalidade deve ser batida em pedaços para que eles se encaixem em um ou dois sprints. Pense muito complicado? E então! Você simplesmente não sabe como cortar ingressos. Serra, Shura, serra, eles são dourados.
Obviamente, isso não é tão simples, precisamos avançar de propósito - para deixar o desenvolvimento de "faremos isso por seis meses, depois lançaremos uma nova versão para o teste".
O segredo é ter uma versão estável no final de cada sprint . Obviamente, pode parecer que isso aumentará o tempo de desenvolvimento devido ao crescimento dos custos indiretos de estabilização da filial e que o desenvolvimento será abaixo do ideal. Mas aqui a situação é quase a mesma dos testes, vamos falar sobre isso abaixo.
Para combater missões
E agora para situações reais. Nossa tarefa era configurar a criptografia entre o nosso sistema e a API para que o usuário assinasse todas as solicitações com uma chave. Eu acho que muitos de vocês já se depararam com o CryptoPro, então tivemos que fazê-lo.
Se você usar a abordagem padrão, uma tarefa isolada será formada - configurando a criptografia para o serviço. Vamos pendurá-lo em uma pessoa e, durante um mês, removeremos o status e nos ressentiremos por que ele não termina de forma alguma. E o desenvolvedor gradualmente se tornará uma criatura selvagem do outro mundo, com espuma na boca e olhos selvagens.

Cortamos em pequenas tarefas e espalhamos por toda a equipe. Comparo criptografia com álcool: na empresa e em uma dose moderada, é muito melhor do que muito e sozinho.
Como estamos desenvolvendo um serviço de nuvem, foi mais fácil usar o plug-in CryptoPro EDS Browser (isso não é publicidade, é desesperança). Ele permite que o CryptoPro CSP estenda chaves e métodos de criptografia para uma página da web.
Primeiro, o usuário escolhe qual chave ele usará ao trabalhar com o serviço e, em seguida, a autenticação ocorre para obter um token. E somente então, com a ajuda de criptografia e chamadas à API de token, são feitas. Parece que tudo é simples (não).
Primeiro, fizemos o MVP isoladamente do nosso serviço - para configurar a interação do plug-in com a API. Você sabe quanta documentação existe sobre o plugin do navegador cryptoPro do fabricante? Nem um pouco! Apenas exemplos de engenharia reversa, apenas hardcore. Noites sem dormir e tentativas de determinar o que esses ou outros parâmetros influenciam.
E só então fomos capazes de tentar incorporá-lo ao nosso ecossistema. Uma pessoa traz a aparência do componente do protótipo para uma forma humana, a segunda o conecta à lógica de negócios, a terceira escreve instruções para a posteridade para configurá-lo. Cada tarefa tem um objetivo específico e é relativamente isolada dos outros. E as pessoas têm algo a discutir e com quem compartilhar a dor.
Então a situação se tornou bastante estável. Em pequenas iterações, adicionamos novas funcionalidades; de tempos em tempos, atualizamos a interface externa e divulgamos as alterações profundamente em nosso sistema. Mas surge a pergunta: morar em um ramo separado até o último, ou tentar manter constantemente pequenos recursos no mestre?
Teste de funcionalidade
Proponho descobrir o que fazer com o teste e o que fazer se adicionarmos integração a um projeto ativo.
Vamos começar com o teste. Para começar, determinaremos o que podemos chamar de funcionalidade testada. Proponho nomear o funcional que passou nos testes de aceitação, incluindo os de regressão. Concordamos apenas que não recorreremos ao hack de vida "sem testes - isso significa que tudo está testado". Precisamos manter nosso código no produto e, quanto mais cobertura de teste, melhor. Quanto maior a porcentagem de automação desses testes, mais barata é a iteração de testes da funcionalidade e mais frequentemente isso pode ser feito.
Temos um certo conjunto de testes de aceitação e regressão, alguns deles são automatizados, outros são realizados manualmente.
Se nos aproximarmos formalmente, devemos realizar testes após cada alteração de código. Por exemplo, eles dividiram seu recurso em seis tickets e, durante o processo de teste, encontraram dez erros. E deixe cada teste levar quatro horas: o automático não conta, mas o manual leva apenas quatro horas. Acontece que, com a maneira básica e formal de trabalho, passaremos 64 horas.
Agora vamos tentar testar não após cada alteração de código, mas após uma. A lógica sugere que passemos apenas 32 horas. E se você realizar o teste somente após desenvolver a funcionalidade e após corrigir todos os defeitos. Porém, desde que todos os defeitos sejam isolados um do outro, o que na vida não acontece.
Na vida, acontece que após o desenvolvimento da funcionalidade, os testes são realizados e a primeira onda de bugs é realizada. Em seguida, os bugs são reparados, a funcionalidade é quebrada, eles são testados pelo bug. Eles realizam um segundo teste global e uma nova onda de bugs aparece. Esses erros foram ocultados e apareceram quando eles corrigiram a primeira onda e alteraram a funcionalidade. E tantas vezes.
Geralmente, você realiza de três a cinco execuções completas de testes - dependendo da complexidade da funcionalidade e da franqueza das mãos. Mas o processo pode ser acelerado ainda mais - se você vencer em pequenos recursos.
Por exemplo, se você dividir um recurso com testes às quatro horas em dois com testes às três horas e meia e meia, terá cinco horas em vez de quatro. Parece não haver lucro. Mas ela se manifesta em uma diminuição na complexidade da funcionalidade liberada e na diminuição da probabilidade de cadeias de defeitos relacionados. Como resultado, as iterações do teste completo se tornam menos.

Adicionar ao projeto
Agora, analisaremos a situação quando você criar um recurso em um projeto no qual os desenvolvedores ainda estão trabalhando. Suponha que usemos o conceito relativamente padrão de código-fonte de filial por recurso (filial por ticket).
Obviamente, o valor do custo para manter a relevância do brunch é proporcional à sua vida útil. Quanto menos interseções de código nos tickets atuais, menos problemas haverá na fusão. Isso depende indiretamente da vida útil: quanto mais tempo se passa, maiores são as chances de os tickets relacionados aparecerem. Por conseguinte, quanto menos vidas de brunch, menos esforço gastamos em sua relevância.
Resta entender como implementar funcionalidades parcialmente funcionais no produto. A resposta é bastante simples: não deve ser acessível ao usuário. Se você quiser perguntar por que deve fazer isso, retorne alguns parágrafos acima.
Parece que manter o código morto no assistente não é muito bom. Está certo, mas ele não está morto. Você pode criar uma página secreta com canetas que incluirá essa funcionalidade. Ou uma sequência especial de ações, que, como os ovos de Páscoa nos jogos, levarão você a essa funcionalidade. Além disso, você pode testá-lo imediatamente.
Obviamente, existem outras maneiras de lidar com a variabilidade de requisitos. E essa abordagem tem suas limitações e condições de uso. Como você sabe, ainda não há bala de prata.