Communitygithub.com

caseycme/skills

Orienta a refatoração sistemática para alinhar funcionalidades com padrões estabelecidos, remover duplicação e melhorar o reuso de código. Inclui orientações sobre DataGrid/pattern-library, tipagem, paginação de fetch, view-model vs override-prop, e layout MUI sx-first (evitar props shorthand de estilo Box obsoletas). Use ao refatorar funcionalidades, consolidar código duplicado, extrair utilitários compartilhados ou alinhar padrões entre funcionalidades semelhantes.

O que é skills?

Esta skill guia o processo de refatoração sistemática de código, com foco em alinhar funcionalidades existentes com padrões estabelecidos, remover duplicação e melhorar o reuso. Ela é útil para desenvolvedores que trabalham com aplicações React/TypeScript, especialmente aquelas que utilizam Material-UI (MUI) e Next.js App Router. A skill abrange desde a análise pré-refatoração (identificação de funcionalidades semelhantes e oportunidades de duplicação) até etapas concretas como extração de código compartilhado, consolidação de componentes, alinhamento de padrões de carregamento, e limpeza de comentários. Ela também inclui práticas avançadas de qualidade de código, como uso de `import type`, remoção de asserções de tipo desnecessárias, e extração de padrões JSX repetidos. Além disso, a skill fornece diretrizes específicas para componentes DataGrid, tipagem, paginação de fetch, e layout MUI com `sx`, priorizando o uso de `sx` sobre props shorthand obsoletas do Box. É compatível com ferramentas como Claude Code, Cursor e Codex, e seu conteúdo é baseado no arquivo SKILL.md.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/caseycme/skills/tree/HEAD/skills/engineering/refactoring

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Habilidade de Refatoração

Use esta habilidade ao refatorar funcionalidades para alinhar com padrões estabelecidos, remover duplicação e melhorar o reuso de código.

Para saber se a UI pertence à biblioteca de padrões (pattern-library) ou à composição local, use a habilidade design-system.

Análise Pré-Refatoração

1. Identificar Funcionalidades Semelhantes

  • Encontre funcionalidades comparáveis no código com funcionalidade semelhante
  • Compare estruturas de diretórios, padrões de componentes e uso de utilitários
  • Procure por funcionalidades com funcionalidade semelhante, mas implementações diferentes
  • Use busca semântica para encontrar funcionalidades relacionadas: "funcionalidades com padrões semelhantes de tabela/grid de dados"

2. Encontrar Oportunidades de Duplicação

Pesquise por padrões de código duplicados:

  • Componentes: Procure por arquivos de componentes semelhantes em diferentes funcionalidades (mesmo nome ou funcionalidade semelhante)
  • Hooks: Verifique lógica de hook duplicada (hooks de tabela, hooks de filtro, hooks de agregação)
  • Utilitários: Encontre funções utilitárias duplicadas (agregações, formatadores, validadores, transformadores)
  • Estilos: Identifique padrões de prop sx duplicados ou estilos inline
  • Constantes: Verifique constantes ou objetos de configuração duplicados
  • Tipos: Procure por definições de tipo ou interfaces duplicadas

3. Verificar Utilitários Compartilhados

Antes de criar novos utilitários, verifique os existentes:

  • Pesquise nos diretórios @/util/ por utilitários específicos de domínio
  • Verifique @/util/dates para funções de formatação de data
  • Verifique @/util/numbers para funções de formatação de número
  • Verifique @/components/ para componentes reutilizáveis
  • Verifique pattern-library para componentes do sistema de design
  • Use grep/busca para encontrar implementações existentes antes de criar novas

Processo de Refatoração

Etapa 1: Extrair Código Compartilhado

Ao encontrar duplicatas:

  1. Crie utilitários compartilhados nos diretórios @/util/ apropriados:

    // Exemplo: util/[dominio]/[categoria].ts
    export const funcaoCompartilhada = () => { ... };
    export const constanteCompartilhada = { ... };
    
  2. Atualize todas as funcionalidades para importar do local compartilhado:

    // Arquivo específico da funcionalidade - reexportar compartilhado
    export { funcaoCompartilhada, constanteCompartilhada } from "@/util/[dominio]/[categoria]";
    
  3. Remova implementações duplicadas dos diretórios de funcionalidades

  4. Atualize todas as importações em todo o código

Etapa 2: Consolidar Componentes

Ao encontrar componentes duplicados:

  1. Identifique a versão mais completa/rica em recursos (verifique props, funcionalidade, testes)
  2. Extraia para local compartilhado (@/components/ ou local independente de funcionalidade)
  3. Torne as props flexíveis para suportar todos os casos de uso (use tipos união, props opcionais ou genéricos)
  4. Atualize importações em todas as funcionalidades que usam o componente
  5. Exclua arquivos de componente duplicados
  6. Verifique se todos os usos ainda funcionam corretamente

Etapa 3: Alinhar Padrões de Carregamento

Para estados de carregamento:

  1. Verifique arquivos loading.tsx - devem ser skeletons simples, não layouts de página completos
  2. Remova loaders skeleton no nível do componente - confie no loading.tsx da página para carregamento inicial
  3. Durante refetches: Mostre dados existentes, não skeletons (a menos que explicitamente necessário para UX)
  4. Combine padrões de funcionalidades semelhantes no código
  5. Use convenções do Next.js App Router - loading.tsx lida com carregamento inicial, componentes lidam com estados de refetch

Etapa 4: Consolidar Estilos

Para estilização:

  1. Extraia padrões de estilo reutilizáveis para componentes compartilhados ou utilitários de estilo
  2. Remova props sx inline quando um componente compartilhado existir
  3. Use componentes do sistema de design de pattern-library em vez de estilos personalizados
  4. Verifique objetos de estilo duplicados que poderiam ser utilitários compartilhados ou extensões de tema
  5. Siga padrões do Material-UI - use valores do tema, evite números mágicos

Etapa 5: Verificar Alinhamento de Padrões

Garanta consistência com funcionalidades estabelecidas:

  1. Compare estruturas de diretórios - devem seguir padrões semelhantes (_components/, _hooks/, _utils/)
  2. Verifique padrões de hook - hooks mais simples são aceitáveis se os requisitos da funcionalidade diferirem
  3. Verifique fluxo de dados - deve corresponder a funcionalidades semelhantes quando possível
  4. Revise composição de componentes - deve usar padrões semelhantes (wrappers, composição, etc.)
  5. Verifique convenções de nomenclatura - siga padrões estabelecidos (PascalCase para componentes, camelCase para hooks)

Etapa 6: Consolidação de Funções Utilitárias

Para funções utilitárias:

  1. Verifique se a função existe em utilitários compartilhados primeiro (pesquise no código)
  2. Compare implementações - use a versão mais completa/robusta
  3. Consolide duplicatas - mova para local compartilhado, atualize todas as importações
  4. Documente diferenças - se as implementações diferem intencionalmente, documente o porquê
  5. Preserve funcionalidade - garanta que a versão consolidada lida com todos os casos de uso

Etapa 7: Limpar Comentários

Após a refatoração, remova comentários desnecessários:

  1. Remova notas de migração - Comentários explicando de onde o código foi movido (ex.: "isCollecting agora é exportado de Table/utils.ts")
  2. Remova comentários óbvios - Comentários que apenas reiteram o que o código faz (ex.: "// Reexportar agregações compartilhadas" quando o código mostra claramente as reexportações)
  3. Mantenha comentários explicativos - Comentários que explicam POR QUE algo é feito (ex.: "// Usar setTimeout para garantir que o estado do formulário seja atualizado")
  4. Mantenha comentários de lógica complexa - Comentários que explicam lógica de negócios não óbvia ou casos de borda
  5. Remova comentários TODO/FIXME - A menos que sejam itens de trabalho futuro legítimos

Exemplos de comentários desnecessários:

// ❌ Desnecessário - óbvio pelo código
// Reexportar agregações compartilhadas de util
export { latest, totalRange } from "@/util/keywords/gridAggregations";

