Repositório de instrução para o desafio técnico da vaga de Pessoa Engenheira de Dados Júnior da Base dos Dados.
O objetivo do desafio é emular, em escala reduzida, o fluxo real de ingestão e publicação de uma tabela no projeto basedosdados: extrair os dados na fonte, tratar conforme o Manual de estilo da BD, materializar com dbt e orquestrar com Prefect, mantendo a tabela atualizável.
Você deverá construir uma pipeline de ingestão e atualização para o conjunto de dados Unidades de Conservação, publicado pelo Ministério do Meio Ambiente (MMA) no portal dados.gov.br.
A pipeline deve, ao final, materializar uma tabela pública seguindo as convenções da BD — nome do dataset, nome da tabela, snake_case sem acentos, tipos do BigQuery (ou do warehouse equivalente), e schema documentado. Sugestão de nomenclatura, conforme o manual:
- Dataset:
br_mma_unidades_conservacao - Tabela:
unidade_conservacao
O objetivo é que a tabela final reflita o status mais recente das unidades de conservação publicado na fonte. A estratégia de atualização (full refresh com overwrite, append incremental, snapshot, merge por chave, etc.) fica a seu critério — escolha a abordagem que considerar mais adequada para o volume e a cadência de mudanças desse conjunto, e justifique a decisão no README da sua solução. Espera-se apenas que rodar a pipeline mais de uma vez não corrompa a tabela.
A arquitetura deve emular o fluxo de dados da BD (fluxo de dados e infraestrutura):
Fonte (dados.gov.br)
└─ download + tratamento (Python, dentro do flow Prefect)
└─ arquivo parquet/csv local
└─ upload para storage (GCS ou S3/MinIO)
└─ tabela externa em <dataset>_staging
└─ dbt run/test (target=dev) → <dataset>.<tabela> (materializado)
└─ promoção para "prod" (cópia do storage + dbt run target=prod)
Você pode implementar a separação dev/prod com dois projetos BigQuery (recomendado, mais próximo do real) ou com dois schemas/databases dentro do mesmo warehouse local — desde que o conceito de gate de qualidade entre as duas zonas fique claro: nada vai para prod antes de passar nos testes dbt.
- Ingestão: baixar os arquivos de Unidades de Conservação direto da fonte (sem versionar os dados brutos no repositório).
- Tratamento: limpar tipos, normalizar nomes de colunas para snake_case, remover acentos, padronizar UFs/municípios usando os códigos do IBGE (consulte o Manual de estilo para os diretórios canônicos). Ver detalhes em Etapas de tratamento abaixo.
- Upload: gravar o arquivo tratado em um bucket (
<bucket>/staging/br_mma_unidades_conservacao/unidade_conservacao/). - Modelagem dbt: criar
models/br_mma_unidades_conservacao/br_mma_unidades_conservacao__unidade_conservacao.sqle seuschema.ymlcomdescription, tipos e os testes obrigatórios descritos abaixo. - Orquestração: o flow Prefect deve encadear extração → upload →
dbt run→dbt test→ promoção, abortando se qualquer teste dbt falhar. - Atualização: a pipeline deve ser capaz de refletir mudanças da fonte em execuções subsequentes (ver discussão da estratégia mais acima).
- Agendamento: configurar um schedule Prefect (mesmo que mensal) e demonstrar que ele dispara o flow.
- Código organizado, com
pyproject.toml/uv/poetry, lint e formatação configurados. - Variáveis sensíveis (credenciais GCP, chaves S3) fora do código — use
.envou Prefect Blocks/Secrets. - README da sua solução com: como rodar localmente, como rodar o flow no Prefect, como apontar para o bucket, e decisões de arquitetura.
Além da limpeza geral (snake_case, sem acentos, tipos corretos), a tabela final precisa atender às regras abaixo. A ideia é que ela fique cruzável com qualquer outra tabela publicada em basedosdados que use as mesmas chaves.
-
Coluna
id_municipio— a tabela final deve conter uma colunaid_municipiocom o código IBGE de 7 dígitos do município da unidade de conservação. A fonte traz, em geral, o nome do município e a UF; cabe à pipeline fazer a conversão para o código IBGE usando o diretório oficial da BD como referência:br_bd_diretorios_brasil.municipio— diretório canônico de municípios brasileiros mantido pela BD. Use as colunasid_municipio(7 dígitos),nomeesigla_ufpara a junção.Casos a tratar explicitamente (e documentar no README da sua solução):
- Unidades que cobrem mais de um município: defina a granularidade da tabela (uma linha por UC × município, ou uma linha por UC com lista, etc.) e justifique.
- Unidades marinhas / sem município definido: como representar
id_municipioquando não se aplica. - Nomes que não casam com o diretório (acentuação, grafia antiga, abreviações): a estratégia de normalização precisa ser reprodutível, não um conjunto de fixes manuais.
O schema.yml da tabela unidade_conservacao deve declarar, no mínimo:
Na coluna de identificador único da unidade de conservação:
- Unicidade —
unique: garantir que a coluna de ID não possui valores repetidos. - Não-nulo —
not_null: garantir que a coluna de ID não possui valores nulos.
Na coluna id_municipio:
- Não-nulo —
not_null: toda linha precisa ter um município associado. Para UCs marinhas ou casos sem município definido, a estratégia adotada (sentinela, linha extra, exclusão) precisa estar documentada e o teste precisa passar — não é aceitável deixar nulo sem justificativa. - Relacionamento —
relationships: cadaid_municipioprecisa existir no diretório oficial da BD (br_bd_diretorios_brasil.municipio). Esse teste valida que a junção foi feita corretamente e que não há códigos IBGE inventados ou desatualizados.
Esses testes precisam rodar dentro do flow Prefect (etapa dbt test) e abortar a promoção para prod caso falhem — é o gate de qualidade descrito no fluxo da BD.
Testes adicionais (accepted_values para categorias de UC / esfera administrativa, ranges para áreas e datas, etc.) contam como diferencial.
| Componente | Tecnologia |
|---|---|
| Orquestração | Prefect 3 (docs) |
| Transformação | dbt (Core, adapter à sua escolha) |
| Storage | GCS ou compatível com S3 (AWS S3, MinIO, Backblaze B2, etc.) |
| Warehouse | BigQuery ou equivalente (DuckDB local, Postgres) — desde que o dbt rode contra ele |
| Linguagem | Python 3.11+ para o flow |
| Versionamento | Git + GitHub |
Você tem duas opções, ambas aceitas:
- GCP trial (cadastro) — recomendado por ser idêntico ao que usamos na BD. Crie um projeto, ative BigQuery e GCS, e use service account com permissões mínimas.
- Stack local com S3-compatível —
docker-composecom MinIO + DuckDB (ou Postgres) como warehouse. Mais simples de subir, sem depender de cartão de crédito. O dbt precisa estar conectado ao warehouse local via o adapter correspondente (dbt-duckdboudbt-postgres), comprofiles.ymlapontando para o serviço subido pelodocker-composee lendo os arquivos do bucket MinIO (no caso do DuckDB, via extensãohttpfscom as credenciais do MinIO). A separação dev/prod nesse cenário é feita com dois schemas/databases distintos, e o gate dbt continua valendo.
Se optar pela GCP, não exponha a service account no repositório; envie a chave por e-mail no momento da entrega — ver checklist abaixo.
Caso você opte pelo caminho GCP, a revisão é feita contra o seu próprio projeto, usando a service account que você nos enviar. Para que isso funcione sem fricção, sua entrega precisa cumprir todos os itens abaixo:
No projeto que você criou, habilite explicitamente (uma vez, via console ou gcloud services enable):
bigquery.googleapis.comstorage.googleapis.comiam.googleapis.com
Crie uma service account dedicada ao desafio (ex.: desafio-bd-runner@<projeto>.iam.gserviceaccount.com) com os papéis:
roles/bigquery.dataEditor— criar/popular datasets e tabelas no projeto.roles/bigquery.jobUser— executardbt run/dbt test.roles/storage.objectAdminno bucket criado para o desafio (não no projeto inteiro).
Não atribua roles/owner nem roles/editor — uma SA com permissão excessiva é critério de desconto.
Crie o dataset BigQuery e o bucket GCS na mesma região (recomendado: US ou southamerica-east1). Tabela externa só funciona se dataset e bucket compartilham região.
Tudo que é específico do seu projeto deve vir de variável de ambiente — nada hardcoded no código nem no profiles.yml. No mínimo:
GCP_PROJECT_IDGCS_BUCKET_NAMEBQ_DATASET_DEV/BQ_DATASET_PROD(ou um único dataset com convenção_staging)GOOGLE_APPLICATION_CREDENTIALSapontando para o JSON da SA
Inclua um .env.example no repositório com todas as variáveis listadas (sem valores reais). O profiles.yml do dbt deve ler via {{ env_var('...') }}.
Para emular o fluxo da BD, use dois datasets BigQuery dentro do mesmo projeto (<dataset>_dev_staging + <dataset>_dev e <dataset>_staging + <dataset>) — não exige dois projetos. Se preferir dois projetos, documente.
No e-mail de envio do desafio, anexe (ou compartilhe via link com expiração):
- O JSON da service account.
- O
project_ide obucket_nameque você usou (a menos que sejam óbvios pelo.env.example). - Confirmação de que a SA estará ativa pelos próximos 14 dias corridos a partir do envio. Se for desativada antes, avise.
Após receber retorno da avaliação, você é responsável por deletar a service account, o bucket e o projeto (ou pelo menos revogar a chave da SA). Não deixe credenciais ativas indefinidamente.
- Aderência ao manual de estilo: nomes, tipos, snake_case, diretórios — leia o Manual de estilo antes de codar. A maior parte dos ajustes que pedimos em PRs reais é, na prática, manual de estilo.
- Fidelidade ao fluxo da BD: dev → gate de qualidade → prod, com tabela externa em
_staginge materializada via dbt. - Idempotência e atualização: a pipeline aguenta rodar de novo? Detecta mudanças na fonte?
- Qualidade do código: legibilidade, modularização, ausência de duplicação, tratamento de erros razoável.
- Testes dbt: pelo menos os básicos (
not_null,unique,accepted_valuesonde fizer sentido). - Documentação: README claro, descrições de colunas no
schema.yml, decisões justificadas. - Simplicidade: solução direta, sem complexidade gratuita.
- Testes unitários do código Python de tratamento.
- CI no GitHub Actions rodando lint + testes +
dbt parseem PRs. update_django_metadata-like: ao final do flow, atualizar um JSON/YAML com cobertura temporal (max_dateda tabela) — emulando o que fazemos no backend GraphQL.- Observabilidade: logs estruturados, métricas mínimas do flow.
- Ler o Manual de estilo e o fluxo de dados da BD por inteiro.
- Definir nomes (dataset/tabela/colunas) antes de escrever código.
- Subir o ambiente (GCP trial ou
docker-composecom MinIO + warehouse local). - Escrever o script de extração + tratamento.
- Subir o arquivo tratado no bucket e criar a tabela externa em
<dataset>_staging. - Escrever o modelo dbt em
models/br_mma_unidades_conservacao/+schema.ymlcom testes. - Encadear tudo num flow Prefect 3 com schedule.
- Implementar a estratégia de atualização e demonstrar que funciona (rodar duas vezes, simular fonte atualizada).
- Escrever o README da sua solução com instruções de reprodução.
- A solução deve ser publicada em um repositório público no seu GitHub (pode fazer fork deste, ou criar um novo).
- Envie o link do repositório para gabriel.pisa@basedosdados.org com o assunto "Desafio Engenheiro de Dados Júnior — BD".
- Caso seja aprovado(a) para a próxima etapa, você apresentará a solução para a equipe, explicando decisões de arquitetura, trade-offs e o que faria diferente com mais tempo.
- Se não conseguirmos reproduzir a solução localmente seguindo o seu README, ela não será avaliada — capriche nas instruções.
- Manual de estilo da BD — leitura obrigatória.
- Fluxo de dados e infraestrutura da BD — o que a sua pipeline deve emular.
- Conjunto de dados — Unidades de Conservação (dados.gov.br)
- Repositório
basedosdados/pipelines— exemplos de pipelines reais (ex.:br_bcb_agencia,br_bcb_sicor). - Prefect 3
- dbt
- MinIO · DuckDB · GCP Free Tier
Fale conosco em gabriel.pisa@basedosdados.org.