De Notebooks em Python para Contratos em YAML: Como um framework de ingestão declarativa de PBs de dados acelerou a operação do Data Lake
← Voltar para Artigos

De Notebooks em Python para Contratos em YAML: Como um framework de ingestão declarativa de PBs de dados acelerou a operação do Data Lake

TL;DR

  1. Colocamos em produção uma stack declarativa de ingestão para o Data Lake baseada em contratos YAML.
  2. Hoje operamos uma quantidade massiva de dados com cerca de 7 PB de dados, ~8.000 tabelas transacionais e ~850 YAMLs declarativos.
  3. Saímos de um modelo espalhado via implementações locais para outro com 1 tabela : 1 YAML e 2 notebooks centrais.
  4. O novo fluxo já cobre cerca de 85% do caminho Source → Bronze → Silver.
  5. O tempo estimado para colocar uma nova ingestão no ar caiu de dias para horas.

O Problema de Escala que Virou Problema de Arquitetura

Durante muito tempo, o problema não era colocar dado no Data Lake. O problema era crescer sem transformar cada nova ingestão em mais custo estrutural.

Hoje, a CERC opera uma plataforma com cerca de 7 PB de dados e ~8.000 tabelas transacionais. Nessa escala, ingestão deixa de ser script. Ela vira infraestrutura de plataforma.

Enquanto a operação era menor, o modelo antigo parecia aceitável. Cada domínio criava seus próprios notebooks, seus próprios padrões e, em alguns casos, seu próprio repositório. Isso dava liberdade local. Também criava divergência estrutural.

Com o tempo, a conta apareceu. O esforço de manutenção passou a crescer mais rápido do que o valor entregue por cada nova fonte. O custo real não estava só em compute. Estava no tempo de engenharia gasto repetindo estrutura, revisando variações da mesma ideia e reconstruindo contexto a cada nova ingestão.

Esse problema ficava mais visível no fluxo Source → Bronze → Silver, que concentra uma parte grande da superfície operacional do Data Lake. Nesse trecho, pequenas diferenças de implementação viravam mais revisão, mais manutenção e menos velocidade.

As dores apareciam em quatro frentes:

Código repetido demais

Cada nova ingestão repetia a mesma base estrutural, com variações difíceis de governar.

Velocidade baixa

Criar uma fonte nova levava dias, porque o trabalho era implementar pipeline, não declarar ingestão.

Governança fraca

O padrão esperado nem sempre era o padrão executado, porque cada implementação tinha liberdade demais.

Custo cognitivo alto

Cada mudança exigia entender decisões locais antes de mexer em qualquer coisa.

Não era mais uma questão de estilo. Era uma questão de operabilidade.


A Mudança de Modelo

Não bastava reduzir o número de notebooks. Precisávamos trocar o paradigma de desenvolvimento da ingestão.

O objetivo era sair de um modelo em que cada time descrevia como executar a ingestão para outro em que o time declarasse o que precisava ser ingerido, e a plataforma cuidasse do resto.

Na prática, isso significava centralizar no núcleo da stack o que antes ficava espalhado: validação de contrato, resolução de ambiente, publicação em Bronze e Silver, tratamento de deletes e regras de schema.

Os critérios eram diretos:

  1. Padronizar a maior parte dos workflows sem abrir espaço demais para exceções estruturais.
  2. Reduzir a superfície de manutenção da plataforma.
  3. Acelerar a entrada de novas fontes no Data Lake.
  4. Fortalecer governança sem transformar o time de plataforma em gargalo manual.

Quando formulamos o problema desse jeito, a decisão ficou clara. O gargalo não estava na falta de notebooks. Estava no excesso de liberdade estrutural.


O Contrato Declarativo

A filosofia da nova stack pode ser resumida em uma frase: tornar a coisa certa a coisa fácil.

Uma nova ingestão deixou de começar com um notebook Python. Ela passou a começar com um contrato YAML. Esse contrato descreve metadados, origem, destino, schema e regras de publicação. O YAML virou a interface humana da plataforma. O runtime continuou como código reutilizável.

Em linhas gerais, uma ingestão segue este padrão/template:

metadata:
  table_description: "Descrição funcional da tabela"
  table_source_owner: "time-dono-da-fonte"
  table_datalake_owner: "time-dono-do-datalake"
  ingestion_type: batch
  ingestion_mode: full

workflow:
  name: fonte-bronze-silver-nome-da-tabela
  schedule_america_sp: "25 03 * * *"

ingestion:
  bronze:
    source:
      prd:
        format: cloud-spanner
        dynamic_configs:
          project_id: "projeto-prd"
          instance_id: "instancia-origem"
          database_id: "database-origem"
          table: "nome_da_tabela_origem"
    destination:
      format: parquet
      unity:
        schema_unity: "dominio_bronze"
        table_unity: "nome_da_tabela_bronze"

  silver:
    destination:
      format: delta
      unity:
        schema_unity: "dominio_silver"
        table_unity: "TB_NOME_DA_TABELA_SILVER"
    schema_config:
      partition_by: ["CuratedDt"]
      columns:
        - source_name: source_id
          silver_name: Id
          datatype: STRING
          primary_key: true
        - source_name: data_operacao
          silver_name: DataOperacao
          datatype: DATE
          primary_key: false
        - source_name: valor_financeiro
          silver_name: ValorFinanceiro
          datatype: FLOAT
          primary_key: false
        - source_name: data_pagamento
          silver_name: DataPagamento
          datatype: DATE
          primary_key: false

O ponto importante é este: o YAML não descreve só o nome da tabela. Ele descreve o contrato de ingestão de uma tabela.

No modelo novo, essa é a unidade principal de autoria: 1 tabela : 1 YAML. O engenheiro descreve a ingestão. A plataforma decide como executá-la.


Como a Stack Executa o Contrato

O YAML não vai direto para produção. Antes disso, a stack valida o contrato e o transforma em parâmetros válidos de execução.

Na prática, o fluxo segue esta ordem:

  1. Um engenheiro cria ou atualiza uma spec YAML.
  2. A spec passa por validação estrutural e semântica.
  3. A plataforma transforma a spec em parâmetros de execução carregando o YAML como um dicionário em runtime.
  4. Dois notebooks centrais executam o contrato em Bronze e Silver com parâmetros do item 3.
  5. A ingestão acontece com caminhos, formatos e regras padronizadas dependendo dos parâmetros extraídos do YAML.

Esse desenho reduz um erro clássico de plataforma: o pipeline funciona, mas cada time o implementa de um jeito.

No núcleo do runtime, a divisão é simples:

  1. O notebook de Bronze lê a origem e escreve os dados no caminho padronizado no bucket do Google Cloud Storage na bronze.
  2. O notebook de Silver lê a Bronze (o bucket do Google Cloud Storage na bronze), aplica schema, casting, deduplicação e publica a tabela final no bucket do Google Cloud Storage na silver.

Essa centralização muda a economia da manutenção. Quando uma regra estrutural evolui, ela evolui em um núcleo comum, não em centenas de notebooks quase iguais.


Governança e Operação no Centro da Stack

Uma parte importante dessa história não está no YAML. Está no que impede o YAML de virar bagunça.

Antes de qualquer execução, a spec passa por uma camada de validação com Pydantic. Essa camada verifica formato aceito de source, presença de campos obrigatórios, coerência entre campos, consistência por ambiente e regras de schema.

Na prática, a governança aparece em mecanismos concretos:

  1. Campos obrigatórios e enums bloqueiam configurações inválidas logo na entrada.
  2. Allowlists garantem que projetos, formatos e certos comportamentos sigam convenções conhecidas.
  3. Guardrails impedem usos perigosos, como casos de método de escrita overwrite fora do fluxo aprovado.
  4. Regras cruzadas validam coerência entre modo de ingestão e filtro configurado.
  5. Ownership e metadados deixam explícito quem é dono da origem e quem é dono da tabela no Data Lake.

Esse é o ponto em que a stack troca liberdade por operabilidade. Convenção deixa de ser recomendação. Ela vira critério de entrada.

Essa camada também faz a stack ir além de “copiar dado”. O runtime já incorpora validação, data quality e controles operacionais que antes ficavam espalhados por implementações locais.


GhostBuster: Deletes Viraram Fluxo de Plataforma

O GhostBuster é o mecanismo da stack que garante que exclusões feitas na origem transacional sejam refletidas corretamente na camada silver do Data Lake.

No contrato declarativo, esse comportamento pode ser habilitado na própria spec YAML. A partir daí, delete deixa de ser exceção tratada caso a caso em cada tabela e passa a fazer parte da operação padrão da plataforma.

No dia a dia, isso muda a ingestão em quatro pontos:

  1. A tabela já nasce com uma regra explícita de tratamento de exclusões.
  2. Em reprocessamentos, a stack evita que registros já removidos na origem voltem a aparecer na silver.
  3. Quando a validação encontra IDs pendentes de remoção, o caso entra em um fluxo controlado de deleção.
  4. Esse fluxo fica registrado em uma trilha operacional até a execução do hard delete.

O efeito prático foi reduzir um tipo recorrente de atrito operacional. Antes, deletes na silver costumavam abrir demandas manuais e estender a janela de inconsistência entre origem e Data Lake. Agora, boa parte desse trabalho é absorvida pela própria stack.


Adoção em Escala de IA Generativa

A stack virou o padrão operacional da ingestão quando o contrato declarativo passou a ser a unidade principal de autoria da plataforma.

Hoje, operamos com cerca de 850 YAMLs em produção. Esse número importa menos pelo volume em si e mais pelo que ele prova: a stack deixou de ser um padrão novo e virou o padrão operacional da ingestão.

Usamos agentes de IA para acelerar a parte mais repetitiva da migração, como criação e atualização de specs. Eles reduziram trabalho mecânico, mas não mudaram a lógica central do desenho. O ganho estrutural veio da stack declarativa. O repositório conta com diversas skills, instructions e prompts para os agentes auxiliarem na criação e evolução dos YAMLs levendo em horas o que antes levava dias.

Migração: De 530 Notebooks para 530 YAMLs

Essa mudança não aconteceu em um espaço vazio. Cerca de 530 notebooks legados precisaram ser convertidos para o novo contrato declarativo. Essa migração foi o passo necessário para trocar o modelo antigo por um fluxo em que a plataforma consegue evoluir em um núcleo comum.

Agentes de IA nos ajudaram em todo o processo de migração, desde a identificação de notebooks candidatos até a criação inicial dos YAMLs.

O importante não era só converter o código. Era converter a lógica de cada ingestão para o modelo declarativo, o que exigiu decisões de modelagem e ajustes para casos especiais. O resultado foi uma migração mais rápida e consistente, que deixou a stack pronta para operar em escala com o novo modelo.

Migrar 530 notebooks para 530 YAMLs não foi só uma questão de volume. Foi uma questão de transformar a forma como a ingestão é pensada, escrita e mantida. O contrato declarativo virou o novo centro da operação, e a migração foi o passo necessário para chegar lá.


O que a Stack Cobre Hoje

A stack declarativa hoje governa cerca de 850 YAMLs em produção e cobre aproximadamente 85% dos workflows do fluxo Source → Bronze → Silver.

Dentro desse caminho principal, a stack já padroniza:

  1. O fluxo principal de batch.
  2. Suporte a múltiplos formatos de origem, incluindo Spanner, BigQuery, Delta e arquivos.
  3. Configuração explícita por ambiente, com stg, int e prd tratados como parte do contrato.
  4. Extensões para streaming e outros casos especiais fora do caminho mais comum.

Isso importa porque mostra o limite real do modelo. A stack cobre a maior parte da operação sem fingir que todo caso especial cabe no mesmo caminho. O ganho está em padronizar o que é recorrente e deixar explícito onde a borda começa.

E a Sustentação?

A stack declarativa eliminou a necessidade de uma grande parte da sustentação. Ela mudou o tipo de sustentação que fazemos. Por um lado antes cada notebook podia ser um caso diferente. Por outro lado, agora temos um núcleo comum para evoluir e melhorar. A sustentação hoje é mais focada em evoluir o runtime, melhorar a camada de validação e garantir que o contrato continue sendo a interface humana da plataforma. O ganho é que, quando fazemos uma melhoria estrutural, ela impacta toda a stack, não só um caso específico.

Colocar uma coluna nova vindo de uma migração transacional, por exemplo, não é mais um caso de notebook. É uma evolução do contrato que pode ser aplicada em centenas de YAMLs com o mesmo ajuste. O resultado é que a sustentação evolui de um trabalho de manutenção reativa para um trabalho de evolução proativa da plataforma.

Alie isso a Agente de IA e temos um cenário em que a sustentação é mais rápida, mais consistente e mais focada em evoluir a plataforma do que em manter casos específicos. O contrato declarativo virou o centro da operação, e a sustentação virou o centro da evolução da plataforma.

Qualquer um pode criar uma nova ingestão?

Sim. Essa é a ideia. O modelo declarativo e a camada de validação foram desenhados para que qualquer engenheiro possa criar uma nova ingestão seguindo o contrato. A governança é garantida pela validação, que bloqueia configurações inválidas ou perigosas. O resultado é que a criação de novas ingestões se torna mais self-service, sem depender de um time central de plataforma para cada nova fonte. O contrato declarativo é a interface humana da plataforma, e ele foi desenhado para ser acessível e fácil de usar, mesmo para quem não tem experiência prévia com a stack. O objetivo é democratizar a criação de ingestões, mantendo a governança e a operabilidade da plataforma.

Times internos já começaram a fazer PRs de criação de novas ingestões seguindo o modelo declarativo, e a resposta tem sido positiva. O processo é mais rápido, mais previsível e menos propenso a erros do que o modelo anterior. O contrato declarativo virou o novo padrão para criar ingestões, e a plataforma está pronta para escalar com esse modelo. O resultado é que, com o contrato declarativo, a plataforma pode crescer de forma mais rápida e consistente, sem repetir os custos estruturais do passado.

