Developer Experience (DX) é fundamental para a produtividade de times. Compartilho aprendizados práticos de como criar pacotes e bibliotecas internas transformou a velocidade e qualidade do desenvolvimento no dia a dia.
O Que é Developer Experience?
Developer Experience vai além de apenas "fazer funcionar". É sobre criar um ambiente onde desenvolvedores podem ser produtivos, felizes e focados em resolver problemas de negócio, não em configurar ferramentas ou reescrever código que já existe.
Componentes essenciais da DX:
- Ferramentas que funcionam "out of the box"
- Documentação clara e acessível
- Feedback rápido (builds, testes, linting)
- Código reutilizável e bem estruturado
- Onboarding simplificado para novos membros do time
Por Que Criar Pacotes Internos?
1. Consistência Entre Projetos
Quando múltiplos times trabalham em projetos diferentes, é comum que cada um reinvente a roda. Criar pacotes compartilhados garante que todos usem as mesmas soluções testadas e validadas.
Benefícios observados:
- Padrões de código consistentes
- Menos bugs por reimplementações incorretas
- Facilita code review (revisores já conhecem os padrões)
- Reduz tempo de onboarding
2. Aceleração do Desenvolvimento
Pacotes bem feitos eliminam trabalho repetitivo. Em vez de configurar TypeScript, ESLint, Prettier e outras ferramentas em cada projeto novo, você pode simplesmente instalar um pacote que já traz tudo configurado.
// Antes: Configuração manual em cada projeto // tsconfig.json, .eslintrc, .prettierrc, etc. // Depois: Um único comando npm install @empresa/config-preset
3. Manutenção Centralizada
Quando você precisa atualizar uma dependência ou corrigir um bug, faz isso uma vez e todos os projetos se beneficiam. Isso é especialmente valioso para correções de segurança.
Estratégias de Reaproveitamento de Código
1. Identificando Oportunidades de Reaproveitamento
O primeiro passo é identificar padrões que se repetem. Alguns sinais claros:
Código duplicado:
- Funções utilitárias idênticas em múltiplos projetos
- Componentes React similares com pequenas variações
- Configurações repetidas (webpack, babel, etc.)
- Hooks customizados que resolvem o mesmo problema
Exemplo prático:
// Projeto A
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(value);
};
// Projeto B (código duplicado!)
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(value);
};Solução: Extrair para um pacote `@empresa/utils`:
export const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(value);
};2. Criando Pacotes com Monorepo
Monorepos são ideais para gerenciar múltiplos pacotes. Ferramentas como Turborepo, Nx ou pnpm workspaces facilitam o gerenciamento.
Estrutura de exemplo:
monorepo/ ├── packages/ │ ├── utils/ # Utilitários gerais │ ├── ui-components/ # Componentes React compartilhados │ ├── config/ # Configurações (ESLint, TypeScript, etc.) │ └── hooks/ # Hooks customizados ├── apps/ │ ├── web-app/ │ └── admin-panel/ └── package.json
Vantagens do monorepo:
- Compartilhamento de código simplificado
- Builds incrementais (só recompila o que mudou)
- Versionamento coordenado
- Testes integrados
Boas Práticas na Criação de Pacotes
1. API Design Consistente
Uma boa API é intuitiva e previsível. Siga padrões estabelecidos e seja consistente:
// ✅ Bom: API consistente
export const formatCurrency = (value: number): string => { ... }
export const formatDate = (date: Date): string => { ... }
export const formatPhone = (phone: string): string => { ... }
// ❌ Ruim: Inconsistente
export const currency = (value: number): string => { ... }
export const formatDate = (date: Date): string => { ... }
export const phoneFormatter = (phone: string): string => { ... }2. TypeScript First
Sempre forneça tipos TypeScript. Isso melhora a DX significativamente:
// ✅ Bom: Tipos explícitos
export interface ApiResponse<T> {
data: T;
status: number;
message?: string;
}
export const fetchData = async <T>(
url: string
): Promise<ApiResponse<T>> => {
// ...
};
// ❌ Ruim: Sem tipos
export const fetchData = async (url) => {
// ...
};3. Documentação Clara
Documentação é crucial. Use JSDoc ou ferramentas como TypeDoc:
/**
* Formata um número como moeda brasileira (BRL)
*
* @param value - O valor numérico a ser formatado
* @returns String formatada no padrão brasileiro (ex: "R$ 1.234,56")
*
* @example
* ```ts
* formatCurrency(1234.56) // "R$ 1.234,56"
* formatCurrency(0) // "R$ 0,00"
* ```
*/
export const formatCurrency = (value: number): string => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(value);
};Exemplo Prático: Criando um Pacote de Configuração
Vou mostrar como criar um pacote de configuração TypeScript reutilizável:
Estrutura do Pacote
packages/config-typescript/ ├── package.json ├── tsconfig.json └── README.md
package.json
{
"name": "@empresa/typescript-config",
"version": "1.0.0",
"description": "Configuração TypeScript compartilhada",
"main": "tsconfig.json",
"files": [
"tsconfig.json"
],
"peerDependencies": {
"typescript": "^5.0.0"
}
}tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}Uso no Projeto
{
"extends": "@empresa/typescript-config/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}Como isso foi útil no dia a dia
Implementação de pacotes compartilhados resultou em:
- Redução de 60% no tempo de setup — Novos projetos ficaram prontos para desenvolvimento muito mais rápido
- Consistência de 95% entre projetos — Padrões unificados facilitaram code reviews e manutenção
- Redução de 40% em bugs — Reaproveitar código testado reduziu erros comuns
- Aceleração de 3x no onboarding — Novos desenvolvedores se tornaram produtivos muito mais rápido
Desafios e Como Superá-los
1. "Mas é só uma função pequena..."
Problema: Resistência a criar pacotes para código "simples".
Solução: Lembre-se que código simples se multiplica. Se 5 projetos precisam da mesma função "simples", você já tem 5 cópias para manter.
2. Over-engineering
Problema: Criar pacotes muito genéricos que ninguém usa.
Solução: Comece específico. Extraia código que já está sendo duplicado, não código que "pode ser útil no futuro".
3. Versionamento Complexo
Problema: Gerenciar versões de múltiplos pacotes pode ser complicado.
Solução: Use ferramentas como Changesets ou Lerna para automatizar o versionamento.
Conclusão
Investir em Developer Experience através de pacotes compartilhados é uma estratégia de negócio. Times mais produtivos entregam mais valor com maior qualidade.
Comece pequeno: identifique padrões repetidos, extraia para pacotes, documente e compartilhe. Com o tempo, você terá uma biblioteca robusta que acelera todo o time. No dia a dia, isso se traduz em menos tempo configurando projetos e mais tempo resolvendo problemas de negócio.
Este artigo reflete aprendizados práticos de criar e manter pacotes compartilhados em projetos reais. Developer Experience é uma jornada contínua de melhoria, e esses aprendizados foram valiosos no dia a dia.