Os Primeiros Passos na Nossa Transformação DevOps
5 min. de leitura

No segundo capítulo desta série – DevOps para a Vitória, exploramos os passos iniciais cruciais de uma jornada de transformação DevOps. Os primeiros passos em qualquer jornada de transformação são frequentemente os mais difíceis. Em DevOps, identificar os passos iniciais corretos – aqueles que são simultaneamente impactantes e alcançáveis – é fundamental para impulsionar um progresso significativo.

Muitas organizações cometem o erro de implementar cegamente as melhores práticas sem considerar as suas restrições únicas, o que frequentemente leva à frustração e à fadiga do insucesso. Em vez disso, é necessária uma abordagem mais estratégica – uma que identifique os estrangulamentos e os remova sistematicamente.

Como Identificar os Primeiros Passos?

Um princípio poderoso para identificar por onde começar provém de uma pesquisa publicada há 30 anos em A Meta: Um Processo de Melhoria Contínua de Eliyahu M. Goldratt. A ideia é simples: identificar o principal estrangulamento – o fator limitante que restringe o desempenho do sistema. No nosso caso, isso tornou-se claro quando vimos o trabalho a acumular-se na equipa de desenvolvimento. As tarefas estavam a ser iniciadas, mas não concluídas, e o backlog continuava a crescer.

Para descobrir as causas-raiz, organizámos sessões de aprofundamento com equipas de desenvolvimento, líderes técnicos e gestores de produto. Estas discussões revelaram várias questões-chave:

  • As equipas estavam a assumir novas tarefas antes de concluir as existentes.
  • Não existiam critérios claros de conclusão de tarefas – o que os programadores consideravam “feito” diferia frequentemente das expectativas das partes interessadas.
  • Não havia medidas objetivas de qualidade antes de o trabalho ser dado como concluído, o que levava a defeitos e retrabalho.
  • As frequentes reatribuições perturbaram a estabilidade da equipa, causando ineficiências.

Estabelecer Equipas Estáveis

Uma das primeiras medidas corretivas foi a reestruturação das equipas para consistência e foco. Em vez de tratar grupos de indivíduos como forças-tarefa ad hoc, formámos equipas estáveis e de longa duração com responsabilidades claramente definidas.

A criação de algumas equipas, como as de desenvolvimento, foi relativamente simples. No entanto, organizar equipas de suporte – como infraestrutura, gestão de projetos, gestão de produto e análise de negócios – revelou-se mais complexo. Passámos por várias iterações para encontrar as combinações certas.

Seguindo o princípio do pensamento de equipa em primeiro lugar delineado em Team Topologies por Matthew Skelton e Manuel Pais, nós:

  • Criámos equipas pequenas e autónomas (4–7 membros) para promover um profundo conhecimento do domínio e responsabilidade.
  • Eliminámos as dependências entre equipas, garantindo que os indivíduos estivessem totalmente alocados a uma única equipa.
  • Iterámos nas estruturas das equipas, particularmente para funções de suporte como infraestrutura e gestão de produto, para otimizar o fluxo.

Utilizar Ferramentas para Visibilidade e Gestão

Assim que as equipas foram estruturadas, passámos a utilizar o Azure DevOps para a gestão de backlog e de trabalho. Uma plataforma unificada permitiu:

  • Maior visibilidade do trabalho para que as equipas pudessem acompanhar o progresso de forma transparente.
  • Definições padronizadas de “concluído” para alinhar as expectativas entre as diferentes funções.
  • Melhor gestão do backlog, reduzindo a expansão do âmbito e os conflitos de priorização.

Aplicação de Métricas de Qualidade Objetivas

Para resolver a questão das medidas de qualidade pouco claras antes de marcar as tarefas como “prontas para teste”, integrámos uma ferramenta de análise de código estático, fornecendo informações objetivas sobre:

  • Cobertura de código
  • Vulnerabilidades de segurança
  • Anomalias no código

Também melhorámos a nossa definição de “Concluído” para tornar as verificações de qualidade um passo obrigatório antes de marcar os itens de trabalho como concluídos.

Gestão do Trabalho em Curso (WIP)

Uma das mudanças mais impactantes foi monitorizar e limitar o Trabalho em Curso (WIP). Um WIP elevado indicava estrangulamentos, permitindo-nos abordar proativamente as áreas onde o trabalho estava a estagnar.

Esta abordagem sistemática, baseada em The Goal e Team Topologies, lançou as bases para a melhoria contínua, reduzindo as dependências e melhorando a responsabilização.

Na nossa jornada de transformação DevOps, uma das constatações mais significativas foi que, após as melhorias iniciais, a principal restrição ao fluxo mudou para o design de software – especificamente, a testabilidade do nosso código. Após abordar os estrangulamentos iniciais, melhorando a configuração da equipa, definindo uma “Definição de Concluído” e implementando a análise de código, notámos que o trabalho se acumulava durante a fase de testes. O código desenvolvido pelas equipas scrum estava frequentemente à espera de ser testado pelos membros de QA dentro das mesmas equipas scrum.