// ❌ Desnecessário - nota de migração, não é mais necessária
// isCollecting agora é exportado de Table/utils.ts para evitar duplicação
// Importe de lá: import { isCollecting } from "../KeywordListDetail/Table/utils";

Exemplos de bons comentários:

// ✅ Bom - explica POR QUE
// Usar setTimeout para garantir que o estado do formulário seja atualizado após processamento CSV
setTimeout(() => {
  void handleSubmit(onSubmit)();
}, 100);

// ✅ Bom - explica lógica complexa
// Uma palavra-chave é atualizada se:
// 1. Seu valor lastUpdated mudou (indicador mais confiável), OU
// 2. Não está mais em estado de coleta E já pesquisamos vezes suficientes (fallback)

Etapa 8: Melhorias de Qualidade de Código

Aplique padrões consistentes de qualidade de código em todo o código refatorado:

Verificações de Nulo/Indefinido

Simplifique verificações de nulo e indefinido usando igualdade flexível:

// ❌ Verboso
if (value !== undefined && value !== null) { ... }
if (value !== undefined && value !== null && value !== "") { ... }

// ✅ Simplificado
if (value != null) { ... }
if (value != null && value !== "") { ... }

Nota: Use != null (igualdade flexível) para verificar tanto null quanto undefined em uma única verificação. Isso é idiomático em TypeScript/JavaScript e mais conciso.

Importações Apenas de Tipo

Use import type para importações apenas de tipo para melhorar o desempenho da build e tornar a intenção clara:

// ❌ Importação em tempo de execução para uso apenas de tipo
import { SomeType } from "app-api/types/domain";

// ✅ Importação apenas de tipo
import type { SomeType } from "app-api/types/domain";

Aplique a:

  • Definições de tipo de app-api
  • Importações de interface
  • Tipos utilitários apenas de tipo
  • Qualquer importação usada apenas em anotações de tipo

Remover Asserções de Tipo Desnecessárias

Remova asserções de tipo quando o TypeScript puder inferir o tipo corretamente:

// ❌ Asserção desnecessária
const items = data.map((item) => item as SomeType);

// ✅ Deixe o TypeScript inferir
const items = data.map((item) => item);

Quando manter asserções: Apenas quando o TypeScript genuinamente não puder inferir o tipo, ou ao restringir tipos de uma forma que o TypeScript não possa verificar.

Remover Coalescência Nula Desnecessária

Remova ?? null quando o valor já for anulável ou o TypeScript inferir o tipo correto:

// ❌ Desnecessário - valueGetter já retorna anulável
valueGetter: (value, row) => {
  return row.someField ?? null;
};

// ✅ Simplificado
valueGetter: (value, row) => {
  return row.someField;
};

Quando manter: Quando convertendo explicitamente undefined para null para compatibilidade de API ou quando o sistema de tipos exigir.

Remover Props Não Utilizadas

Remova props de interfaces de componente e implementações quando não forem usadas:

// ❌ Prop não utilizada
interface ComponentProps {
  item: Item;
  unusedField: string; // Nunca usado no componente
}

// ✅ Interface limpa
interface ComponentProps {
  item: Item;
}

Adicionar Tipos Explícitos a Callbacks

Adicione anotações de tipo explícitas a parâmetros de callback quando o TypeScript não puder inferi-los:

// ❌ Any implícito
const items = ids.map((id) => { ... });

// ✅ Tipo explícito
const items = ids.map((id: string | number) => { ... });

Aplique a: Métodos de array (map, filter, forEach), manipuladores de eventos e outros callbacks onde os tipos não são claros pelo contexto.

Extrair Padrões JSX Repetidos

Extraia padrões JSX repetidos em componentes reutilizáveis:

// ❌ Padrão repetido
{
  isAggregation ? (
    <Typography
      variant="body2"
      fontWeight="medium"
      fontStyle="italic"
      color="text.secondary"
    >
      {formattedValue}
    </Typography>
  ) : (
    <Typography variant="body2" fontWeight="medium">
      {formattedValue}
    </Typography>
  );
}

// ✅ Componente extraído
<AggregationTypography
  formattedValue={formattedValue}
  isAggregation={isAggregation}
