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.