Por tudo que vimos nos últimos anos, parece que a engenharia de dados está convergindo com o DevOps. Ambos adotaram infraestrutura em nuvem, conteinerização, CI/CD e GitOps para fornecer produtos digitais confiáveis a seus clientes.
A convergência em um subconjunto de ferramentas levou muitos à opinião de que não há distinção significativa entre engenharia de dados e engenharia de software. Consequentemente, o fato de que a engenharia de dados é bastante “áspera” é simplesmente porque os Engenheiros de Dados ficam para trás na adoção de boas práticas de desenvolvimento de software.
Essa avaliação é equivocada. A engenharia de dados e a engenharia de software compartilham muitas ferramentas e práticas comuns, mas também diferem substancialmente em várias áreas-chave. Ignorar essas diferenças e gerenciar uma equipe de engenharia de dados como uma equipe de produto de software é um erro. Pense na engenharia de dados como um tomate: é uma fruta, mas isso não significa que deva ser adicionado a uma salada de frutas.
Este post (dividido em duas partes) tem como objetivo destacar alguns dos desafios exclusivos da engenharia de dados e por que isso requer uma abordagem personalizada.
Pipelines de Dados Não São Aplicativos
A engenharia de software tende a se preocupar com a construção de aplicativos. No contexto deste post, um aplicativo é definido de forma muito ampla e pode ser um site, um aplicativo de desktop, uma API, um aplicativo de mainframe monolítico, um jogo, um microsserviço, uma biblioteca ou qualquer coisa intermediária. As características que todos compartilham são que eles:
1- Fornecem valor aos usuários oferecendo uma nova modalidade de interação. Um jogo pode ser jogado, um site pode ser navegado, uma API pode ser utilizada em outro software.
2- Têm uma série de recursos independentes. Um site pode aumentar seu número de páginas, um jogo pode crescer em número de níveis ou personagens jogáveis, uma API pode adicionar mais endpoints. Um aplicativo nunca é verdadeiramente completo.
3- Lidam com uma quantidade relativamente pequena de estado que eles criam. O estado é projetado para oferecer suporte ao aplicativo. O gerenciamento desse estado geralmente é transferido para um sistema externo. O objetivo é que grandes partes do software sejam sem estado. Um aplicativo web pode ser encerrado e reiniciado a qualquer momento; seu estado é gerenciado por um banco de dados executado em um processo separado.
4- São vagamente acoplados a outros softwares e serviços. Um bom software deve funcionar de forma independente em qualquer ambiente, daí a popularidade dos microsserviços e contêineres.
Já os Engenheiros de Dados estão preocupados em construir pipelines de dados. Os pipelines de dados pegam os dados do local onde são produzidos, os transformam e os colocam em outro local de onde são consumidos. Normalmente, o objetivo é automatizar esses pipelines para que os conjuntos de dados sejam atualizados ao longo do tempo com novos dados.
Assim como os aplicativos, os pipelines de dados geralmente são peças de software. Mas, ao contrário dos aplicativos, um pipeline de dados:
- Não fornece nenhum valor direto. Não há usuários do pipeline. Somente o conjunto de dados produzido pelo pipeline é valioso para os consumidores. Se os dados chegassem ao seu destino por meio de algum esquema elaborado de copiar e colar, o cliente ficaria igualmente satisfeito.
- Possui apenas uma única característica de relevância para o cliente: produzir o conjunto de dados que foi solicitado.
- Gerencia uma grande quantidade de estados. Um pipeline é projetado para processar o estado existente de outro software que ele não controla e convertê-lo no estado que ele controla. Muitos pipelines criam conjuntos de dados de forma incremental, adicionando mais dados a cada execução. Nesse sentido, esses pipelines podem ser vistos como processos de longa duração que criam continuamente mais e mais estados.
- Tem acoplamento inevitável. O objetivo de um pipeline de dados é precisamente vincular-se a uma fonte de dados. O pipeline sempre será tão estável e confiável quanto a fonte.
Essas diferenças fundamentais levam a desafios únicos para a engenharia de dados que geralmente são mal compreendidos pelas áreas de negócio, pelo gerenciamento de TI e até mesmo pelos engenheiros de software.
Um Pipeline Está Concluído ou Sem Valor
Muitas organizações atualmente gerenciam suas equipes de software sob algum tipo de Agile. A filosofia central dessas estruturas é maximizar a velocidade com que o software agrega valor ao cliente, criando e lançando software em iterações curtas. O objetivo é entregar um produto minimamente viável (MVP) o mais rápido possível e garantir ciclos de feedback rápidos, garantindo assim que a equipe esteja sempre trabalhando nos recursos com a maior prioridade.
Essas ideias não mapeiam a engenharia de dados.
Um pipeline de dados não pode ser desenvolvido em pequenas iterações para aumentar o valor do cliente. Não há equivalente MVP em um pipeline de dados. Ele produz o conjunto de dados desejado para o cliente ou não.
Consequentemente, o desenvolvimento do pipeline de dados não se encaixa perfeitamente em estruturas ágeis. Um pipeline de dados complexo corresponde a uma única história de usuário, mas geralmente requer que vários sprints sejam concluídos.
O gerenciamento não técnico raramente leva em consideração esse ponto sutil e tenta encaixar Engenheiros de Dados em equipes scrum de qualquer maneira. O resultado é que as histórias do usuário são substituídas por tarefas, por exemplo, “build API connector” e “build ingestion logic”, que inevitavelmente transformam o scrum board em um dispositivo de microgerenciamento.
Quando a administração não entende os fundamentos do que gerencia, ela tende a tomar decisões ruins e impraticáveis. Frustrado com o progresso lento em um pipeline, um gerente pode exigir de um Engenheiro de Dados que construa o conjunto de dados iterativamente, coluna por coluna, para que o cliente “pelo menos tenha alguns dados para começar a trabalhar”. Engenheiros de Dados com experiência prática em pipelines complexos e Cientistas de Dados que já receberam um conjunto de dados inútil reconhecerão o ridículo dessa situação. Para os leitores sem esse histórico, aqui estão três razões pelas quais isso não funciona:
1- Um conjunto de dados parcial não tem utilidade proporcional
Se um conjunto de dados contém 9 de 10 colunas, é 90% útil? Depende de qual coluna é omitida. Se um Cientista de Dados pretende construir um modelo preditivo com base nos dados, mas a coluna que está faltando são os rótulos ou valores que deseja prever, o conjunto de dados é 0% útil. Se a coluna for algum metadado aleatório não correlacionado com os rótulos, pode ser 100% útil.
Mais comumente, uma coluna representa um campo que pode ou não estar correlacionado com os rótulos; descobrir se está correlacionado é exatamente o que o Cientista de Dados deseja descobrir por meio da experimentação. É por isso que um Cientista de Dados deseja um conjunto de dados o mais completo possível para começar a experimentar, explorar e otimizar gradualmente seu modelo. Fornecer a eles um conjunto de dados parcial garante que a experimentação e a otimização precisem ser refeitas assim que campos adicionais estiverem disponíveis.
2- O tempo para desenvolver um pipeline não está correlacionado com o tamanho do conjunto de dados
Mesmo que um cliente ficasse satisfeito com metade de um conjunto de dados, produzi-lo não levaria metade do tempo que leva para o conjunto de dados completo.
Um pipeline de dados não consiste em trabalhos independentes em que cada um produz uma coluna. Várias colunas podem estar relacionadas se forem originárias da mesma fonte, portanto, incluir uma ou várias no conjunto de dados final é a mesma quantidade de trabalho.
No entanto, a lógica para mesclar essas colunas com colunas em outra tabela pode ser tão simples quanto uma única junção ou pode exigir uma série complicada de funções de janela. Além disso, muitas regras podem precisar ser escritas em um pipeline de dados antes que qualquer dado saia na outra extremidade, por exemplo, permissão para um cliente acessar uma API ou um analista para processar dados não estruturados. Uma vez que isso esteja em vigor, expandir a lógica para processar campos adicionais provavelmente é trivial. Portanto, o número de colunas no conjunto de dados final é uma métrica terrível para a complexidade do pipeline, a par do uso do número de linhas de código para medir a produtividade.
O tamanho do conjunto de dados também pode se referir ao número de linhas/registros. Um pipeline bem projetado deve ser capaz de lidar com qualquer número de registros, portanto, o tempo de desenvolvimento deve ser totalmente desacoplado. No entanto, pode haver “saltos” no tempo de desenvolvimento dependendo de requisitos específicos como:
- Com que frequência o conjunto de dados precisa ser atualizado, ou seja, precisamos de lote ou streaming?
- Quais são os volumes de dados esperados e a velocidade?
- Os dados cabem na memória RAM?
Essas considerações devem ser conhecidas a priori e influenciarão todo o projeto do pipeline de dados.
3- O tempo e o custo econômico para construir um conjunto de dados estão correlacionados com seu tamanho
Quanto maior for um conjunto de dados, tanto em termos de linhas quanto de colunas, mais tempo levará para construí-lo e atualizá-lo.
Editar um único registro em um banco de dados enorme é trivial e rápido, mas esse é um cenário incomum para conjuntos de dados analíticos. Modificar um conjunto de dados analíticos normalmente envolve adicionar/alterar colunas inteiras (o que altera todos os registros) ou atualizar milhares ou milhões de linhas. Existem duas maneiras de lidar com mudanças nos dados, e nenhuma delas é barata.
Do ponto de vista do desenvolvedor, a maneira mais fácil de atualizar um conjunto de dados é sobrescrever tudo atualizando e executando novamente o pipeline. No entanto, este é o mais caro em termos de custos de computação e tempo para atualizar o conjunto de dados. Projetar um pipeline de modo que ele sobrescreva corretamente o estado de execuções anteriores também nem sempre é trivial e requer planejamento inicial adequado.
Como alternativa, a lógica para atualizar um conjunto de dados pode ser codificada em um pipeline separado que usa o conjunto de dados antigo como entrada. Isso pode ser mais econômico em termos de custo e velocidade de computação, mas incorre em tempo de desenvolvimento adicional e sobrecarga mental. Os pipelines que aplicam deltas não são idempotentes, por isso é importante acompanhar o estado atual, bem como quando operações específicas foram executadas. Mesmo nesse segundo cenário, o pipeline antigo deve ser atualizado para incluir a alteração desejada nas novas atualizações.
De qualquer forma, o problema é cortado em cubos, os conjuntos de dados têm uma inércia inerente. Tentar fazer alterações requer mais tempo, esforço e/ou dinheiro quanto maior for o conjunto de dados.
Conclusão da Parte 1: Implantar Um Pipeline Parcial Para Produção é Um Desperdício
A implantação de um pipeline parcialmente concluído para produção não é útil para o cliente, desperdiça computação e complica a vida dos Engenheiros de Dados que constroem o pipeline, pois eles terão que lidar com o estado antigo. O cultivo de cargas DevOps e princípios ágeis sobre o desenvolvimento do pipeline de dados, no qual mudanças incrementais e implantações frequentes são incentivadas, simplesmente ignora a inércia dos dados.
Os Engenheiros de Dados gostariam de “fazer certo da primeira vez” e minimizar o número de implantações na produção. Um pipeline implantado com frequência significa que o cliente não sabe o que deseja ou que a fonte de dados é muito instável e o pipeline precisa de patches constantes. Ao contrário dos aplicativos sem estado, em que as atualizações tendem a ser tão fáceis quanto eliminar alguns contêineres e criar novos, atualizar um conjunto de dados não é o mesmo que reimplantar o código do pipeline. Empacotar o código do pipeline em um contêiner e executá-lo no Kubernetes não preencherá essa lacuna.
Continuamos na Parte 2.
David Matos
Referências:
1 thought on “Engenharia de Dados Não é Engenharia de Software – Parte 1”