Boas práticas de versionamento semântico em bibliotecas internas
1. Por que versionamento semântico importa em bibliotecas internas?
Em ambientes corporativos, bibliotecas internas frequentemente são tratadas com menos rigor do que pacotes públicos. Essa é uma armadilha perigosa. O versionamento semântico (SemVer) não é apenas para consumidores externos — ele é a espinha dorsal da confiabilidade em sistemas modulares internos.
Diferença entre versionamento público e interno: Em bibliotecas públicas, o impacto de uma quebra atinge milhares de usuários. Em bibliotecas internas, o impacto é mais controlado, mas não ausente. Uma mudança breaking em uma biblioteca compartilhada por cinco equipes pode paralisar dezenas de pipelines, causar retrabalho e gerar incidentes silenciosos em produção.
Prevenção de quebras silenciosas: Sem SemVer, uma atualização de dependência pode introduzir breaking changes sem aviso. Por exemplo, se uma biblioteca interna auth-lib muda o formato do token sem incrementar a versão major, todos os serviços que dependem dela podem falhar em runtime.
Comunicação clara entre times: O SemVer estabelece um contrato claro:
- Major (X.0.0): Mudanças incompatíveis com versões anteriores
- Minor (0.X.0): Novas funcionalidades retrocompatíveis
- Patch (0.0.X): Correções de bugs retrocompatíveis
# Exemplo de package.json com versionamento semântico explícito
{
"name": "@internal/auth-lib",
"version": "2.5.1",
"description": "Biblioteca interna de autenticação",
"main": "dist/index.js",
"engines": {
"node": ">=18.0.0"
}
}
2. Definindo o contrato de versão para consumo interno
O primeiro passo para um versionamento eficaz é definir o que constitui a API pública da biblioteca. Em bibliotecas internas, isso inclui:
- Funções exportadas: Assinaturas de funções, tipos de retorno
- Interfaces e tipos: Estruturas de dados expostas
- Hooks e componentes: Em bibliotecas React internas
- Configurações esperadas: Variáveis de ambiente, arquivos de configuração
Quando considerar uma mudança como breaking change no contexto interno:
- Remoção de parâmetros obrigatórios
- Alteração do formato de retorno
- Mudança em nomes de funções exportadas
- Alteração em tipos de dados compartilhados
- Mudança no comportamento esperado (ex: ordem de execução)
# Exemplo de contrato de API pública documentado
# @internal/user-lib API pública
## Funções exportadas (breaking se alteradas)
- getUser(id: string): Promise<User>
- createUser(data: CreateUserInput): Promise<User>
- deleteUser(id: string): Promise<void>
## Interfaces (breaking se alteradas)
- interface User { id: string; name: string; email: string; }
- interface CreateUserInput { name: string; email: string; }
## Comportamento esperado
- getUser() retorna null se usuário não existe (não lança erro)
- createUser() valida email antes de criar
Versionamento de dependências transitivas: Em bibliotecas internas, é crucial especificar versões exatas de dependências no lockfile para evitar surpresas. Use npm shrinkwrap ou yarn.lock versionado.
3. Estrutura de release baseada em Conventional Commits
Conventional Commits fornece um padrão leve para mensagens de commit que se correlacionam diretamente com o versionamento semântico.
Padrão de commits:
feat: adiciona validação de CPF no cadastro de usuário
fix: corrige timeout na consulta de histórico
BREAKING CHANGE: altera formato do token JWT de base64 para JWE
Uso de semantic-release: Ferramenta que analisa commits e automaticamente:
1. Determina o próximo número de versão
2. Gera changelog
3. Publica a nova versão
Integração com CI/CD:
# .github/workflows/release.yml (exemplo)
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
4. Gerenciamento de changelog automatizado e rastreável
Um changelog bem estruturado é essencial para que as equipes consumidoras saibam o que mudou e como reagir.
Geração a partir de commits convencionais:
# Changelog gerado com standard-version
# Changelog
## [3.2.0] - 2025-03-15
### Features
- Adiciona suporte a autenticação biométrica (feat: #123)
- Implementa cache de tokens (feat: #127)
### Bug Fixes
- Corrige vazamento de memória em sessões longas (fix: #130)
- Ajusta validação de email duplicado (fix: #132)
### BREAKING CHANGES
- Altera formato do token JWT para JWE (#125)
- Migração: atualizar todos os consumidores para usar `jwt.decode()`
Diferenciação de changelogs:
- Changelog técnico (interno): Detalhado, com links para PRs e issues
- Changelog para stakeholders: Resumo executivo, foco em impacto para negócio
Inclusão de links: Cada entrada deve referenciar a issue ou tarefa correspondente no sistema de gerenciamento (Jira, Linear, GitHub Issues).
5. Políticas de release e versionamento em monorepos
Monorepos com múltiplas bibliotecas internas exigem estratégias específicas.
Versionamento independente vs. sincronizado:
- Independente: Cada biblioteca tem seu próprio ciclo de versão. Ideal quando as bibliotecas têm equipes e cronogramas diferentes.
- Sincronizado: Todas as bibliotecas compartilham o mesmo número de versão. Útil quando há dependências fortes entre elas.
Ferramentas recomendadas:
- Changesets: Perfeito para monorepos com muitas bibliotecas. Permite que cada PR declare quais pacotes foram alterados e como.
- Lerna: Clássico para gerenciamento de monorepos, com suporte a versionamento independente.
# Exemplo de configuração de changeset
# .changeset/config.json
{
"$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
Estratégias para dependências entre bibliotecas do mesmo monorepo:
- Use workspace:* no package.json para referenciar versões locais
- Atualize automaticamente as dependências internas quando uma biblioteca sobe de versão
- Evite dependências circulares entre bibliotecas do mesmo monorepo
6. Testes de regressão e validação antes do bump de versão
Antes de publicar uma nova versão, é fundamental validar que a mudança não quebrou nada.
Execução de testes automatizados:
# Script de validação pré-release
npm run test:unit
npm run test:integration
npm run test:compatibilidade -- --versao-anterior=2.5.0
Canary releases e versões pré-release:
# Publicação de versão alpha para teste
npm version prerelease --preid alpha
npm publish --tag alpha
# Consumidores podem testar com:
npm install @internal/auth-lib@3.0.0-alpha.1
Verificação de compatibilidade:
# Usando semver-diff para comparar versões
const semver = require('semver');
function verificarCompatibilidade(versaoAtual, novaVersao) {
const diff = semver.diff(versaoAtual, novaVersao);
if (diff === 'major') {
console.log('⚠️ BREAKING CHANGE detectada!');
// Disparar alerta para times consumidores
}
return diff;
}
7. Documentação e comunicação das versões internas
A documentação deve ser explícita e a comunicação proativa.
Versionamento explícito no README:
# @internal/auth-lib
## Versão Atual: 2.5.1
### Compatibilidade
- Node.js >= 18.0.0
- @internal/logger-lib: ^1.3.0
### Histórico de Versões
| Versão | Data | Mudanças |
|--------|------|----------|
| 3.0.0 | - | BREAKING: Novo formato JWT |
| 2.5.1 | 2025-03-10 | Fix: Timeout em sessões longas |
| 2.5.0 | 2025-02-28 | Feature: Autenticação biométrica |
Notificações automáticas:
- Configure webhooks no Slack para cada nova versão publicada
- Envie e-mails automáticos para os mantenedores das bibliotecas consumidoras
- Crie issues de migração para versões major, com checklist de passos necessários
Guia de migração entre versões major:
# Guia de Migração: v2.x → v3.0.0
## Mudanças Principais
1. Token JWT agora usa formato JWE em vez de base64
2. Função `authenticate()` agora aceita objeto de opções em vez de parâmetros posicionais
## Passos para Migração
1. Atualizar dependência: `npm install @internal/auth-lib@^3.0.0`
2. Substituir chamadas de `authenticate(token, secret)` por `authenticate({ token, secret })`
3. Atualizar decodificação de tokens para usar `jwt.decode()` em vez de `atob()`
## Testes Recomendados
- Executar suite de testes da sua aplicação
- Verificar logs de autenticação em ambiente de staging
O versionamento semântico em bibliotecas internas não é burocracia — é engenharia de confiabilidade. Quando bem implementado, ele transforma a gestão de dependências internas de um caos potencial em um processo previsível e rastreável. Adote Conventional Commits, automatize seus releases, documente suas breaking changes e comunique-se proativamente com seus consumidores. Sua equipe (e seu sono) agradecerão.
Referências
- Semantic Versioning 2.0.0 (semver.org) — Documentação oficial do padrão SemVer, com especificação completa e exemplos
- Conventional Commits 1.0.0 — Especificação do padrão de commits que se alinha ao versionamento semântico
- semantic-release - Automated package publishing — Ferramenta para automação completa de releases baseada em Conventional Commits
- Changesets - Managing versions in monorepos — Ferramenta para versionamento em monorepos com suporte a changelogs automatizados
- Lerna - Monorepo management — Gerenciador de monorepos com suporte a versionamento independente e publicação de múltiplos pacotes
- standard-version - Automated changelog generation — Utilitário para gerar changelogs e bump de versão baseado em commits convencionais
- semver-diff - Semantic version diff utility — Pacote Node.js para comparar versões semânticas e identificar o tipo de mudança