Principios Base
Readability First
El código se lee más de lo que se escribe. Nombres claros, formato consistente.
KISS
La solución más simple que funciona. Sin over-engineering ni optimización prematura.
DRY
Extrae lógica común a funciones. Sin copy-paste. Utilidades compartidas por módulos.
YAGNI
No construyas features antes de necesitarlos. Empieza simple, refactoriza cuando sea necesario.
Puntuación por Archivo (0–10)
api/employees.ts
2.5
Calidad: Crítica ❌
- Tipos
anyen parámetros - Nombres sin semántica (x, flag, data)
- Mutación directa de objetos
- Nesting de 5+ niveles
- Sin error handling en fetch
hooks/search.ts
3.0
Calidad: Crítica ❌
- Promesas secuenciales innecesarias
- Sin tipo de retorno declarado
- Sin error handling ni try/catch
- Concatenación insegura en URL
components/EmployeeCard.tsx
4.0
Calidad: Mejorable ⚠
- Sin tipos en props (props.bmi, props.fn)
- Magic number: 25 (BMI), 500 (timeout)
- Side effect en render (setTimeout + alert)
- Nombres sin contexto (fn, n)
Refactors Aplicados
1 · api/employees.ts — Tipos, naming e inmutabilidad
any → interface
Mutación directa
Deep nesting
Magic flag
✗ ANTES — Code smells detectados
// ❌ any en parámetros, sin tipo retorno async function get(id: any) { const res = await fetch(`/api/employees/${id}`) return res.json() // sin ok check } // ❌ naming: x, flag — magic variable async function process(data: any) { const x = data.employees const flag = true for (let i = 0; i < x.length; i++) { if (flag) { if (x[i].active) { if (x[i].plan) { // ❌ mutación directa x[i].plan.calories = 2000 } } } } }
✓ DESPUÉS — Estándares aplicados
// ✅ Interface con tipos estrictos interface Employee { id: string; active: boolean plan?: { calories: number } } async function fetchEmployee( employeeId: string ): Promise<Employee> { const response = await fetch( `/api/employees/${employeeId}` ) if (!response.ok) throw new Error( `HTTP ${response.status}` ) return response.json() } const DEFAULT_CALORIES = 2000 function applyDefaultCalories( employees: Employee[] ): Employee[] { // ✅ early returns, spread (inmutabilidad) return employees.map(emp => { if (!emp.active) return emp if (!emp.plan) return emp return { ...emp, plan: { ...emp.plan, calories: DEFAULT_CALORIES } } }) }
2 · hooks/search.ts — Promesas paralelas y error handling
Promesas secuenciales
Sin error handling
URL insegura
Promise.all
✗ ANTES
// ❌ sin tipos, secuencial, sin error export function searchEmployees(query) { // espera ~900ms (3 × 300ms) const employees = await fetchEmployees() const plans = await fetchNutritionPlans() const stats = await fetchStats() // ❌ URL concatenada sin encode const response = await fetch( '/api/search?q=' + query ) return response.json() // ❌ sin try/catch }
✓ DESPUÉS
interface SearchResult { employees: Employee[] plans: NutritionPlan[] stats: DashboardStats } // ✅ tipado, paralelo, con error handling export async function searchEmployees( query: string ): Promise<SearchResult> { try { // ✅ ~300ms en vez de ~900ms const [employees, plans, stats] = await Promise.all([ fetchEmployees(), fetchNutritionPlans(), fetchStats() ]) // ✅ URL segura con encodeURIComponent const params = new URLSearchParams( { q: query } ) const response = await fetch( `/api/search?${params}` ) if (!response.ok) throw new Error(`HTTP ${response.status}`) return { employees, plans, stats } } catch (error) { console.error('Search failed:', error) throw new Error('Failed to search employees') } }
3 · components/EmployeeCard.tsx — Types, constants, side effects
Sin tipos en props
Magic numbers
Side effect en render
useEffect correcto
✗ ANTES
// ❌ sin tipos, magic numbers, side effect export function Card(props) { // ❌ 25 = BMI_OVERWEIGHT_THRESHOLD if (props.bmi > 25) { // ❌ side effect directo en render // ❌ 500 = ALERT_DELAY_MS setTimeout( () => alert('BMI alto'), 500 ) } // ❌ fn, n — nombres sin semántica return ( <div onClick={props.fn}> {props.n} </div> ) }
✓ DESPUÉS
// ✅ interface + named constants const BMI_OVERWEIGHT_THRESHOLD = 25 const ALERT_DELAY_MS = 500 interface EmployeeCardProps { employeeName: string bmiScore: number onCardClick: () => void variant?: 'compact' | 'full' } export function EmployeeCard({ employeeName, bmiScore, onCardClick, variant = 'full' }: EmployeeCardProps) { // ✅ side effect en useEffect, no render useEffect(() => { if (bmiScore > BMI_OVERWEIGHT_THRESHOLD) { const timer = setTimeout( () => showBmiWarning(employeeName), ALERT_DELAY_MS ) return () => clearTimeout(timer) } }, [bmiScore, employeeName]) return ( <div onClick={onCardClick} className={`card card--${variant}`}> <span>{employeeName}</span> </div> ) }
Checklist de Code Review
✅ Code Review Checklist — NutriSync v1.0
Naming & Legibilidad
✓
Variables y funciones con nombres descriptivos (no x, flag, data, fn)
ALTA
✓
Funciones siguen patrón verbo-nombre (fetchEmployee, calculateBMI, isValidPlan)
ALTA
Componentes React en PascalCase, hooks con prefijo use, utils en camelCase
MEDIA
Type Safety
✓
Prohibido any en parámetros y retornos — usar interfaces tipadas
ALTA
✓
Props de componentes definidas con interface explícita
ALTA
Respuestas de API validadas con Zod antes de procesar
MEDIA
Inmutabilidad
✓
Sin mutaciones directas de objetos/arrays — usar spread operator o map/filter
ALTA
✓
Prohibido .push() / obj.prop = val fuera de setters explícitos
ALTA
Async & Error Handling
✓
Todas las funciones async con try/catch y error descriptivo
ALTA
✓
Promesas independientes ejecutadas en Promise.all (no secuencial)
ALTA
Verificar response.ok antes de .json() en todos los fetch
ALTA
Code Smells
Sin funciones de más de 50 líneas — dividir en subfunciones si supera
MEDIA
Sin nesting > 3 niveles — usar early returns para aplanar lógica
MEDIA
Sin magic numbers/strings — extraer a constantes con nombre descriptivo
MEDIA
Comentarios explican el POR QUÉ, no el QUÉ (evitar // increment counter)
BAJA