BI e engenharia de dados
O que acontece entre a API do anunciante ter o dado e o gráfico estar na tela
A rodada tinha horário de início e não tinha horário de término. Nenhum log, nenhuma exceção, nenhum alerta — e o painel do dia seguinte mostrou um número menor que era perfeitamente plausível.
Bruno Chipelo · Mídia, medição e engenharia de dados 12 de agosto de 2026 11 min de leitura · 2.121 palavras Parte 2 de 2 · Engenharia de dados aplicada a marketing
01 O sintoma
A rodada que morreu sem deixar rastro
Na tabela de estado do pipeline, a linha daquela fonte tinha o horário de início preenchido e o de término nulo. Só isso. Nenhuma linha de log, nenhuma exceção capturada, nenhum alerta disparado. Do ponto de vista de qualquer ferramenta de monitoração, não havia acontecido nada de errado — não havia acontecido nada.
A rodada tinha sido interrompida por limite de recurso no meio da carga. Interrupção assim não gera exceção aplicacional: o processo é encerrado por quem está por fora dele, e o código nem chega a saber que vai morrer. O que sobra é a assimetria na tabela de estado — começou, não terminou.
O painel do dia seguinte mostrou um número menor. E o número menor era plausível: dia de semana, verba estável, nada gritante. Falha que grita é barata, porque alguém vê. Cara é a que continua verossímil — e quase tudo que se projeta num pipeline de dados existe para transformar a segunda na primeira.
Este texto descreve o desenho: as quatro etapas do pipeline e o que decide cada fronteira, cinco defeitos que pagamos para aprender, quando o desenho não deve ser esse — e como se prova, com testemunha externa, que uma refatoração não mudou nada.
02 O conceito
Extrair, normalizar, carregar, servir — e o que decide cada fronteira
Um pipeline de dados de marketing tem quatro etapas, e o desenho inteiro é decidido pelo que se coloca em cada fronteira entre elas. Reis e Housley chamam isso de ciclo de vida da engenharia de dados; o vocabulário importa menos que a disciplina de não misturar as etapas.
Extrair é falar com a fonte e trazer o que ela tem, no formato dela, sem opinião. Normalizar é onde mora quase todo o trabalho: converter tipo e fuso, decidir o que é chave, traduzir o nome que cada plataforma dá ao mesmo canal, descartar a linha duplicada e marcar de onde cada campo veio. Carregar é escrever de um jeito que possa ser repetido sem estrago. Servir é a agregação da leitura — e é etapa separada de propósito, porque o dia em que a pergunta mudar não pode ser o dia em que o histórico precisa ser recarregado.
Onde Python entra, e onde termina
Python faz a extração e a normalização. É onde estão os SDK oficiais das plataformas, os clientes HTTP com política de repetição decente e as ferramentas de manipulação de dados. Ele termina quando o dado está conformado e escrito. A agregação acontece em SQL, na leitura, dentro do banco — trazer uma tabela para a memória do processo só para devolvê-la somada é trabalho e risco a mais, e piora com o tempo à medida que a base cresce.
Explicitar esse limite vale mais que defender a linguagem. A escolha é por trecho, não por gosto: extração e conformação em Python porque a variedade das APIs é o problema; agregação em SQL porque o volume é o problema; a rotina agendada rodando na borda porque a disponibilidade é o problema. Uma linguagem que faz tudo bem em toda etapa não existe, e o pipeline que finge que existe acaba com regra de negócio espalhada em três lugares.
Idempotência como requisito, não como virtude
Rodar de novo tem de ser barato e seguro. Se rodar duas vezes muda o resultado, o pipeline não é confiável — é sortudo. Isso se paga na primeira falha real: com idempotência, a resposta a "a carga de ontem quebrou no meio" é rodar de novo e ir dormir; sem ela, é abrir o banco e reconciliar à mão, às onze da noite, num estado que ninguém conhece.
Na prática isso significa gravar de forma que a repetição seja inofensiva: uma chave que identifique a linha sem ambiguidade e uma escrita que substitua a versão anterior daquela chave em vez de acrescentar outra. Kleppmann é claro sobre o motivo de isso ser requisito e não elegância: entrega exatamente uma vez é ilusão em sistemas distribuídos, e o que existe de verdade é entrega pelo menos uma vez somada a uma operação que tolera repetição.
Janela móvel e marca d’água
Reprocessar toda a série todo dia é caro e desnecessário; reprocessar só o dia de ontem é barato e errado. Dado de marketing amadurece: conversão é reprocessada, atribuição é retroativa, pedido muda de status depois de faturado, ferramenta de busca só publica o dia depois de alguns dias. A janela precisa caber na latência da fonte — reprocessa-se os últimos N dias, com N escolhido pelo tempo que aquela fonte leva para parar de mudar de ideia.
A marca d’água é o registro de até onde o dado é considerado final. Ela existe para que a leitura saiba o que ainda pode mudar, e é o que permite marcar o período corrente como parcial numa tela em vez de deixá-lo cair no gráfico como se fosse queda de desempenho. Quase toda "queda no último dia" de painel é ausência de marca d’água.
Qual é o recurso escasso
Otimizar antes de saber o que é caro é adivinhação. Nem sempre o escasso é o tempo. Quando o caro é a escrita — ambiente com cota de linhas gravadas, banco de borda, API com limite de chamadas —, o desenho inteiro muda: em vez de reescrever a fatia toda, lê-se o que já está lá, compara-se com o que chegou e grava-se apenas o que mudou. O mesmo pipeline, com o mesmo resultado final, passa a custar uma fração.
Identificar o recurso escasso antes de escrever a primeira linha é metade do projeto. A outra metade é aceitar que ele muda: o que era limitado por chamada de API vira limitado por memória quando a base dobra, e o desenho precisa ser refeito na fronteira certa.
Fonte isolada
Um pipeline com quatro fontes não pode deixar a falha de uma derrubar as outras três. Cada fonte registra o próprio estado — início, término, contagem de linhas, erro — e o orquestrador segue para a seguinte independentemente do que aconteceu na anterior. O ganho não é só disponibilidade: é diagnóstico. Com estado por fonte, "o painel está desatualizado" vira "a fonte tal parou de responder às 3h", que é uma frase acionável.
03 Como aplicamos
Cinco defeitos que custaram caro
O upsert que apaga o que não veio
Uma escrita do tipo "insere ou substitui" grava a linha inteira. A coluna que não estava no payload não fica como estava: volta vazia. O comando acerta exatamente o que você mandou e apaga o resto, sem erro e sem aviso. A mesma armadilha aparece na ordem de uma migração: se o backfill de uma coluna nova começar antes de a rotina de sincronismo saber que ela existe, a rotina antiga reescreve com vazio o que o backfill acabou de preencher, e as duas ficam brigando em silêncio. Publicar as duas pontas antes de carregar resolve.
A fonte portada sem retry
Uma extração que funcionava havia meses foi portada por nós para outro ambiente. A tradução ficou fiel em tudo, menos na política de repetição, que não veio junto. A primeira rodada morreu em um segundo, no primeiro erro transitório da API — o tipo de erro que aquela fonte devolve várias vezes por semana e que a versão original absorvia sem ninguém notar. O retry não era detalhe de implementação: era metade da razão de a versão antiga parecer estável.
A morte que não loga
É o caso do começo deste texto. Interrupção por limite de recurso não gera exceção aplicacional, então não existe except capaz de registrá-la. O único sinal é o registro de execução com início preenchido e término nulo. Isso muda o que se monitora: em vez de vasculhar log atrás de erro, verifica-se a tabela de estado atrás de rodada que começou e não terminou — e é essa checagem, não o log, que vira alerta.
Segredo nunca em query string
Token passado na URL vaza no traceback: a biblioteca HTTP imprime a URL inteira quando levanta o erro, e o log de execução passa a guardar a credencial em texto puro. Detalhe pequeno, consequência desproporcional. Vale também para a paginação — alguns serviços devolvem o link da próxima página com o token embutido, de modo que reaproveitar esse link propaga o segredo para dentro de todo o resto do código. Credencial vai no cabeçalho, e o filtro de log mascara o que passar por perto.
Fusos diferentes na mesma coluna
Uma plataforma de anúncio reportava no fuso configurado na conta; o analytics do site, em outro. No agregado mensal isso é invisível. No grão diário, as duas séries desencontram em uma hora — e o desencontro se concentra nas transações do fim da noite, que mudam de dia. A comparação lado a lado fica errada sem que nada acuse, porque as duas séries continuam internamente consistentes. Fuso é atributo da fonte, não do banco: ou se normaliza na entrada, ou se guarda o fuso junto e se converte na leitura. O que não dá é misturar os dois na mesma coluna.
04 Trade-offs
Quando o pipeline não deve ser assim
Quando não há API
Nem toda conta tem credencial de API — algumas plataformas exigem aprovação que leva meses, outras não liberam para quem não é parceiro. Nesses casos a extração pode viver como rotina agendada dentro do próprio painel de anúncios, gravando numa planilha que o pipeline lê depois. É menos elegante, tem menos garantia de execução e resolve. Tem inclusive uma vantagem que a solução elegante não tem: nenhum segredo nosso passa a morar dentro da conta do cliente.
Quando não é Python
Se o dado já está no banco, a transformação é SQL. A tentação de trazer para um dataframe é grande porque a manipulação é confortável; o custo aparece depois, quando a regra de negócio passa a existir em dois dialetos e a correção só chega a um deles. A fronteira útil é geográfica, não estética: enquanto o dado está vindo de fora e ainda não tem forma, é Python; depois que ele está gravado e tipado, é do banco. Cruzar essa linha nos dois sentidos é o que produz pipeline em que ninguém sabe mais onde uma regra é aplicada.
Pipeline próprio ou ferramenta gerenciada
Não há veredito aqui, há critério. Quantas fontes existem e quantas delas a ferramenta já cobre de fábrica; quão previsíveis são essas APIs, porque conector genérico envelhece mal em plataforma que muda de contrato; quem mantém o que quebrar às sete da manhã; e quanto custa uma hora de indisponibilidade do painel. Com poucas fontes bem cobertas e ninguém disponível para manter, a ferramenta gerenciada ganha com folga. Com fontes que exigem regra própria de conformação, ela vira uma camada a mais para depurar.
05 Como provamos
Como se afirma que uma mudança não mudou nada
A pergunta mais difícil de uma refatoração de pipeline não é "funciona?", é "o resultado continua idêntico?". Feathers chama de teste de caracterização: não se escreve o teste a partir da especificação, e sim a partir do comportamento atual, inclusive das esquisitices dele.
- Capturar a versão antiga verbatim, por cópia de arquivo — nunca redigitando. Redigitar já é reinterpretar, e a diferença aparece justamente no detalhe que a memória não guarda.
- Rodar as duas versões ponta a ponta contra o artefato real de produção, em modo somente leitura, e comparar a saída inteira, não uma amostra.
- Publicar o número da comparação junto com o tamanho da base, para que quem lê saiba o peso do "nada mudou".
Numa refatoração recente de um módulo de calendário, o resultado foi zero linha alterada numa base de cerca de dez mil. É um número pequeno e por isso mesmo dito com o denominador junto: taxa publicada sem tamanho de amostra é exatamente o defeito que este par de textos denuncia.
É a mesma lógica que sustenta o outro lado desta série: um número só é confiável quando existe uma segunda fonte capaz de contradizê-lo. No pipeline, essa fonte é o artefato antigo; no modelo, é a dimensão que precisa bater com o código que já estava em produção.
Perguntas frequentes
As respostas curtas, para quem chegou pela pergunta.
- Python ou SQL para transformar dado de marketing?
- Os dois, em trechos diferentes. Python faz extração e normalização, porque o problema ali é a variedade das APIs. A agregação é SQL, dentro do banco, porque ali o problema é volume. A fronteira é geográfica: enquanto o dado vem de fora e ainda não tem forma, é Python; depois de gravado e tipado, é do banco.
- O que é idempotência num pipeline, na prática?
- É poder rodar de novo sem estrago. Uma chave que identifica a linha sem ambiguidade e uma escrita que substitui a versão anterior daquela chave, em vez de acrescentar outra. Sem isso, a resposta a uma carga que quebrou no meio deixa de ser "rode de novo" e passa a ser reconciliação manual num estado que ninguém conhece.
- Quantos dias reprocessar na janela móvel?
- O tanto que a fonte leva para parar de mudar de ideia. Conversão reprocessada, atribuição retroativa e pedido que muda de status depois de faturado definem esse prazo, e ele é diferente por fonte. Reprocessar só ontem é barato e errado; reprocessar tudo todo dia é caro e desnecessário.
- Como descobrir que uma rodada falhou se não há erro no log?
- Interrupção por limite de recurso não gera exceção aplicacional, então não existe captura que a registre. O sinal fica na tabela de estado: início preenchido e término nulo. O alerta tem de olhar para essa assimetria, não para o log.
- Vale mais um pipeline próprio ou uma ferramenta gerenciada?
- Depende de quantas fontes existem e quantas a ferramenta cobre de fábrica, de quão previsíveis são essas APIs, de quem mantém o que quebrar às sete da manhã e de quanto custa uma hora de painel indisponível. Com poucas fontes bem cobertas e ninguém para manter, a ferramenta ganha. Com fontes que exigem regra própria de conformação, ela vira mais uma camada para depurar.
Bibliografia
De onde vem o que não inventamos.
- Martin Kleppmann, Designing Data-Intensive Applications idempotência, entrega pelo menos uma vez e por que exactly-once é uma propriedade do efeito, não da entrega.
- Maxime Beauchemin, "Functional Data Engineering: a modern paradigm for batch data processing" (2018) imutabilidade e sobrescrita de partição como base da repetição segura.
- Joe Reis e Matt Housley, Fundamentals of Data Engineering (O’Reilly, 2022) o ciclo de vida da engenharia de dados, que dá a espinha deste artigo.
- Michael Feathers, Working Effectively with Legacy Code testes de caracterização — escrever o teste a partir do comportamento existente.