---
title: "Dependencias em Zig: Guia Pratico com build.zig.zon"
url: "https://ziglang.com.br/artigos/zig-dependencias-build-zig-zon-guia-pratico/"
markdown_url: "https://ziglang.com.br/artigos/zig-dependencias-build-zig-zon-guia-pratico.MD"
description: "Como decidir, adicionar e isolar dependencias em projetos Zig usando build.zig.zon, modulos, codigo C legado e uma politica simples para times brasileiros."
date: "2026-06-02"
author: ""
---

# Dependencias em Zig: Guia Pratico com build.zig.zon

Como decidir, adicionar e isolar dependencias em projetos Zig usando build.zig.zon, modulos, codigo C legado e uma politica simples para times brasileiros.


Zig evita a cultura de instalar cinquenta pacotes antes de escrever a primeira linha de codigo. Isso e uma vantagem, mas tambem cria uma duvida pratica: quando um projeto cresce, como trazer uma biblioteca externa sem transformar o `build.zig` em uma caixa-preta? A resposta nao e "nunca use dependencias". A resposta e tratar dependencia como parte do design do binario.

Essa pergunta aparece com frequencia em comunidades Zig: como usar projetos que nao nasceram com `build.zig`, onde colocar `resolved_target` e `optimize`, quando criar um modulo, quando compilar C junto e quando simplesmente copiar um arquivo pequeno para dentro do repositorio. Para um time brasileiro que esta avaliando Zig em CLI interna, gateway HTTP, ferramenta de dados ou componente embarcado, essa decisao pesa mais que a sintaxe.

Este guia mostra um caminho pragmatico para **dependencias em Zig** com `build.zig.zon`, `b.dependency`, modulos explicitos e uma politica simples de revisao. Se voce ainda nao domina o build system, leia primeiro [Zig Build System: guia completo](/tutoriais/zig-build-system/). Para contexto de projetos reais, veja tambem [Zig para programadores C](/artigos/zig-para-programadores-c-guia-migracao/), [OpenAPI em Zig](/artigos/zig-openapi-contratos-json-clientes/) e [Zig com systemd](/artigos/zig-systemd-servico-linux-producao/).

## O criterio antes do comando

Antes de adicionar qualquer pacote, responda quatro perguntas:

| Pergunta | Por que importa |
|---|---|
| A dependencia reduz risco real ou so economiza algumas linhas? | Zig valoriza superficie pequena e previsivel. |
| Ela precisa alocar memoria? | O chamador deve enxergar allocator e tempo de vida. |
| Ela funciona no alvo de deploy? | Linux, Windows, macOS, musl, WASM e embarcado mudam a resposta. |
| Ela tem API pequena o bastante para isolar? | Um wrapper local evita espalhar detalhes externos pelo projeto. |

Uma biblioteca de TLS, parser complexo, driver de banco ou binding C pode valer muito. Uma funcao de `split`, `slugify` ou cor de terminal talvez nao valha o custo de auditoria, atualizacao e compatibilidade. Em Zig, a dependencia ideal e aquela que voce consegue explicar no README e remover sem reescrever o sistema inteiro.

## A forma moderna: `build.zig.zon`

O arquivo `build.zig.zon` descreve o pacote e suas dependencias. Ele fica ao lado do `build.zig` e deve ser versionado.

```zig
.{
    .name = .minha_cli,
    .version = "0.1.0",
    .minimum_zig_version = "0.14.0",
    .dependencies = .{
        .zig_args = .{
            .url = "https://example.com/zig-args/archive/v0.2.0.tar.gz",
            .hash = "1220...",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}
```

O ponto importante nao e decorar esse formato. E entender que a dependencia passa a ser declarativa, revisavel e presa a um hash. Quando alguem roda `zig build`, o build system sabe o que baixar, validar e colocar no cache.

Na pratica, voce costuma usar `zig fetch` para obter a URL e o hash inicial, depois revisa o diff manualmente:

```bash
zig fetch --save https://example.com/zig-args/archive/v0.2.0.tar.gz
git diff build.zig.zon
```

Nao aceite um diff de dependencia sem ler o nome, a origem, a versao e o escopo. Esse e o equivalente Zig de revisar `go.mod`, `Cargo.toml` ou `package-lock.json`, so que normalmente bem menor.

## Ligando a dependencia no `build.zig`