/>;

Usar Funções Fábrica para Implementações Variadas

Quando a lógica é semelhante, mas precisa lidar com diferentes estruturas de dados, use funções fábrica:

// ✅ Padrão de função fábrica
export const createIntentOperators = <T extends { intent?: Intent | Intent[] }>(
  normalizeToArray: (value: T) => Intent[]
) => {
  return {
    // Operadores que trabalham com array normalizado
  };
};

// Uso em diferentes funcionalidades
const operators1 = createIntentOperators<Feature1Type>((item) => [item.intent]);
const operators2 = createIntentOperators<Feature2Type>(
  (item) => item.intents || []
);

Benefícios: Reutiliza lógica central enquanto permite que diferentes estruturas de dados sejam tratadas adequadamente.

Reforço de Revisão de Código (feedback recorrente)

Aplique estes ao refatorar grids, dashboards, fetches ou formas compartilhadas "linha + métricas" (alinha com feedback repetido de PRs de UI Moz Pro).

DataGrid: alternância de painel de detalhes

  • Prefira DetailPanelToggle de pattern-library para expandir/recolher em uma coluna de painel de detalhes em vez de IconButton + @mui/icons ad hoc em uma célula personalizada.
  • Configure acessibilidade: passe buttonTabIndex={0} quando a célula deve ser focável por tab (veja app/(default)/competitive-research/top-competing-content/_module/components/Table/columns.tsx, SerpFeaturesCell).
  • Layout / centralização: use sx em Box/Stack (ou direction / spacing do Stack quando esse for o padrão estabelecido). Não substitua sx por props de estilo do sistema MUI legadas em Box (ex.: display, alignItems como props de nível superior)—o MUI está descontinuando essa abordagem em favor de sx.

Tipagem: evitar object / formas de linha anônimas

  • Não use readonly object[] para brand_definitions, fragmentos de linha ou listas adjacentes à API quando um tipo estrutural existir ou puder ser definido uma vez.
  • Defina uma interface "like" compartilhada (ex.: BrandDefinitionLike) no módulo utils da funcionalidade; exporte-a e um único auxiliar de linha de exibição (ex.: getBrandDefinitionDisplayLine) em vez de duplicar getBrandName com casts dentro de células.
  • Remova casts redundantes (as BrandDefinitionLike[]) após os parâmetros serem tipados.
  • Reutilize auxiliares de domínio de *_module/utils/dataHelpers quando a forma for um BrandDefinition real; use o auxiliar mais flexível apenas onde as cargas de linha de prompt diferirem.

Typography vs Box (pattern-library)

  • Para texto (estados vazios, "—" inline, spans enfatizados), prefira Typography de pattern-library com component="span" quando apropriado—não Box fingindo ser cópia.
  • API Typography: use variant, color, fontWeight, component onde eles expressam semântica (estes não são descontinuados).
  • Box / wrappers de layout: prefira sx para espaçamento, layout flex, dimensionamento e valores de tema. Evite refatorações impensadas que movam estilização de sx para props shorthand de estilo Box; isso entra em conflito com as diretrizes atuais do MUI.

Limites de componentes: props vs estado interno

  • Derive booleanos de data dentro do componente quando o pai não precisar deles para layout ou estado de URL (ex.: "tem termos de marca" de brand_terms da marca primária).
  • Mantenha alternâncias apenas de UI internas (ex.: alternar entre dois modos de gráfico em um card) a menos que outro irmão ou rota precise controlar o mesmo estado—então documente por que a elevação é necessária.

Fluxo de controle e JSX

  • Prefira return antecipado para branches de carregamento/vazio em vez de um grande ternário atribuído a uma variável imediatamente antes de um único return, a menos que a memoização genuinamente precise de uma expressão.
  • Comparadores de ordenação: nunca codifique um sentinela mágico (ex.: 5) atrelado a um máximo de domínio assumido; use Number.POSITIVE_INFINITY / "ordenar por último" explícito para nulo/indefinido para que o comportamento permaneça correto se os limites mudarem.

Fetches e paginação

  • Evite um limit fixo pequeno (ex.: 100) para listas em escala de dashboard quando o backend for paginado e os projetos puderem exceder uma página.
  • Faça loop com um tamanho de página estável até que uma página retorne menos linhas que o limite ou zero linhas, ou siga o contrato documentado da ação (veja docs da ação moz-pro-service / Bruno para limit máximo).
  • Exiba total ou totais derivados de forma consistente se os chamadores precisarem de contagens.

Wrappers de apresentação: não repita o óbvio

  • Remova campos de estado alternativo redundantes quando o Card pai já definir o título visível (ex.: evite duplicar cardContentTitle que corresponde ao título do card).
  • Verificações de nulo: se uma prop for tipada como string, não faça verificação de nulo; confie no sistema de tipos.

Fetches: coluna de painel de detalhes vs dados da linha

  • Use row ou um fetch simples (ex.: fetchCompetitors) para dados do painel de detalhes—não um hook de fetch complexo que exija gerenciamento de estado adicional.
  • Evite derivar um novo fetch de dados de fetch existentes (ex.: pegar id de uma linha de grid, depois buscar detalhes completos quando a linha já contiver a maioria dos dados).

View-model vs override-prop

  • View-model (componente estatelss + hook separado): prefira quando a funcionalidade principal envolver fetch, transformação de dados ou estados de carregamento, e o componente puder ser um apresentacional puro.
  • Override-prop (props do componente que substituem lógica interna): prefira quando o componente for predominantemente UI que pode ser personalizada pelo chamador (ex.: um MetricCard com overrides para valor, cor, tooltip).
  • Não force view-model em componentes simples que só renderizam props.

Reducers: casos de ação ausentes

  • Em um reducer (ex.: useReducer da funcionalidade), se uma ação retornar state inalterado, ainda liste-a explicitamente no switch com return state; ou um comentário: // Ação X não altera estado, apenas Y. Isso torna casos ausentes rastreáveis na revisão e impede que o TypeScript perca cobertura.

Migração MUI v4 → v5 (sx vs props shorthand)

  • No MUI v5+, props shorthand de sistema (ex.: display="flex", alignItems="center") em Box/Stack/Grid estão sendo descontinuados em favor da prop sx.
  • Correção: mova essas props para dentro de sx:
// ❌ MUI v4 legado (descontinuado)
<Box display="flex" alignItems="center" justifyContent="space-between">

// ✅ MUI v5+ (sx-first)
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
  • Exceções: Stack usa direction/spacing/divider como props de nível superior (não descontinuadas). Typography usa variant/color/fontWeight (não descontinuados).
  • Quando refatorar: corrija à medida que tocar no código—não faça uma migração em massa. Se uma seção estiver sendo refatorada por outros motivos, mova o estilo para sx.
  • Evite regressões: se uma prop shorthand for amplamente usada em um arquivo, refatore-a como parte de uma alteração maior, não como uma correção isolada que adicione ruído ao diff.

Padrões de Fetch

Paginação de Fetch

  • Nunca codifique um limit estático pequeno (ex.: 100) para endpoints paginados—projetos podem ter milhares de linhas.
  • Use paginação baseada em cursor ou deslocamento conforme o endpoint exigir.
  • Página com tamanho de página estável (ex.: 1000 se a API suportar) até uma página vazia ou última página.
  • Para dados de painel: faça um único fetch se o total for pequeno (< limite máximo).
  • Para listas em escala: sempre página.
  • Considere memoização: use useCallback para funções de fetch que são passadas como props ou dependências de hook.

Tratamento de Erros de Fetch

  • Sempre trate erros em fetches—não deixe promessas rejeitadas não tratadas.
  • Mostre feedback ao usuário: toast, alerta inline ou estado de erro no componente.
  • Use blocos try/catch ou operações .catch().
  • Para fetches de fundo (ex.: refetch periódico), suprime erros silenciosamente ou registre-os, a menos que o usuário precise saber.
  • Evite mostrar erros de fetch em componentes que não iniciaram o fetch (ex.: um componente filho que recebe dados por prop não deve exibir seu próprio toast de erro para o fetch do pai).

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas