Este artigo é uma continuação da Parte 1.
Loops de Feedback no Desenvolvimento do Pipeline
Para criar rapidamente novos recursos ou corrigir bugs no software, os desenvolvedores precisam de um feedback rápido que diga a eles que tudo o que escreveram está correto e mova o software na direção certa.
No desenvolvimento de software, isso normalmente é obtido usando um conjunto de testes de unidade, que o desenvolvedor executa localmente para verificar se cada componente do software (ainda) funciona como pretendido.
Os testes de unidade devem ser rápidos, não interagir com nenhum sistema externo nem depender de nenhum estado. Eles devem testar funções, métodos e classes de forma independente e isolada.
Dessa forma, um desenvolvedor de software obtém feedback rápido durante o desenvolvimento e pode ter certeza de que seu código funcionará conforme o esperado quando receber uma solicitação. Se houver necessidade de testar a interação com outros sistemas, um pipeline de CI também pode executar testes de integração mais lentos.
Aqui está um segredo de engenharia de dados: os pipelines de dados raramente são testados em unidade (suspiro!). Os pipelines de dados geralmente são testados simplesmente implantando-os — geralmente primeiro em um ambiente de teste ou homologação. Isso requer uma etapa de construção e implantação, após a qual o Engenheiro de Dados deve monitorar o pipeline por um tempo para ver se funciona conforme o esperado. Uma reimplantação do pipeline pode primeiro exigir intervenção manual para redefinir o estado deixado pela implantação anterior. Esse ciclo de feedback é muito lento em comparação com a execução de testes de unidade.
Então, por que não apenas escrever testes de unidade? Aqui estão as razões:
1- Os pipelines de dados falham em aspectos que não podem ser testados na unidade
A lógica independente que pode ser testada na unidade geralmente é limitada em pipelines de dados. Portanto, quase todas as falhas ocorrem em interfaces defeituosas entre sistemas ou dados inesperados que entram no pipeline.
Interfaces entre sistemas não podem ser testadas com testes unitários, pois esses testes não podem ser executados isoladamente. Na realidade, o Engenheiro de Dados raramente conhece todos os detalhes relevantes.
Vamos dar um exemplo do mundo real: para evitar ataques DDOS, uma API pública pode ter um limite não divulgado no número de solicitações que podem ser enviadas do mesmo IP dentro de um determinado intervalo de tempo. Uma simulação da API provavelmente não explicaria isso, mas o fato de existir no sistema real pode interromper o pipeline na produção. Além disso, os sistemas externos raramente são estáveis. Na verdade, os pipelines de dados geralmente são construídos porque as pessoas querem mover dados de sistemas duvidosos para sistemas mais confiáveis. Uma simulação não revelaria se o sistema real mudou de forma a quebrar o pipeline.
Os produtores de dados são notoriamente ruins em entregar dados de qualidade de forma consistente. Um pipeline de dados sempre deve fazer algumas suposições sobre os dados que serão alimentados nele. Conteúdo ou estrutura inesperada nos dados interromperá o pipeline ou, no mínimo, produzirá um resultado incorreto. Uma estratégia comum para defender o pipeline contra fontes de dados malfeitas é verificar os esquemas na leitura. Mas isso não protege contra conteúdo errôneo dos dados e “bugs de dados” sutis.
Por exemplo, uma série temporal lida corretamente com o horário de verão? Existem strings em uma coluna que não seguem um padrão esperado? Os valores em uma coluna numérica, representando medições do mundo real, fazem sentido físico? Nada disso está relacionado à lógica do pipeline que pode ser testada com testes de unidade.
2- Os testes de unidade são mais elaborados do que a lógica do pipeline
Os testes de unidade necessários para testar a lógica de transformação independente limitada em um pipeline são mais complexos do que o próprio código, pois exigem que o Engenheiro de Dados crie dados de teste representativos, bem como os dados de saída esperados.
É muito trabalho que não melhora substancialmente a confiança no funcionamento correto do pipeline. Além disso, isso muda a questão de “essa função funciona conforme o esperado?” para “esses dados de teste representam meus dados reais adequadamente?”.
Os testes de unidade cobrem idealmente um bom subconjunto de combinações de parâmetros de entrada. Mas em funções que transformam um conjunto de dados, por exemplo na forma de um dataframe, o próprio argumento do conjunto de dados apresenta um espaço de parâmetro dimensional quase infinito.
Desenvolver Um Pipeline é Lento
A melhor maneira de obter feedback confiável sobre um pipeline de dados é implantá-lo e executá-lo. Isso sempre será mais lento do que executar testes de unidade localmente, o que significa que leva muito mais tempo para obter feedback. O resultado é que o desenvolvimento do pipeline, especialmente no estágio de depuração, é irritantemente lento.
Testes de integração mais rápidos do que executar todo o pipeline podem ser considerados. No entanto, eles geralmente não podem ser executados na máquina de um Engenheiro de Dados, pois ele não tem acesso direto aos sistemas de origem relevantes. Portanto, esses testes só podem ser executados no mesmo ambiente do pipeline, o que novamente requer uma implantação. Isso anula em grande parte o objetivo de escrever testes para obter feedback rápido.
Os “contratos de dados” agora estão na moda para lidar com produtores de dados imprudentes. Ter confiança nos dados que entram no pipeline eliminaria muita incerteza do desenvolvimento do pipeline e os tornaria menos frágeis.
No entanto, esses contratos são difíceis de cumprir, pois os produtores não são incentivados a cumpri-los. Além disso, uma organização desejará usar dados extraídos de fontes externas, como APIs públicas; boa sorte negociando um contrato de dados com essas partes externas.
Desenvolvimento de Pipeline Não Pode Ser Paralelizado
Estabelecemos que os pipelines de dados são uma única história sobre os dados que demora a se desenvolver devido ao longo ciclo de feedback. Mas como um pipeline é composto por várias tarefas, alguns gerentes tentam distribuí-las entre vários desenvolvedores na tentativa de agilizar o processo.
Infelizmente, isso não funciona.
As tarefas para processar os dados em um pipeline são sequenciais. Para construir a segunda etapa, a saída da primeira etapa deve ser estável. Os insights obtidos com a construção da segunda etapa retroalimentam as melhorias na primeira etapa. Como tal, o pipeline como um todo deve ser considerado como um recurso no qual um desenvolvedor itera.
Alguns gerentes respondem que isso simplesmente significa que o pipeline não foi suficientemente planejado desde o início. Há dados no início e está claro quais dados precisam ser divulgados no final. Então não é óbvio o que precisa ser construído no meio? Paradoxalmente, os mesmos gerentes que usam esse argumento defenderão veementemente os méritos do Agile.
Planejar todo o pipeline não funciona enquanto a fonte de dados não for caracterizada adequadamente. Na ausência de contratos e documentação, o Engenheiro de Dados tem que tropeçar no escuro para descobrir as particularidades dos dados. Esse processo de descoberta molda a arquitetura do pipeline. Em certo sentido, isso é ágil; simplesmente não é como as partes interessadas do negócio querem que funcione.
Conclusão e Recomendação
Os pipelines de dados são software, mas não produtos de software. Eles são a fábrica que serve para construir o carro que o cliente realmente pediu. Eles são um meio para um fim, uma receita de automação para criar um conjunto de dados facilmente consumível a partir de fontes de dados.
Eles são a fita adesiva entre sistemas que não foram projetados para conversar entre si. Seu único objetivo é gerenciar o estado, o que torna seu desenvolvimento lento, imprevisível e frequentemente o principal gargalo para projetos de análise de dados.
Não importa o que os influenciadores de dados afirmem ao vender mais uma ferramenta, não importa quantas camadas de abstração sejam empilhadas umas sobre as outras, os dados são fundamentalmente diferentes do software. Não reconhecer essas diferenças e aplicar processos ágeis em uma equipe de dados porque funcionou na equipe de software só vai sair pela culatra.
E o que pode ser feito para tornar uma equipe de dados bem-sucedida e produtiva?
1- Dê aos Engenheiros de Dados algum tempo para experimentar as fontes de dados. Assuma que todas as estimativas de tempo sobre quando o conjunto de dados estará disponível estarão erradas.
2- Não divida um pipeline entre vários desenvolvedores. Em vez disso, permita que dois ou vários desenvolvedores colaborem simultaneamente em um pipeline. A programação par/extrema/de grupo com desenvolvimento baseado em tronco garante produtividade máxima, pois evita o inferno de ramificação do git, solicitações pull e revisões de código. Ter quatro olhos inspecionando o código o tempo todo ajuda a detectar problemas com antecedência. Isso é especialmente valioso em pipelines onde os loops de feedback são lentos.
Por isso as oportunidades para Engenheiros de Dados estão aí e são cada vez maiores.
David Matos
Referências: