CodeGym /Cursos /Go SELF /vendor/ em Go: quando ele e necessario e o que ele muda

vendor/ em Go: quando ele e necessario e o que ele muda

Go SELF
Nível 35, Lição 3
Disponível

1. Introducao

Se voce ja chegou a gostar da ideia de que “as dependencias sao descritas em go.mod, e o Go baixa tudo sozinho”, entao o surgimento da pasta vendor/ pode parecer um retorno repentino do passado: “Por que copiar o codigo de outra pessoa para o repositorio? Nos nao somos arquivistas, somos?”. Mas a pratica rapidamente lembra que o mundo real adora criar enredos do tipo “a internet caiu”, “o proxy nao funciona”, “na producao nao pode haver acesso a rede”, “precisamos passar por auditoria”, “a compilacao precisa ser reproduzida daqui a dois anos, quando a biblioteca ja tiver desaparecido”.

Vendoring e como um “kit de emergencia de dependencias” no porta-malas. Normalmente voce dirige sem ele, mas em certas condicoes ele deixa de ser uma opcao estranha e se torna a unica forma de chegar ao destino. E e importante entender nao apenas “como habilitar”, mas tambem o que exatamente muda no comportamento da compilacao e na vida da equipe.

2. O que e vendor/ e o que muda na compilacao

vendor/ e um diretorio dentro do seu repositorio, onde sao colocados os fontes das dependencias externas (normalmente aquelas descritas em go.mod como require, mais as dependencias transitiveis delas). Ou seja, voce guarda uma copia do codigo de terceiros “ao lado do seu”, para que a compilacao possa buscar as dependencias nao na rede e nem no cache de modulos, mas no repositorio.

Historicamente, isso nao e uma tradicao aleatoria: a comunidade Go, mesmo antes dos modulos, ja usava ativamente a abordagem de “copiar as dependencias para o repositorio”, e ate adicionaram suporte no nivel da ferramenta para isso, porque fazer manualmente era muito inconveniente e fragil. Existiam ferramentas externas (godep, glide etc.) que copiavam dependencias para o repositorio, e depois o Go ganhou suporte explicito a diretorios vendor para tornar esse cenario mais simples.

Importante: vendor/ nao e substituto de go.mod/go.sum. Ele e exatamente uma “copia local dos fontes”, e nao um novo sistema de selecao de versoes.

Como vendor/ afeta a resolucao de imports

Quando voce adiciona vendor/, voce nao reescreve os imports no codigo. Seu codigo continua escrevendo:

import "github.com/google/uuid"

E isso e fundamental: o import path e “como o pacote e chamado no mundo dos modulos”, enquanto vendor/ e apenas “de onde pegar os fontes para esse import path” durante a compilacao.

A mudanca acontece na cabeca da ferramenta go: ela pode (dependendo do modo) usar vendor/ como fonte de dependencias na compilacao e nos testes.

Esquema visual (de forma simplificada):

go build / go test
   |
   v
Resolucao de imports (import path -> fontes)
   |
   +--> opcao A: pegar do module cache (modo normal)
   |
   +--> opcao B: pegar de ./vendor/... (modo vendoring)

Ou seja, vendor/ nao afeta a sintaxe do codigo Go, e sim a resolucao de dependencias: de onde o pacote realmente vem.

3. vendor/, module cache e replace: parecem semelhantes, mas o significado e diferente

Para nao se confundir, e util manter uma “tabela de bom senso” — onde o codigo das dependencias fica e quem decide o que usar.

Abordagem Onde fica o codigo da dependencia O que voce fixa Motivo tipico
Modules normais No cache global de modulos (baixado uma vez e reutilizado) go.mod + go.sum “Vida normal”: limpo, compacto, previsivel
replace
Em uma fonte alternativa (muitas vezes uma pasta local no disco) Regra de substituicao em go.mod Desenvolvimento local / depuracao de dependencia
vendor/
Diretamente no repositorio (./vendor/...) Snapshot dos fontes das dependencias Offline / auditoria / compilacao hermetica

Repare que replace muda a “rota de entrega” da dependencia (e pode tornar o projeto dependente da estrutura de pastas em uma maquina especifica), enquanto vendor/ torna o repositorio auto-suficiente em termos de fontes das dependencias. Ao mesmo tempo, go.mod/go.sum continuam sendo a “verdade sobre as versoes”.

4. Como surge o vendor/ e o que ha dentro dele

