Parando a linha, rumo ao sucesso

Garfield Há uns tempos atrás passei por uma crise de qualidade em um dos meus projetos. Foram uma meia dúzia de bugs, mas tratando de um projeto de missão crítica, tiveram um impacto considerável no produto e na credibilidade na equipe. Eu estava de férias durante o período, apesar de haver ouvido rumores quando estava afastado, logo que cheguei pude perceber a realidade dos fatos.

A coisa mais importante naquele momento era saber como chegamos naquela situação e o que ocasionou os problemas.

Desculpas, desculpas e mais desculpas

A base do código do projeto foi recebida de outra empresa, ela possuía uma séria de problemas desde problemas arquiteturais até erros básicos de programação.

Apesar da causa estar ligada à origem do código, não fazia sentido usar isso como desculpa. Afinal o que diferencia uma equipe excelente de uma mediana é a capacidade dela lidar com situações adversas e saber usar boas práticas para reverter essa situação.

Como chegamos até aqui?

Ao receber o projeto, por questões de negócio decidimos trocar a roda com o carro andando. Ou seja, iríamos implementar novas funcionalidades com a melhor qualidade possível e ao mesmo tempo reservar um tempo para corrigir problemas legados e fazer melhorias no projeto e no processo de uma maneira geral. Então se havia um bug legado ou uma melhoria a ser feita ela ia para nossa pilha de tarefas e quando pudermos iríamos resolve-lo.

Desde o início adotamos práticas ágeis como criação de testes automatizados, refatorações, programação em par e mais importante a melhoria contínua, que geraram muitas melhorias para o processo.

A estratégia funcionou bem e a qualidade do produto melhorou consideravelmente. Porém por causa desses bugs e alguns incidentes passados nós tivemos que repensar a nossa estratégia.

Tolerância a problemas

Devido à natureza do projeto (legado), problemas antigos vieram a tona e problemas novos surgiram, seguindo a nossa estratégia esses problemas iam para a pilha de tarefas. Apesar disso nos permitir manter o foco nas tarefas atuais, isso inconscientemente criou um efeito colateral indesejado, a tolerância a problemas. Ou seja, se um problema surgisse ele simplesmente ia para a pilha, assim como os outros, afinal ele vai ser só mais um lá.

Intolerância a problemas

Conversando com a equipe, veio a tona um dos pilares do pensamento Lean, o Jidoka. Jidoka é a prática de parar a linha de produção quando algum erro, por menor que seja, é detectado. No caso da Toyota, por exemplo, logo assim que um problema é detectado em uma etapa da produção, o trabalhador tem o direito e o dever de interromper a linha de produção e corrigir o problema.

Apesar do custo, essa prática é importante por impedir que o problema se propague na linha de produção, ou seja, que outros automóveis sejam manufaturados com esse problema. Isso trás um benefício psicológico importante que é a criação de uma cultura que qualquer erro ou problema é uma anormalidade.

No Jidoka não existe pilha ou espera para problemas eles são resolvidos imediatamente. Isso me levou a lembrar um assunto antigo, a teoria das janelas quebradas.

Como funciona na prática

No mundo do software, estruturamos a parada de linha da seguinte forma: sempre parar o desenvolvimento quando um problema ou bug for encontrado.

Isso na prática quer dizer que não faz sentido um programador começar a desenvolver uma funcionalidade nova se existe um bug em uma já existente e se um desenvolvedor está trabalhando em uma funcionalidade nova e um bug surgir ele deve parar para analisar o bug.

Quando a causa do bug é encontrada é disparada uma parada de linha geral momentânea. Na nossa equipe alguém gritava "Parou a linha". Nesse caso todos os membros da equipe se reúnem em volta da máquina do desenvolvedor que encontrou a causa e discutem uma solução para o problema, a causa raiz e a solução da causa raiz.

Se o bug estiver impactando produção é dada uma solução de contorno, caso não esteja o desenvolvedor já parte para a correção da causa raiz. Muitas vezes a correção da causa raiz envolve várias ações que são distribuídas com a equipe.

A correção da causa raiz normalmente tem prioridade sobre outros bugs, já que o mais importante é impedir que aquele bug e outros semelhantes voltem a acontecer. Ou seja a correção nunca deve ser pontual.

Exemplos

Atualmente existe tecnologia e práticas disponíveis para corrigir a causa raiz de muitos problemas encontrados. Para isso funcionar bem é preciso ter uma boa visão de processos e ferramentas. Abaixo listo alguns exemplos resumido de problemas com a causa raiz e sua solução. A busca pela causa raíz e solução normalmente é feita na parada de linha.

Problema 1

  • Funcionalidade não tem comportamento esperado em produção. Por quê?
  • Nullpointer ao acessar método da classe. Por quê?
  • Atributo obrigatório não foi setado. Por quê?
  • Refatoração excluiu a linha que setava o atributo.

Existem algumas formas de atacar a causa do problema acima. Uma que definimos, foi introduzir a prática que todo atributo obrigatório deve ser passado no construtor. Isso evita que um objeto inválido seja criado, mesmo se o código for alterado por uma refatoração.

Problema 2

  • Cálculo de juros está incorreto. Por quê?
  • A classe de serviço está chamando o método do modelo incorreto. Por quê?
  • Existem dois métodos com assinaturas parecidas, um que não estava mais sendo utilizada e estava desatualizado.

Uma mudança no processo que fizemos para resolver a causa do problema acima foi inserir no nosso processo de build uma ferramenta que valida se existe código morto no projeto, ou seja, código que não é referenciado por nenhuma outra classe. Se esse tipo de código existir o build falha. Isso evita o tipo de confusão que ocorreu acima e mantém uma base de código mais limpa e saudável.

Como isso pode ser produtivo?

Essa prática aumenta drasticamente o nível de qualidade do projeto. Com o tempo as paradas de linha vão vão se tornando menos frequentes e isso se torna um ciclo virtuoso.

A qualidade trás uma economia importante, que seria o tempo de resolver e dar suporte aos problemas.

Em relação ao impacto de interromper o fluxo de trabalho, é claro que existe o bom senso, não faz sentido o desenvolvedor parar para corrigir um bug se ele já está corrigindo um bug. Ou parar para corrigir uma causa raíz se ele já está corrigindo uma causa raíz. Nesse caso voltamos temporariamente com a velha pilha de coisas :).