Expectations no Lakeflow: qualidade de dados como código, governada no Unity Catalog
Como declarar regras de qualidade ao lado da transformação, escolher entre registrar, descartar ou falhar, e governar tudo pelo Unity Catalog com regras versionadas e auditáveis.
Na maioria dos pipelines que herdo, a "qualidade de dados" é um script que roda depois da carga: uma bateria de SELECT COUNT(*) WHERE campo IS NULL que alguém abre na segunda-feira e, quando encontra problema, o dado ruim já foi consumido por três dashboards e um modelo. É reação, não prevenção.
As Expectations do Lakeflow (as Databricks Declarative Pipelines, evolução do antigo DLT) invertem essa lógica. A regra de qualidade passa a viver ao lado da transformação, como código versionado, e o Unity Catalog governa, audita e reaproveita essa regra entre pipelines. Neste artigo eu mostro o conceito, o código na prática e por que isso importa para quem opera dados em produção.
O conceito: qualidade como contrato, não como verificação
Uma Expectation é uma restrição declarativa sobre um dataset de um pipeline — uma materialized view, uma streaming table ou uma view temporária. Você descreve, em SQL booleano, o que significa um registro válido e diz ao pipeline o que fazer quando a condição não é satisfeita.
A diferença de mentalidade é a palavra contrato. Em vez de "vou checar depois se veio nulo", você declara "esta tabela não aceita CPF nulo" e o mecanismo cuida do resto — inclusive de registrar quantos registros violaram a regra, para você medir a saúde da fonte ao longo do tempo.
O código na prática
O ponto de entrada em 2026 é o módulo pipelines do PySpark. Você importa, decora a função que produz a tabela e anexa a expectation:
from pyspark import pipelines as dp
@dp.table()
@dp.expect_or_drop("cpf_valido", "cpf IS NOT NULL")
def clientes():
return spark.readStream.table("bronze.clientes_raw")
Três elementos aqui:
@dp.table()declara que a função materializa uma tabela do pipeline.@dp.expect_or_drop(...)anexa a regra. O primeiro argumento é o nome da expectation (aparece nas métricas), o segundo é a condição SQL que um registro válido precisa satisfazer.- A função retorna a query — o Spark cuida do plano de execução.
Os 6 decorators e quando usar cada um
O módulo oferece seis decorators, organizados por o que acontece com a violação e por quantidade de regras.
Regra única:
@dp.expect— registra e mantém a linha (monitorar).@dp.expect_or_drop— descarta a linha ruim.@dp.expect_or_fail— interrompe o pipeline.
Múltiplas regras (dicionário):
@dp.expect_all— registra e mantém.@dp.expect_all_or_drop— descarta.@dp.expect_all_or_fail— interrompe.
A escolha é uma decisão de negócio, não de código:
expect— use quando quer observabilidade sem bloquear. Ideal para começar: você mede a taxa de violação de uma fonte nova antes de decidir barrar.expect_or_drop— use quando a linha ruim não pode chegar ao consumo, mas o pipeline pode seguir com o restante. É o caso mais comum em camadas silver/gold.expect_or_fail— use para invariantes críticas de negócio (ex.: chave primária duplicada, valor monetário negativo onde é impossível). Falhar cedo é mais barato que propagar.
Múltiplas regras de uma vez
Para validar várias condições, use as variantes _all, passando um dicionário nome -> condição:
regras = {
"cpf_valido": "cpf IS NOT NULL",
"idade_plausivel":"idade BETWEEN 0 AND 120",
"uf_preenchida": "uf IS NOT NULL",
}
@dp.table()
@dp.expect_all_or_drop(regras)
def clientes():
return spark.readStream.table("bronze.clientes_raw")
Com expect_all_or_drop, um registro é descartado se falhar em qualquer das regras. Cada regra continua sendo medida individualmente nas métricas — você sabe qual condição está derrubando os dados.
O salto de 2026: regras governadas pelo Unity Catalog
Historicamente, as expectations viviam presas ao código do pipeline. A novidade que muda o jogo é poder armazenar e gerenciar as regras de qualidade dentro de tabelas do Unity Catalog. Na prática, isso traz três benefícios diretos:
- Versionamento e auditoria — a regra deixa de ser uma linha perdida em um notebook e passa a ser um objeto governado, com histórico. Auditoria e compliance conseguem responder "qual regra estava valendo em março?".
- Reutilização entre pipelines — a mesma definição de "cliente válido" pode ser referenciada por vários pipelines, em vez de copiada e colada (e divergindo com o tempo).
- Governança centralizada — junto com a propagação automática de permissões
MANAGEpara materialized views e streaming tables no Unity Catalog, a qualidade entra no mesmo perímetro de governança do resto dos dados.
Some a isso outras evoluções recentes do Lakeflow que tornam o padrão mais robusto em produção: testes unitários em Python direto no editor de pipelines (validando a lógica contra dados mockados por redirecionamento de tabela), type widening para evoluir tipos de coluna sem reset de pipeline, e modo de execução em fila que enfileira updates concorrentes em vez de falhar por conflito.
Por que isso importa
Quando a qualidade vira código declarativo e governado, três coisas mudam para o time:
- Dados confiáveis sem vigiar a carga. A regra roda toda execução; a violação é registrada automaticamente. Ninguém precisa abrir o painel na segunda de manhã.
- Decisão explícita de risco.
expect/drop/failobrigam o time a decidir, para cada regra, o custo de deixar passar versus o custo de barrar. Isso documenta o apetite de risco no próprio pipeline. - Observabilidade nativa. As métricas de qualidade fazem parte do pipeline — dá para acompanhar a taxa de violação por fonte ao longo do tempo e agir antes que vire incidente.
Como começar amanhã
- Escolha uma tabela silver crítica e liste 3 a 5 invariantes que você sabe que deveriam valer.
- Comece com
@dp.expect(só monitorar) por alguns dias para medir a taxa real de violação — evita descartar dado bom por uma regra mal calibrada. - Promova as regras estáveis para
expect_or_drop; reserveexpect_or_failpara as poucas invariantes que justificam parar a carga. - Quando o padrão amadurecer, mova as definições para o Unity Catalog e compartilhe entre pipelines.
Qualidade deixa de ser uma reação semanal e vira um contrato que o pipeline honra a cada execução. Na prática, é o tipo de mudança que costuma pagar dividendos mais rápido do que quase qualquer otimização de performance — porque evita o retrabalho silencioso de reprocessar dado ruim já consumido.
Artigos relacionados
ai_parse_document(): transforme PDF em tabela governada com uma única instrução SQL
Como o ai_parse_document() do Databricks colapsa OCR, parsing e reconstrução de tabelas em uma única instrução SQL — entregando o resultado como tabela governada no Unity Catalog.
Ler artigoCarga incremental no Azure Data Factory: o padrão de watermark passo a passo
Como fazer carga incremental no Azure Data Factory usando o padrão de watermark: Lookup do último valor, Copy Data só da janela nova e Stored Procedure que atualiza o controle. Guia prático.
Ler artigoGostou? Veja os e-books para conteúdo aprofundado.
E-books