A pasta vendor/ nao deve aparecer “na mao, do nada”. Em um projeto normal, ela e gerada pelas ferramentas do Go (por exemplo, com um comando do tipo go mod vendor). Eu digo “do tipo” de proposito, porque o mais importante e entender a ideia: vendoring e um snapshot reproduzivel, e nao um lugar para criacao manual.

O conteudo tipico se parece com isto:

todoapp/
  go.mod
  go.sum
  main.go
  internal/...
  vendor/
    github.com/google/uuid/...
    golang.org/x/text/...
    modules.txt

O arquivo modules.txt (muitas vezes presente) e um “registro” que ajuda as ferramentas a entender quais modulos e versoes voce colocou no vendor.

E outro detalhe importante: a existencia de vendor/ nao elimina o fato de que os comandos de gerenciamento de dependencias continuam funcionando via go.mod/go.sum. Mesmo que a compilacao pegue os fontes de vendor/, os comandos go get e go mod tidy continuam sendo sobre gerenciamento de dependencias e continuam modificando go.mod e go.sum, porque sua tarefa e selecionar versoes, sincronizar imports e checksums.

5. Quando vendor/ e necessario e quando vira peso

Existe a tentacao de habilitar vendor/ “por via das duvidas”, como algumas pessoas instalam antivirus em cima de outro antivirus para que o virus fique realmente com medo. Mas vendor/ nao e um recurso gratuito: ele tem um custo, entao e melhor entender os motivos.

O cenario de vida mais comum e a rede limitada ou proibida. Por exemplo, compilacao em CI/CD ou em ambiente fechado, onde nao se pode baixar dependencias durante a compilacao. Nesse caso, vendor/ transforma o projeto em uma “caixa em que ja esta tudo dentro”.

O segundo cenario e auditoria e controle. As vezes, as organizacoes querem guardar um snapshot exato dos fontes das dependencias ao lado do codigo: assim e possivel garantir que o binario foi gerado a partir de fontes especificos, e que esses fontes estao disponiveis sem servicos externos.

O terceiro cenario e reproducao ao longo do tempo. Modulos, proxies, repositorios — tudo isso pode desaparecer ou ficar indisponivel. E um repositorio com vendor/ (se for mantido corretamente) continua auto-suficiente.

O preco do vendor/: tamanho, ruido e disciplina

Se vendor/ fosse pura magia sem desvantagens, todos os projetos teriam vendor/ por padrao, e nem estaríamos discutindo esta palestra (em vez disso, haveria uma palestra sobre como plantar batatas em Marte). Mas as desvantagens do vendoring sao bem terrenas.

  • Primeiro, o repositorio incha. As vezes bastante. Isso afeta tanto o tamanho do clone quanto a velocidade das operacoes e a “barreira de entrada” para iniciantes: a pessoa abre o projeto e ha dez mil arquivos, e ela ainda nao escreveu uma linha de codigo.
  • Segundo, surgem diffs ruidosos e conflitos de merge. Mesmo que voce tenha alterado so uma dependencia, vendor/ pode ser atualizado com um grande conjunto de arquivos. Para a equipe, isso significa que “atualizacao de dependencias” vira um evento separado, que precisa ser lido e revisado.
  • Terceiro, vendor/ exige disciplina: ele envelhece facilmente. Voce pode atualizar go.mod e esquecer de atualizar vendor/, e entao o projeto passa a ter duas fontes de verdade que discordam uma da outra. E, em geral, a disputa entre duas “verdades” e vencida pela que cai mais alto no CI.

6. Mini-exemplo: UUID em um todoapp didatico e vendoring

Imagine que nosso aplicativo de estudo e um todoapp de linha de comando, que vamos evoluindo aos poucos. Suponha que queremos gerar identificadores de tarefas nao manualmente, mas por meio de uma biblioteca UUID. Para o exemplo, vamos usar o pacote popular github.com/google/uuid (voce pode trocar por qualquer outro pacote externo — a logica sera a mesma).

Vamos adicionar um pequeno pacote wrapper para nao espalhar detalhes de UUID pelo codigo inteiro:

package id

import "github.com/google/uuid"

func New() string {
	return uuid.NewString()
}

Agora, no codigo de criacao de tarefas, podemos fazer algo assim:

package todo

import "example.com/todoapp/internal/id"

func NewTask(title string) Task {
	return Task{ID: id.New(), Title: title}
}

Observe: o import continua normal, “baseado em modulos”. Nenhum vendor/... aparece no codigo — e isso e correto.

Depois disso, o go.mod passara a ter um require para o modulo/versao necessaria (isso e feito por go get ou pelas ferramentas da IDE). E, se voce decidir usar vendoring, voce gera a pasta vendor/, e o projeto passa a conter uma copia dos fontes dessa dependencia.

