Isolando seu Ambiente de Desenvolvimento: Do mise/asdf para DevContainers

Em um mundo ideal, nossa máquina de desenvolvimento seria sempre limpa e cada projeto teria exatamente o que precisa, sem conflitos. Mas quem nunca passou horas brigando com versões de Node, Python ou Go que atire a primeira pedra (ou o primeiro rm -rf /).

Recentemente, eu estava tendo problemas com o mise e o asdf para isolar meus ambientes.

São ótimas ferramentas, por exemplo o mise, por ser escrito em Rust tinha um bom desempenho e o adsf sendo migrado para Go também.

O problema começou quando eu precisei da integração deles na IDE. Por mais que existam plugins, o casamento entre os shims do asdf/mise e o VS Code (e o Zed) nem sempre é perfeito. De repente, a IDE não encontra o linter, o LSP se perde ou você percebe que está rodando o teste em uma versão de runtime diferente da que o terminal está mostrando. É aquele atrito sutil, mas constante, que drena a produtividade.

Foi aí que eu descobri a existência dos DevContainers, que é uma especificação aberta criada pela Microsoft (talvez a segunda melhor coisa que a Microsoft já criou, depois do VSCode) para a criação de ambiente de desenvolvimento completos, padronizados e isolados baseado em conteinêres Docker.

A ideia é simples, em vez de tentar convencer o seu sistema operacional a se comportar como o ambiente de execução, você move o ambiente de desenvolvimento para dentro de um container Docker.

Como usar

Com os devcontainers, você define:

A imagem base (ex: Debian, Alpine, ou uma oficial da linguagem).

Quais extensões da IDE devem ser instaladas automaticamente. (Somente compatível com o VSCode e o Visual Studio no momento)

Configurações de sistema, variáveis de ambiente e ferramentas de CLI.

E quando você abre o projeto, o VS Code (ou qualquer IDE compatível, hoje as principais IDEs do mercado tem integração nativa com o DevContainers sem a necessidade de plugins e isso foi um baita ponto positivo para mim) detecta a configuração e “se monta” dentro do contêiner. O código continua no seu disco local, mas o contexto de execução, as ferramentas de build e até os utilitários de rede estão isolados.

Para você começar a trabalhar com o devcontainers, basta criar um diretório .devcontainers na raiz do seu repositório e criar um arquivo devcontainer.json como o do exemplo abaixo:

// Para mais detalhes, veja https://aka.ms/devcontainer.json.
{
"name": "Meu Projeto Go",
// Use uma imagem base pré-configurada para Go da Microsoft.
// Veja a lista de imagens aqui: https://github.com/devcontainers/images
"image": "mcr.microsoft.com/devcontainers/go:1-1.22-bookworm",

// Use 'features' para adicionar ferramentas comuns de forma modular.
"features": {
// Adiciona suporte a Git.
"ghcr.io/devcontainers/features/git:1": {
"ppa": true
}
},

// Configura as extensões que devem ser instaladas automaticamente no VS Code.
"customizations": {
"vscode": {
"extensions": [
// Extensão oficial para Go.
"golang.go",
// Ferramentas para Docker e Containers.
"ms-azuretools.vscode-docker",
"ms-vscode-remote.remote-containers",
// Um linter para YAML, útil para arquivos de CI/CD.
"redhat.vscode-yaml"
],
// Configurações específicas do VS Code para rodar dentro do container.
"settings": {
"go.toolsManagement.checkForUpdates": "off",
"go.useLanguageServer": true,
"terminal.integrated.defaultProfile.linux": "bash"
}
}
},

// Use 'forwardPorts' para expor portas da aplicação para sua máquina local.
// "forwardPorts": [8080],

// Use 'postCreateCommand' para rodar comandos de setup adicionais.
"postCreateCommand": "go version && go mod tidy"

// Comente a linha abaixo se você quiser rodar como um usuário diferente do 'vscode'.
// "remoteUser": "vscode"
}

Se você tiver um Dockerfile personalizado do seu ambiente de desenvolvimento, você coloca ele nesse mesmo diretório, que o build dele será feito automaticamente assim que você acessar o repositório dentro da IDE.

Algumas vantagens

IDEs com integração nativa: quase todas as principais IDEs hoje aceitam a especificação nativamente, então basta basicamente abrir o projeto e aguardar a IDE montar o ambiente.

Máquina mais limpa: Minha máquina principal não precisa ter várias versões do Go, Python ou Node. Você só precisa do Docker e da IDE.

Uma melhor reprodutibilidade: Se você mudar de máquina ou entregar o projeto para um novo desenvolvedor, não precisa seguir muitos passos para configurar o ambiente. É só ter o Docker instalado, abrir a IDE e voilà, tudo funciona exatamente como estava.

Fim do “Conflito de Versões”: Esse talvez seja mais óbvio, posso ter um projeto que exige uma versão legada de uma lib e outro que usa o que há de mais moderno, sem que um “vaze” para o outro.

Algumas desvantagens

A preparação do ambiente: Costuma ser mais lenta do que usando o mise ou asdf, já que precisa esperar o tempo de baixar ou fazer o build da imagem.

Conhecimento de Docker: você precisa pelo menos saber o básico sobre o Docker para ter um bom uso da especificação.

Debug de problemas de rede: por conta da característica isolada dos contêineres, pode ser um pouco mais complicado de se fazer um debug de algum problema de conectividade.

Conclusão

O mise e o asdf continuam sendo ótimas ferramentas, mas quando você precisa de uma melhor integração com a IDE que você usa (caso você use VSCode, Zed ou IntelliJ ou variações delas), aí na minha opinião o DevContainers brilha. Pode ser mais demorada a criação do ambiente, mas eu acredito que é mais estável do que o uso do mise e do asdf.

Interações & Comentários