Um exemplo muito comum é a criação de ingestões de tabelas públicas que times as encontram e desejam colocar no Data Lake. Com o modelo declarativo, eles podem criar um YAML seguindo o contrato, e a plataforma cuida do resto. O resultado é que a entrada de novas fontes se torna mais rápida e menos dependente de intervenção manual, o que acelera o crescimento do Data Lake sem comprometer a governança ou a operabilidade.


Os Resultados

A tabela abaixo resume o que mudou no modelo de desenvolvimento e operação:

AspectoAntesDepois
Paradigma de desenvolvimentoImperativo, focado no “como”Declarativo, focado no “o que”
Superfície principal de autoriaNotebooks Python, no modelo 2 notebooks : 1 tabela bronze e 1 tabela silverYAMLs declarativos, no modelo 1 YAML : 1 tabela bronze e 1 tabela silver
Tempo estimado para nova ingestãoDias por nova fonteHoras por nova fonte
Escala atual da stackLógica espalhada por implementações de notebooks isolados~850 YAMLs centralizados
Núcleo de execuçãoImplementações distribuídas2 notebooks centrais
GovernançaVariava por implementaçãoValidada por contrato
Tratamento de deletesSoluções locais e intervenção manualGhostBuster com fluxo padronizado e rastreável
OrganizaçãoMúltiplos padrões locaisModelo unificado de ingestão

Quando a autoria da ingestão sai de centenas de implementações livres e vai para contratos validados, a plataforma reduz drasticamente os pontos onde ela pode divergir de si mesma.

Esse ganho aparece em quatro planos ao mesmo tempo:

  1. Menos código repetido para escrever e revisar.
  2. Menos variação estrutural entre workflows.
  3. Mais previsibilidade na operação.
  4. Mais velocidade para colocar fontes novas no ar.

O que Aprendemos

Essa não foi uma troca sem atrito. A simplificação valeu a pena, mas trouxe aprendizados importantes.

1. Adotar um modelo declarativo exigiu mudança de autoria.

Padronizar a tecnologia foi a parte mais direta. Mais difícil foi alinhar a mudança de autoria. Times acostumados a construir a ingestão inteira precisaram passar para um fluxo em que a principal decisão deixa de ser o notebook e passa a ser o contrato.

2. Nem todo workflow entra no modelo novo no mesmo ritmo.

A cobertura de 85% já representa um avanço grande. Ela também mostrou que o contrato precisa ter um limite claro. Quando a exceção vira regra, a stack perde poder de padronização.

3. Simplificar a implementação não elimina a necessidade de boa modelagem.

O modelo declarativo reduz o custo da implementação. Ele não elimina a necessidade de decisões corretas sobre schema, origem, deduplicação, deletes e publicação. Quando o contrato nasce mal modelado, a stack só escala o erro mais rápido.


O que Vem a Seguir

Com 850 YAMLs em produção, a próxima fase é expandir as capacidades da plataforma para novos casos de uso e integrações.

  1. Expandir a cobertura para além dos 85% atuais.
  2. Evoluir a autoria assistida por IA para reduzir o trabalho manual na criação e evolução de specs.
  3. Ampliar conectores, formatos e casos especiais dentro do mesmo modelo declarativo.
  4. Tornar a criação de novas ingestões cada vez mais self-service para os times.
  5. Coletar e extrair mais tabelas transacionais para o Data Lake, acelerando a entrada de novas fontes.

O ponto importante é que a fundação mudou. Agora temos uma base mais simples para crescer sem repetir os custos estruturais do passado.


Tecnologias

CamadaTecnologia
Especificação de ingestãoYAML
ProcessamentoDatabricks + Apache Spark
Camada BronzeNotebook genérico centralizado
Camada SilverNotebook genérico centralizado
Validação e governançaPython + models declarativos + allowlists
Deletes e controle operacionalGhostBuster + Validator + Data Quality
Aceleração de criaçãoAgentes de IA + Asset Inventory + validação automatizada
Organização da stackRepositório unificado de ingestão

A CERC opera a infraestrutura do mercado financeiro brasileiro para registro de recebíveis. Construir plataformas de dados nesse contexto significa trabalhar com escala real, impacto real e decisões de engenharia que precisam ser operáveis no dia seguinte. Se você quer trabalhar em problemas como este, estamos contratando.


Este post foi escrito pelo time de Engenharia de Dados da CERC: Davi Campos, André Tayer e Guilherme Oliveira.