Depois de declarar a dependencia, voce decide como ela entra no build. O caso comum e expor um modulo para o executavel.

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "minha-cli",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const args_dep = b.dependency("zig_args", .{
        .target = target,
        .optimize = optimize,
    });

    exe.root_module.addImport("args", args_dep.module("args"));

    b.installArtifact(exe);
}
```

A regra pratica: passe `target` e `optimize` para dependencias que compilam codigo. Isso evita uma classe comum de erro em que o projeto principal esta em `ReleaseSafe` para Linux musl, mas a dependencia foi resolvida com outra configuracao. Quando uma biblioteca documenta campos como `resolved_target`, siga a API daquela versao, mas mantenha a intencao: todos os componentes devem compilar para o mesmo alvo e perfil.

No codigo da aplicacao, o import fica explicito:

```zig
const args = @import("args");
```

Evite importar a dependencia diretamente em dezenas de arquivos. Prefira um modulo local pequeno, por exemplo `src/cli_args.zig`, que traduz a API externa para tipos do seu projeto. Se voce trocar a biblioteca depois, a mudanca fica concentrada.

## Quando o projeto externo nao tem `build.zig`

Nem tudo no ecossistema vem empacotado para Zig. Voce pode querer usar uma biblioteca C pequena, um arquivo `.c` legado, uma tabela gerada ou um projeto que so publica fonte.

Nesses casos, ha tres opcoes saudaveis:

| Situacao | Opcao recomendada |
|---|---|
| Um ou dois arquivos pequenos e estaveis | Vendorize em `third_party/` com licenca preservada. |
| Biblioteca C com build simples | Compile os `.c` pelo `build.zig` e isole o `@cImport`. |
| Projeto grande com build proprio | Considere manter fora do binario Zig ou criar um wrapper separado. |

Exemplo com um arquivo C local:

```zig
const exe = b.addExecutable(.{
    .name = "minha-cli",
    .root_source_file = b.path("src/main.zig"),
    .target = target,
    .optimize = optimize,
});

exe.addCSourceFile(.{
    .file = b.path("third_party/miniparser/miniparser.c"),
    .flags = &.{ "-std=c99" },
});
exe.addIncludePath(b.path("third_party/miniparser"));
exe.linkLibC();
```

O wrapper Zig deve esconder o C do resto do projeto:

```zig
const c = @cImport({
    @cInclude("miniparser.h");
});

pub fn parse(input: []const u8) !Resultado {
    // converter slice Zig para o formato esperado pela biblioteca C
    // e devolver um tipo do seu dominio, nao um ponteiro C cru
}
```

Isso e mais importante que a mecanica do build. O risco de dependencia externa quase sempre entra pela fronteira: ownership de memoria, ponteiros nulos, strings terminadas em zero, erro retornado por inteiro e funcoes que parecem thread-safe mas nao sao.

## Politica simples para times

Uma politica leve evita discussoes repetidas em cada pull request:

1. Dependencia nova precisa de justificativa curta no PR ou commit.
2. `build.zig.zon` deve fixar origem e hash.
3. API externa entra por um wrapper local quando for usada por mais de um arquivo.
4. Nenhuma dependencia deve esconder allocator global.
5. O build precisa rodar com `zig build` limpo em pelo menos Debug e ReleaseSafe.
6. Se o alvo de producao e musl, Windows, WASM ou embarcado, teste esse alvo antes de aceitar.

Para projetos pequenos, isso parece formal demais. Mas a disciplina paga cedo. Quando a CLI vira ferramenta de time, quando o worker entra em systemd ou quando o binario precisa rodar em container minimo, voce ja sabe de onde vem cada pedaco.

## Checklist antes de publicar uma dependencia

Use esta lista antes de fazer merge:

- A dependencia resolve um problema que a `std` nao cobre bem?
- A licenca permite o uso pretendido?
- O pacote e pequeno o suficiente para auditar?
- `target` e `optimize` estao sendo repassados no `build.zig`?
- O import externo esta isolado atras de um modulo local?
- O codigo deixa claro quem aloca e quem libera?
- O build passa sem rede depois do cache inicial?
- Existe um caminho simples para remover ou substituir a dependencia?

Se voce responder "nao" para varias dessas perguntas, talvez a melhor dependencia seja nenhuma. Isso nao e purismo: e manutencao barata.

## Um exemplo de decisao realista

Imagine uma empresa brasileira com uma CLI interna para validar arquivos de remessa. Ela precisa de parsing de argumentos, leitura de CSV e envio opcional para uma API.

Uma divisao sensata seria:

| Area | Decisao |
|---|---|
| Argumentos de CLI | Dependencia pequena, isolada em `src/cli_args.zig`. |
| CSV simples | Implementacao local ou receita propria, se o formato for controlado. |
| HTTP | Usar `std.http` se suficiente; wrapper local para retries e timeout. |
| JSON | Comecar com `std.json`; so trazer biblioteca externa se houver ganho claro. |
| Validacao de dominio | Codigo do proprio projeto, com testes. |

Essa abordagem preserva o melhor de Zig: binario previsivel, poucos acoplamentos e controle explicito. O time ainda usa bibliotecas quando elas reduzem risco, mas nao importa uma arvore enorme para resolver uma tarefa local.

## Conclusao

Gerenciar dependencias em Zig e menos sobre colecionar pacotes e mais sobre desenhar fronteiras. `build.zig.zon` resolve a parte operacional: origem, hash, cache e reproducibilidade. O `build.zig` resolve a parte tecnica: alvo, otimizacao, modulos e artefatos. O seu projeto ainda precisa resolver a parte de engenharia: decidir o que merece entrar no binario.

Para a maioria dos projetos, a melhor regra e simples: comece com `std`, adicione dependencia quando ela reduzir risco real, isole a API externa e mantenha `target`/`optimize` coerentes em todo o build. Assim Zig continua sendo uma ferramenta de controle, nao mais um ecossistema onde ninguem sabe o que esta rodando em producao.