O ponto principal aqui e que vendor/ nao e uma forma alternativa de conectar um pacote no codigo, e sim uma forma alternativa de armazenar os fontes da dependencia ao lado do projeto, para que a compilacao possa se apoiar neles. E mesmo com vendor/, o gerenciamento de dependencias continua sendo feito via go.mod/go.sum: os comandos go get e go mod tidy continuam alterando esses arquivos, porque essa e a funcao deles.

7. Como conviver com vendor/ em uma equipe

Quando o projeto decide usar vendor/, e bom que a equipe siga um ritual previsivel. Caso contrario, rapidamente surge uma “bola de neve” de alteracoes nao sincronizadas: um desenvolvedor atualizou go.mod, outro puxou a branch e recebeu divergencias estranhas, o terceiro comitou por acidente uma alteracao direto em vendor/ (sim, isso tambem acontece, e e um genero de dor a parte).

Normalmente, o processo e assim: voce altera as dependencias “no nivel dos modulos” (isto e, via go.mod), depois coloca go.sum em ordem, e so entao atualiza vendor/ como snapshot do estado atual. Isso e importante justamente como sequencia: primeiro escolhemos as versoes e as fixamos nos arquivos de modulo, e depois fazemos a “copia para o offline”.

E e util manter em mente uma moldura simples: vendor/ e uma camada “sobre fontes ao lado”, e nao um novo gerenciador de dependencias. As versoes sao escolhidas e fixadas em go.mod, a verificabilidade e garantida por go.sum, e vendor/ e uma forma de tornar a compilacao menos dependente da rede e da infraestrutura externa.

8. Erros tipicos ao trabalhar com vendor/

Erro #1: editar o codigo diretamente dentro de vendor/ “porque e rapido”.
No comeco parece que voce e um gênio: “Consertei um bug na dependencia em 30 segundos”. Depois descobre que agora esta mantendo um fork da dependencia manualmente, mas sem fork, sem historico, sem documentacao e sem chance de atualizar direito. Se realmente for necessario aplicar um patch em uma dependencia, isso e uma historia separada e consciente, e nao uma edicao repentina em vendor/.

Erro #2: achar que vendor/ substitui go.mod e go.sum.
Esse tipo de pensamento quebra a visao de mundo: voce passa a ignorar go.mod, e depois se surpreende porque as ferramentas continuam alterando go.sum, porque a IDE reclama e porque a atualizacao da dependencia nao foi “absorvida” pela compilacao. vendor/ e uma copia dos fontes, enquanto go.mod/go.sum continuam sendo a declaracao das dependencias e de suas verificacoes.

Erro #3: atualizar go.mod, mas esquecer de atualizar vendor/.
Esse e um dos tipos mais desagradaveis de dessincronizacao: o repositorio tem vendor/ com conteudo antigo, enquanto o go.mod ja exige outra coisa. Como resultado, a compilacao “em um modo” pega uma coisa, “em outro modo” pega outra, e comecam as historias do tipo “na minha maquina funciona”. E especialmente doloroso detectar isso quando o CI esta configurado de forma diferente da desenvolvimento local.

Erro #4: enxergar vendor/ como “acelerador padrao de desenvolvimento”.
As vezes parece: “Se as dependencias estao no repositorio, entao vai ser mais rapido”. Na pratica, o que costuma acelerar e apenas a compilacao offline, enquanto o preco e o aumento do repositorio e a complicacao da revisao. Vendoring normalmente e uma solucao para restricoes (rede, auditoria, politica), e nao uma otimizacao universal.

Erro #5: commitar vendor/ sem entender “por que”, porque “esta na moda”.
Em algumas equipes, vendor/ e uma parte critica do processo; em outras, e peso morto. Se nao ha requisitos de compilacao hermetica e nao ha problemas de acesso aos modulos, vendor/ pode muito bem acabar sendo uma mala sem alca: dificil de carregar, dificil de jogar fora.

1
Tarefa
Go SELF, nível 35, lição 3
Bloqueado
Primeiro vendor
Primeiro vendor
1
Tarefa
Go SELF, nível 35, lição 3
Bloqueado
Vendor não salva
Vendor não salva
1
Tarefa
Go SELF, nível 35, lição 3
Bloqueado
Registro corrompido
Registro corrompido
1
Tarefa
Go SELF, nível 35, lição 3
Bloqueado
Execução sem replace
Execução sem replace
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION