Pular para o conteúdo

17. Manutenção e Evolução - Parte 1: Estratégias de Versionamento e Gerenciamento de Dependências

Este documento apresenta estratégias e práticas para manutenção e evolução de soluções web desenvolvidas com Astro 5, garantindo que seu projeto permaneça atualizado, seguro e escalável ao longo do tempo.

Estratégias de Versionamento

Versionamento Semântico

O versionamento semântico (SemVer) é uma convenção de numeração que facilita o gerenciamento de dependências e atualizações. Siga o formato MAJOR.MINOR.PATCH:

  • MAJOR: Mudanças incompatíveis com versões anteriores
  • MINOR: Adições de funcionalidades compatíveis com versões anteriores
  • PATCH: Correções de bugs compatíveis com versões anteriores
package.json
{
"name": "meu-projeto-astro",
"version": "1.2.3", // MAJOR.MINOR.PATCH
"private": true,
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
},
"dependencies": {
"astro": "^5.0.0"
}
}

Changelog

Mantenha um arquivo CHANGELOG.md para documentar todas as alterações significativas:

# Changelog
Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
## [Não lançado]
### Adicionado
- Nova funcionalidade de pesquisa avançada
### Corrigido
- Problema de layout em dispositivos móveis
## [1.2.0] - 2025-02-15
### Adicionado
- Suporte a internacionalização (i18n)
- Tema escuro
### Alterado
- Melhorias de performance no carregamento de imagens
### Corrigido
- Erro de validação em formulários
## [1.1.0] - 2025-01-10
### Adicionado
- Integração com API de pagamentos
- Componente de carrossel de imagens
### Corrigido
- Problemas de acessibilidade em botões
## [1.0.0] - 2024-12-01
### Adicionado
- Lançamento inicial

Controle de Versão com Git

Estrutura de Branches

Implemente um fluxo de trabalho Git que facilite o desenvolvimento colaborativo:

Terminal window
# Estrutura de branches
main # Código em produção
├── develop # Código para próxima versão
├── feature/nova-funcionalidade
├── feature/outra-funcionalidade
└── bugfix/correcao-erro
└── hotfix/correcao-urgente # Correções para produção

Convenções de Commits

Adote um padrão de mensagens de commit para facilitar a geração automática de changelogs:

Terminal window
# Formato: <tipo>(<escopo>): <descrição>
feat(auth): adiciona autenticação com Google
fix(forms): corrige validação de e-mail
docs(readme): atualiza instruções de instalação
style(buttons): ajusta espaçamento nos botões
refactor(api): simplifica lógica de paginação
test(utils): adiciona testes para funções de formatação
chore(deps): atualiza dependências

Hooks de Pre-commit

Configure hooks de pre-commit para garantir qualidade de código:

.husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Executar linting
npm run lint
# Executar testes unitários
npm run test:unit
# Verificar formatação
npm run format:check

Gerenciamento de Dependências

Estratégias de Atualização

Atualizações Regulares

Estabeleça uma rotina para manter dependências atualizadas:

Terminal window
# Verificar dependências desatualizadas
npm outdated
# Atualizar dependências de patch (correções de bugs)
npm update
# Atualizar dependências de minor (novas funcionalidades)
npx npm-check-updates -u --target minor
# Atualizar todas as dependências (incluindo major)
npx npm-check-updates -u

Versionamento de Dependências

Use o operador de caret (^) para permitir atualizações de minor e patch, mas fixe versões críticas:

package.json
{
"dependencies": {
"astro": "^5.0.0", // Aceita 5.0.1, 5.1.0, mas não 6.0.0
"critical-package": "1.2.3" // Versão fixa
}
}

Ferramentas de Análise de Dependências

Dependabot

Configure o GitHub Dependabot para automatizar atualizações de segurança:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
versioning-strategy: auto
allow:
- dependency-type: "direct"
commit-message:
prefix: "chore"
include: "scope"

Renovate Bot

Alternativa ao Dependabot com mais opções de configuração:

renovate.json
{
"extends": [
"config:base"
],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"automerge": true
},
{
"matchPackagePatterns": ["^@astrojs/"],
"groupName": "astro packages"
}
],
"schedule": ["every weekend"]
}

Auditoria de Segurança

Execute verificações regulares de segurança:

Terminal window
# Verificar vulnerabilidades
npm audit
# Corrigir vulnerabilidades automaticamente quando possível
npm audit fix
# Relatório detalhado
npm audit --json > audit-report.json

Script para verificação de segurança:

scripts/security-check.js
import { execSync } from 'child_process';
import fs from 'fs';
try {
// Executar npm audit e capturar saída
const auditOutput = execSync('npm audit --json').toString();
const auditData = JSON.parse(auditOutput);
// Verificar se existem vulnerabilidades
const vulnerabilities = auditData.vulnerabilities;
const totalVulnerabilities = Object.values(vulnerabilities).reduce(
(sum, severity) => sum + severity.length,
0
);
if (totalVulnerabilities > 0) {
console.log(`⚠️ Encontradas ${totalVulnerabilities} vulnerabilidades:`);
// Listar vulnerabilidades por severidade
for (const [severity, vulns] of Object.entries(vulnerabilities)) {
if (vulns.length > 0) {
console.log(`\n${severity.toUpperCase()}: ${vulns.length}`);
vulns.forEach(vuln => {
console.log(`- ${vuln.name}: ${vuln.title}`);
});
}
}
// Salvar relatório detalhado
fs.writeFileSync(
'security-report.json',
JSON.stringify(auditData, null, 2)
);
console.log('\nRelatório detalhado salvo em security-report.json');
// Tentar corrigir automaticamente
console.log('\nTentando corrigir vulnerabilidades automaticamente...');
execSync('npm audit fix', { stdio: 'inherit' });
// Verificar vulnerabilidades restantes
const remainingOutput = execSync('npm audit --json').toString();
const remainingData = JSON.parse(remainingOutput);
const remainingVulns = Object.values(remainingData.vulnerabilities).reduce(
(sum, severity) => sum + severity.length,
0
);
if (remainingVulns > 0) {
console.log(`\n⚠️ ${remainingVulns} vulnerabilidades não puderam ser corrigidas automaticamente.`);
console.log('Considere atualizar manualmente as dependências afetadas ou avaliar o impacto real.');
} else {
console.log('\n✅ Todas as vulnerabilidades foram corrigidas!');
}
} else {
console.log('✅ Nenhuma vulnerabilidade encontrada!');
}
} catch (error) {
console.error('Erro ao verificar segurança:', error.message);
process.exit(1);
}

Gerenciamento de Lockfiles

Mantenha o arquivo de lock no controle de versão para garantir instalações consistentes:

Terminal window
# Instalar dependências exatamente como definidas no lockfile
npm ci

Estratégia para atualização de lockfile:

.github/workflows/update-lockfile.yml
name: Update Lockfile
on:
schedule:
- cron: '0 0 * * 0' # Todo domingo à meia-noite
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Update lockfile
run: |
npm install
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add package-lock.json
git diff --staged --quiet || git commit -m "chore(deps): atualiza lockfile"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
title: 'chore(deps): atualiza lockfile'
body: 'Atualização automática do lockfile para garantir instalações consistentes.'
branch: 'chore/update-lockfile'

Monitoramento de Dependências Obsoletas

Detecção de Dependências Descontinuadas

scripts/check-deprecated-deps.js
import { execSync } from 'child_process';
import fs from 'fs';
try {
// Obter informações sobre pacotes instalados
const npmList = JSON.parse(
execSync('npm list --json --depth=0').toString()
);
const packageJson = JSON.parse(
fs.readFileSync('package.json', 'utf-8')
);
const dependencies = {
...packageJson.dependencies,
...packageJson.devDependencies
};
// Verificar status de cada dependência
const deprecatedPackages = [];
const outdatedPackages = [];
for (const [name, version] of Object.entries(dependencies)) {
try {
// Verificar se o pacote está descontinuado
const packageInfo = JSON.parse(
execSync(`npm view ${name} --json`).toString()
);
if (packageInfo.deprecated) {
deprecatedPackages.push({
name,
version,
message: packageInfo.deprecated
});
continue;
}
// Verificar se existem alternativas recomendadas
if (packageInfo.replacedBy) {
deprecatedPackages.push({
name,
version,
message: `Substituído por: ${packageInfo.replacedBy}`
});
}
} catch (error) {
console.warn(`Não foi possível verificar o pacote ${name}: ${error.message}`);
}
}
// Verificar pacotes desatualizados
try {
const outdated = JSON.parse(
execSync('npm outdated --json').toString()
);
for (const [name, info] of Object.entries(outdated)) {
outdatedPackages.push({
name,
current: info.current,
wanted: info.wanted,
latest: info.latest
});
}
} catch (error) {
// npm outdated retorna código de saída 1 se encontrar pacotes desatualizados
if (error.status === 1 && error.stdout) {
const outdated = JSON.parse(error.stdout.toString());
for (const [name, info] of Object.entries(outdated)) {
outdatedPackages.push({
name,
current: info.current,
wanted: info.wanted,
latest: info.latest
});
}
} else {
console.warn('Erro ao verificar pacotes desatualizados:', error.message);
}
}
// Gerar relatório
if (deprecatedPackages.length > 0) {
console.log('\n⚠️ Pacotes descontinuados:');
deprecatedPackages.forEach(pkg => {
console.log(`- ${pkg.name}@${pkg.version}: ${pkg.message}`);
});
} else {
console.log('\n✅ Nenhum pacote descontinuado encontrado.');
}
if (outdatedPackages.length > 0) {
console.log('\n📦 Pacotes desatualizados:');
outdatedPackages.forEach(pkg => {
console.log(`- ${pkg.name}: ${pkg.current}${pkg.wanted} (latest: ${pkg.latest})`);
});
} else {
console.log('\n✅ Todos os pacotes estão atualizados.');
}
// Salvar relatório
fs.writeFileSync(
'dependency-report.json',
JSON.stringify({
deprecated: deprecatedPackages,
outdated: outdatedPackages,
timestamp: new Date().toISOString()
}, null, 2)
);
console.log('\nRelatório completo salvo em dependency-report.json');
// Retornar código de erro se houver pacotes descontinuados
if (deprecatedPackages.length > 0) {
process.exit(1);
}
} catch (error) {
console.error('Erro ao verificar dependências:', error.message);
process.exit(1);
}

Continua na Parte 2: Refatoração, Modernização e Monitoramento de Desempenho