Blog /desenvolvimento

Ambiente de desenvolvimento com Docker e Git: do zero ao deploy

Por que seu código funciona local e quebra em produção? A resposta está no ambiente: Git, Docker e a diferença entre dev e produção.

7 min
Antes do Framework — série sobre fundamentos de desenvolvimento

Antes do Framework — Ep. 04

Você já entende redes e sabe se virar no terminal. Etapa 3: o ambiente onde o código vai rodar — em qualquer máquina.

"Mas na minha máquina funciona."

Se você já disse isso ou vai dizer em algum momento da sua carreira, esse episódio é pra você.

O problema quase nunca é o código. É o ambiente onde ele roda. E entender isso vai economizar horas de debug e muita vergonha em produção.


O problema do ambiente

Imagine dois cenários:

Cenário 1: Você desenvolve no Windows com Node 18. O servidor do cliente roda Ubuntu com Node 20. Sua aplicação usa uma lib que tem comportamento diferente entre as versões. Deploy feito, sistema quebrado. Você passa horas até descobrir que era isso.

Cenário 2: Você e mais dois devs trabalham no mesmo projeto. Um usa Mac, outro Windows, outro Linux. Cada um instalou dependências em ordens diferentes. "Na minha máquina funciona" vira piada interna, mas o projeto nunca roda igual pra todo mundo.

A solução para os dois casos é a mesma: paridade de ambiente. O ambiente de desenvolvimento precisa ser idêntico ao de produção.


Git: versionamento como hábito

Antes de falar de ambiente, uma base que precisa estar sólida: Git.

Não é só "commitar e dar push". É o registro de tudo que aconteceu no projeto.

O fluxo básico

git init                        # inicia o repositório
git status                      # mostra o que mudou
git add arquivo.ts              # adiciona ao staging
git add .                       # adiciona tudo (use com cuidado)
git commit -m "feat: adiciona autenticação"
git push origin main            # envia para o repositório remoto

Branches: trabalhe sem medo de quebrar tudo

git checkout -b feat/login      # cria e entra na branch
git checkout main               # volta para a main
git merge feat/login            # une a branch na main
git branch -d feat/login        # deleta a branch após o merge

A regra de ouro: nunca commite direto na main em projetos reais. A main é o que está em produção.

O que nunca deve ir para o Git

# .gitignore
.env
node_modules/
.DS_Store
dist/
*.log

Credenciais no Git são um dos erros mais comuns de segurança. Robôs varrem o GitHub em segundos atrás de chaves de API e senhas expostas. Crie sempre um .env.exemplo com as chaves vazias para documentar o que é necessário.


Variáveis de ambiente: o segredo que separa um curioso de um dev verdadeiro

Nunca faça isso:

const db = new Client({
  host: "db.producao.com",
  password: "minha-senha-secreta",
});

Faça isso:

const db = new Client({
  host: process.env.DB_HOST,
  password: process.env.DB_PASSWORD,
});

E crie um .env local:

DB_HOST=localhost
DB_PASSWORD=senha-local-de-dev

Em produção, essas variáveis são configuradas no servidor ou na plataforma de deploy (Vercel, Railway, etc). O código é o mesmo. O ambiente muda. Isso é paridade.


Docker: empacotando o ambiente junto com a aplicação

Docker resolve o "na minha máquina funciona" de forma definitiva. Em vez de documentar como configurar o ambiente, você escreve a configuração uma vez e ela roda igual em qualquer lugar.

O Dockerfile: a receita da sua aplicação

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Cada linha é uma instrução. O resultado é uma imagem: um snapshot do ambiente com tudo que a aplicação precisa para rodar.

Docker Compose: orquestrando múltiplos serviços

API sozinha raramente basta. Na prática, você precisa de banco de dados, talvez cache, talvez um serviço de filas. O compose.yml define tudo junto:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://postgres:senha@db:5432/meuprojeto
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: meuprojeto
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

Para subir tudo:

docker compose up       # sobe em foreground (você vê os logs)
docker compose up -d    # sobe em background
docker compose down     # derruba tudo
docker compose logs -f  # acompanha os logs em tempo real

O depends_on com healthcheck garante que o banco esteja saudável antes da API tentar se conectar. É um detalhe pequeno que evita crashes na inicialização.


Dev, staging e produção: entendendo os ambientes

A maioria dos projetos sérios tem pelo menos três ambientes:

AmbientePropósitoQuem acessa
devDesenvolvimento localVocê
stagingTestes antes de publicarTime e cliente
produçãoO sistema realUsuários finais

Cada um tem suas próprias variáveis de ambiente, banco de dados separado e configurações diferentes. Você nunca testa em produção. Nunca.

Na prática, com Docker Compose, você pode ter um compose.dev.yml e um compose.prod.yml com configurações específicas para cada contexto.


O checklist antes de qualquer deploy

Antes de mandar código para produção, passe por isso:

  • .env está no .gitignore
  • Todas as variáveis necessárias estão documentadas no .env.exemplo
  • O Dockerfile está atualizado com as dependências corretas
  • docker compose up sobe sem erros do zero
  • A branch foi revisada e merged via PR
  • O ambiente de staging foi testado

Parece burocrático no começo. Com o tempo vira instinto.


→ Próximo episódio

Você sabe versionar código e garantir paridade de ambiente. O próximo passo é aprender a trabalhar com mais de uma pessoa no mesmo repositório — sem sobrescrever o trabalho de ninguém.

Antes do Framework — Ep. 05: Git em time — branches, Pull Requests e code review na prática

Gostou do artigo?

Newsletter

Em breve

Em breve você poderá receber novos artigos direto no seu email.