⚙ Estándares de Codificación · Auditoría

Code Review Report — NutriSync

Auditoría de calidad y guía de estándares base para onboarding de equipo

📅 Fecha: Junio 2026
🛠 Stack: Next.js 14 · TypeScript · Supabase
👥 Equipo: 5 devs (3 nuevos)
📁 Archivos auditados: 3
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 any en 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
3
Archivos auditados
14
Issues críticos
3
Refactors aplicados
3.2
Score medio inicial
8.5
Score medio post-refactor
Con estos estándares aplicados, NutriSync puede incorporar los 3 devs nuevos en julio con una base sólida y consistente en todo el codebase.