Abordar o Próximo Gargalo: A Lacuna nos Testes

Com os gargalos iniciais resolvidos, surgiu uma nova restrição: os atrasos nos testes. Embora as tarefas de desenvolvimento estivessem a progredir eficientemente, os testes tornaram-se um obstáculo, impedindo lançamentos mais rápidos. Após investigação, identificámos os principais desafios que atrasavam o processo:

  • Dependência de testes manuais: A maioria dos testes era executada manualmente, levando a ciclos de feedback lentos e deteção tardia de defeitos.
  • Dependência da UI nos testes: Uma vez que os componentes da UI eram tipicamente concluídos por último, os testes não podiam começar senão numa fase avançada do ciclo de desenvolvimento.
  • Falta de automação proativa: Testes automatizados, como os testes de UI baseados em Selenium, eram escritos apenas após os testes manuais, limitando a sua eficácia na validação em fases iniciais.

Transição para Abordagens de Teste Primeiro

Para quebrar este gargalo, priorizámos os testes unitários e os testes de API em detrimento dos testes de UI. Esta mudança exigiu alterações fundamentais na mentalidade de desenvolvimento e no design de software:

Melhorias nos Testes de API

Para tornar os testes de API eficientes:

  1. APIs Bem Definidas: As APIs precisavam de ser simples, bem documentadas e disponíveis numa fase inicial do desenvolvimento para que os testadores pudessem criar casos de teste de forma proativa.
  2. Evitar a Base de Dados como Ponto de Integração:
    • A utilização de bases de dados como ponto de integração entre equipas criou dependências que atrasavam os testes.
    • A criação de casos de teste exigia a configuração de bases de dados com dados complexos, o que aumentava o tempo de configuração e limitava a capacidade de testar múltiplos cenários.
    • Ao mudar para uma integração baseada em API (principalmente REST sobre HTTP e, mais recentemente, GraphQL), reduzimos significativamente os atrasos e as complexidades causados pelas dependências de bases de dados.

Esta mudança permitiu um progresso mais rápido na automatização de casos de teste para APIs, mostrando que focar-se em designs API-first melhorou tanto a testabilidade quanto a eficiência.

Reforçar as Práticas de Testes Unitários

Os testes unitários representaram um desafio maior:

  • Progresso Rápido, Depois Teatro de Cobertura: Inicialmente, alcançámos níveis elevados de cobertura de código, mas as revisões de código revelaram que muitos testes unitários eram superficiais, escritos apenas para cumprir os objetivos de cobertura. Estes testes não conseguiam verificar funcionalidades significativas, cobrir cenários de falha ou validar casos-limite.
  • Mudança de Mentalidades: Os programadores precisavam de compreender o valor dos testes unitários – não apenas para melhorar a qualidade do código, mas também para acelerar o desenvolvimento ao detetar problemas precocemente.

Desafios na Escrita de Código Testável

O maior obstáculo para testes unitários eficazes foi a falta de testabilidade na própria base de código. Os principais problemas incluíam:

  • Funções grandes e monolíticas.
  • Componentes fortemente acoplados.
  • Fraca separação de responsabilidades.
  • Abstração insuficiente.

Estas questões dificultaram o isolamento e o teste eficaz de unidades individuais. Para resolver isto, nós:

  1. Diretrizes Fornecidas: Partilhámos as melhores práticas sobre:
    • Seleção de funções para testes unitários.
    • Refatorização de código para melhorar a testabilidade.
    • Conceber e usar mocks para facilitar os testes.
  2. Focados em Melhorias Incrementais: Os programadores foram incentivados a fazer pequenas e significativas alterações para melhorar a testabilidade ao longo do tempo.

Progresso, Desafios e o Caminho a Seguir

Para produtos legados, melhorar a testabilidade permaneceu um processo lento devido a restrições arquitetónicas. No entanto, estas ações não visavam apenas melhorar a base de código atual – eram um investimento no futuro:

  • Os programadores desenvolveram melhores hábitos, incorporando a testabilidade em novas bases de código.
  • As equipas tornaram-se mais autossuficientes, reduzindo a dependência de esforços externos de QA.
  • A organização evitou repetir erros do passado, garantindo que os produtos futuros fossem mais fáceis de manter e evoluir.

A principal conclusão? DevOps não se trata de implementar ferramentas ou listas de verificação. Trata-se de melhorar continuamente o fluxo de trabalho, uma restrição de cada vez.

No próximo capítulo desta série, exploraremos “Conceber para a Testabilidade”. Aprofundaremos como a melhoria do design de software pode remover as restrições de teste, permitindo ciclos de feedback mais rápidos e maior qualidade de software – primeiro nas estruturas de equipa, depois na gestão do fluxo de trabalho e, finalmente, na testabilidade – estabelecemos as bases para uma transformação DevOps sustentável.

Subscrever o Blogue da Freyr

Política de Privacidade