Aikido

Información sobre la calidad del código de FreeCodeCamp: reglas que pueden mejorar cualquier código base.

Introducción

FreeCodeCamp es más que una plataforma de aprendizaje; es una gran base de código de código abierto con miles de colaboradores y millones de líneas de JavaScript y TypeScript. Gestionar un proyecto tan complejo requiere patrones arquitectónicos consistentes, convenciones de nomenclatura estrictas y pruebas exhaustivas para evitar regresiones y mantener la fiabilidad.

En este artículo, presentamos las reglas de revisión de código más impactantes extraídas de FreeCodeCamp. Cada regla demuestra cómo un diseño cuidadoso, un flujo de datos consistente y un manejo robusto de errores pueden ayudar a los grandes proyectos a mantenerse organizados y reducir el riesgo de errores sutiles con el tiempo.

Los desafíos

Mantener la calidad del código en FreeCodeCamp es todo un reto debido a su gran base de código JavaScript y TypeScript y a los miles de colaboradores que tiene. Para garantizar patrones coherentes, un flujo de datos predecible y una funcionalidad fiable, se necesitan reglas de revisión estructuradas y comprobaciones automatizadas.

Los desafíos clave incluyen patrones de codificación inconsistentes, módulos heredados fuertemente acoplados, cobertura de pruebas desigual, desactualización de la documentación y un alto volumen de solicitudes de extracción.

Estándares claros, validación automatizada y una revisión de código minuciosa ayudan a mantener la base de código mantenible, estable y escalable a medida que el proyecto crece.

¿Por qué importan estas reglas?

Las reglas consistentes de revisión de código mejoran la mantenibilidad al aplicar una estructura de módulo uniforme, convenciones de nomenclatura y flujos de datos predecibles, haciendo que las pruebas sean más fiables y las dependencias más fáciles de rastrear.

También mejoran la seguridad mediante la validación de entradas, el manejo de errores y los efectos secundarios controlados, al tiempo que aceleran el onboarding al ayudar a los nuevos colaboradores a comprender rápidamente las responsabilidades del módulo y los puntos de integración.

Conectando el contexto con estas reglas

Estas reglas se extraen del repositorio y de las solicitudes de extracción de FreeCodeCamp, y reflejan problemas recurrentes como flujos de datos poco claros, falta de manejo de errores y pruebas inconsistentes que afectan la estabilidad y la mantenibilidad.

Cada regla destaca un error concreto, explica su impacto en el rendimiento, la claridad o la fiabilidad, e incluye ejemplos de JavaScript o TypeScript ❌ no conformes vs ✅ conformes.

1. Evite el uso excesivo de cualquier tipo en TypeScript

Evitar el uso del tipo 'any' en TypeScript. Definir siempre tipos precisos y explícitos para variables, parámetros de función y valores de retorno para garantizar la seguridad de tipos y prevenir errores en tiempo de ejecución.

❌ No conforme:

let userData: any = fetchUserData();

✅ Conforme:

interface UserData {
  id: string;
  name: string;
  email: string;
}

let userData: UserData = fetchUserData();

Por qué es importante: El uso de `any` desactiva la verificación de tipos de TypeScript, lo que puede permitir que ocurran errores en tiempo de ejecución y reducir la mantenibilidad y fiabilidad del código. Los tipos explícitos hacen que el código sea más seguro y fácil de entender para otros desarrolladores.

2. Preferir nombres de variables descriptivos en lugar de abreviaturas

Utilice siempre nombres de variables claros y descriptivos. Evite abreviaturas o nombres crípticos que oscurezcan el significado del código.

❌ No conforme:

const usr = getUser();

✅ Conforme:

const user = getUser();

Por qué esto es importante: Los nombres de variables descriptivos hacen que el código sea más fácil de leer, entender y mantener. Una mala nomenclatura puede confundir a los desarrolladores y aumentar el riesgo de introducir errores.

3. Evitar bucles o condicionales profundamente anidados

Refactorice el código para evitar el anidamiento profundo en bucles o condicionales. Utilice retornos anticipados o funciones auxiliares para simplificar la lógica.

❌ No conforme:

if (user) {
  if (user.isActive) {
    if (user.hasPermission) {
      // Realizar acción
    }
  }
}

✅ Conforme:

if (!user) return;
if (!user.isActive) return;
if (!user.hasPermission) return;

// Realizar acción
processUserAction(user);

Por qué esto es importante: La lógica profundamente anidada es difícil de seguir, mantener y probar. Complica la escritura de pruebas unitarias, especialmente para casos negativos y fallos tempranos. Aplanar el flujo de control con retornos anticipados hace que el código sea más fácil de razonar, mejora la cobertura de pruebas y reduce la posibilidad de errores ocultos en casos límite.

4. Garantizar un manejo de errores consistente en toda la base de código.

Implemente siempre un manejo de errores consistente. Utilice funciones de error centralizadas o patrones estandarizados para gestionar las excepciones de manera uniforme.

❌ No conforme:

try {
  // Some code
} catch (e) {
  console.error(e);
}

✅ Conforme:

try {
  // Some code
} catch (error) {
  logError(error);
  throw new CustomError('An error occurred', { cause: error });
}

Por qué esto es importante: Un manejo de errores consistente facilita la depuración, previene comportamientos inesperados y asegura la fiabilidad en toda la aplicación.

5. Evitar la codificación directa de valores de configuración.

No codifiques valores específicos del entorno como URLs, puertos o secretos. Utiliza siempre archivos de configuración o variables de entorno.

❌ No conforme:

const apiUrl = 'https://api.example.com';

✅ Conforme:

const apiUrl = process.env.API_URL;

Por qué esto es importante: Los valores codificados (hardcoded) reducen la flexibilidad, hacen que el código sea menos seguro y complican la implementación en diferentes entornos. El uso de configuraciones asegura la mantenibilidad y la seguridad.

6. Mantener las funciones centradas en una única responsabilidad

Asegúrate de que cada función realice una única tarea bien definida. Evita las funciones que manejan múltiples responsabilidades, ya que esto puede generar confusión y dificultad en el mantenimiento.

❌ No conforme:

function processUserData(user) {
  const validatedUser = validateUser(user);
  saveUserToDatabase(validatedUser);
  sendWelcomeEmail(validatedUser);
}

✅ Conforme:

function validateUser(user) {
  // lógica de validación
}

function saveUserToDatabase(user) {
  // lógica de guardado
}

function sendWelcomeEmail(user) {
  // lógica de envío de correo electrónico
}

Por qué esto es importante: Las funciones con una única responsabilidad son más fáciles de probar, depurar y mantener. Promueven la reutilización del código y mejoran la legibilidad.

7. Evitar el uso de números mágicos

Reemplazar números mágicos con constantes con nombre para mejorar la claridad y mantenibilidad del código.

❌ No conforme:

const area = length * 3.14159 * radius * radius;

✅ Conforme:

const PI = 3.14159;
const area = length * PI * radius * radius;

Por qué esto es importante: Los números mágicos pueden oscurecer el significado del código y hacer que las futuras modificaciones sean propensas a errores. Las constantes con nombre proporcionan contexto y reducen el riesgo de introducir errores.

8. Minimizar el uso de variables globales

Limite el uso de variables globales para reducir las dependencias y los posibles conflictos en el código base.

❌ No conforme:

let user = { name: 'Alice' };

function greetUser() {
  console.log(`Hello, ${user.name}`);
}

✅ Conforme:

function greetUser(user) {
  console.log(`Hello, ${user.name}`);
}

const user = { name: 'Alice' };
greetUser(user);

Por qué esto es importante: Las variables globales pueden crear dependencias ocultas y efectos secundarios impredecibles. Dificultan el seguimiento del origen de los datos o cómo cambian en el código. Pasar datos explícitamente a través de parámetros de función mantiene el flujo de datos claro y controlado, lo que mejora la modularidad, la depuración y la mantenibilidad a largo plazo.

9. Utilizar literales de plantilla para la concatenación de cadenas

Prefiera los template literals sobre la concatenación de cadenas para una mejor legibilidad y rendimiento.

❌ No conforme:

const message = 'Hola, ' + user.name + '! Tienes ' + user.notifications + ' nuevas notificaciones.';

✅ Conforme:

const message = `Hello, ${user.name}! You have ${user.notifications} new notifications.`;

Por qué es importante: Los literales de plantilla proporcionan una sintaxis más limpia y mejoran la legibilidad, especialmente al tratar con cadenas complejas o contenido multilínea.

10. Implementar una validación de entrada adecuada.

Valide siempre las entradas de usuario para evitar que datos no válidos entren en el sistema y para mejorar la seguridad.

❌ No conforme:

function processUserInput(input) {
  // lógica de procesamiento
}

✅ Conforme:

function validateInput(input) {
  if (typeof input !== 'string' || input.trim() === '') {
    throw new Error('Invalid input');
  }
}

function processUserInput(input) {
  validateInput(input);
  // processing logic
}

Por qué esto es importante: La validación de entradas es crucial para prevenir errores, asegurar la integridad de los datos y proteger contra vulnerabilidades de seguridad como los ataques de inyección.

11. Mantener un cambio lógico por cada pull request.

Asegúrate de que cada pull request (PR) implemente un único cambio lógico o característica; evita combinar correcciones, refactorizaciones y adiciones de características no relacionadas en una sola PR.

❌ No conforme:

# "Fix login + update homepage"
--- auth.js
+ if (!user) throw new Error('User not found');

--- HomePage.js
- <button>Start</button>
+ <button>Begin Journey</button>

✅ Conforme: (diferencia)

# PR 1: Fix login validation
+ if (!user) throw new Error('User not found');

# PR 2: Update homepage button
+ <button>Begin Journey</button>

Por qué es importante: Los PRs pequeños y enfocados simplifican la revisión de código, reducen el riesgo de efectos secundarios no deseados y aceleran los ciclos de fusión. Las herramientas de IA pueden detectar cuándo archivos, módulos o dominios no relacionados cambian en el mismo PR, algo que los linters no pueden determinar.

12. Utilizar una nomenclatura alineada con el dominio para APIs y servicios

Nombra las API, servicios y módulos según el dominio de negocio (p. ej., challengeService.createSubmission, no handler1.doIt); los nombres deben reflejar claramente la entidad y la acción.

❌ No conforme:

// backend/services/handler.js
export async function doIt(data) {
  return await process(data);
}

// routes/index.js
router.post('/submit', handler.doIt);

✅ Conforme:

// backend/services/challengeService.js
export async function createSubmission({ userId, challengeId, answer }) {
  return await challengeModel.create({ userId, challengeId, answer });
}

// routes/challenges.js
router.post('/submissions', challengeService.createSubmission);

Por qué esto es importante: Una nomenclatura alineada con el dominio hace que el código se autodocumente, facilita la claridad para nuevos colaboradores y se alinea con la lógica de negocio. Solo una IA consciente del contexto semántico (nombres de entidades, capas de servicio) puede detectar nombres desalineados o genéricos entre módulos.

13. Asegurar que las pruebas cubran fallos y casos límite

Escribe pruebas no solo para el 'happy path' (ruta feliz), sino también para condiciones de error, casos límite y valores frontera; confirma que cada módulo crítico tiene pruebas tanto positivas como negativas.

❌ No conforme:

describe('login', () => {
  it('should succeed with correct credentials', async () => { … });
});

✅ Conforme:

describe('login', () => {
  it('should succeed with correct credentials', async () => { … });
  it('should fail with incorrect password', async () => { … });
  it('should lock account after 5 failed attempts', async () => { … });
});

Por qué esto es importante: La lógica crítica de negocio a menudo falla cuando se omiten casos límite, como entradas inválidas, tiempos de espera o inicios de sesión fallidos. Probar tanto las rutas de éxito como las de fallo asegura que la aplicación se comporte de manera fiable en condiciones reales y previene regresiones difíciles de detectar posteriormente.

14. Evitar la mezcla de capas: los componentes de UI no deben ejecutar lógica de negocio

Mantenga los componentes de la interfaz de usuario (React, front-end) libres de lógica de negocio y llamadas a bases de datos/servicios; delegue estas tareas a servicios o hooks dedicados.

❌ No conforme:

// FreeCodeCamp-style
function CurriculumCard({ user, challenge }) {
  if (!user.completed.includes(challenge.id)) {
    saveCompletion(user.id, challenge.id);
  }
  return <Card>{challenge.title}</Card>;
}

✅ Conforme:

function CurriculumCard({ user, challenge }) {
  return <Card>{challenge.title}</Card>;
}

// In service:
async function markChallengeComplete(userId, challengeId) {
  await completionService.create({ userId, challengeId });
}

Por qué es importante: Mezclar la lógica de negocio en los componentes de la interfaz de usuario (UI) difumina los límites entre capas y hace que los cambios futuros sean arriesgados. También obliga a los desarrolladores front-end a comprender la lógica del backend y ralentiza la colaboración. Mantener las responsabilidades separadas asegura que cada capa pueda evolucionar de forma independiente y reduce el riesgo de introducir errores sutiles al refactorizar.

Conclusión

Al analizar el repositorio de FreeCodeCamp, extrajimos reglas prácticas de revisión de código que ayudan a mantener su gran base de código organizada, legible y mantenible. Estas 20 reglas reflejan prácticas reales de años de contribuciones, aunque no sean aplicadas por ninguna herramienta automatizada.

Aplicar estas lecciones en tus propios proyectos puede mejorar la claridad, reducir errores y facilitar la colaboración. Proporcionan una base sólida para escalar tu código de forma segura, asegurando que, a medida que el proyecto crece, el código siga siendo fiable y fácil de mantener.

La revisión de código es más que comprobar la sintaxis. Se trata de mantener la calidad y la integridad de tu código a lo largo del tiempo. Aprender de un proyecto de código abierto maduro como FreeCodeCamp ofrece a los desarrolladores una guía concreta para mejorar cualquier base de código.

Preguntas frecuentes

¿Tiene preguntas?

Cómo abordan estas reglas el flujo de datos y los límites de los módulos en FreeCodeCamp?

Enfuerzan un manejo consistente de entrada/salida, un comportamiento predecible de las funciones y una clara separación de responsabilidades, reduciendo el acoplamiento y facilitando la refactorización y prueba de los módulos.

Cómo mejoran estas reglas la cobertura y fiabilidad de las pruebas?

Al exigir pruebas unitarias y de integración para casos límite, manejo de errores y lógica de negocio crítica, se aseguran de que las regresiones se detecten tempranamente y de que las pruebas automatizadas reflejen con precisión el comportamiento del sistema.

Cómo ayudan estas reglas a gestionar el código heredado?

Proporcionan patrones para refactorizar módulos fuertemente acoplados o desactualizados, manteniendo la compatibilidad con versiones anteriores y minimizando el riesgo de regresiones.

¿Pueden estas reglas prevenir problemas de seguridad en FreeCodeCamp?

Sí. Aplican la validación de entradas, el manejo seguro de datos y la gestión consistente de errores, lo que mitiga riesgos como ataques de inyección, excepciones no controladas y fugas de datos.

¿Cómo pueden los nuevos colaboradores beneficiarse de estas reglas?

Las responsabilidades claras de los módulos, una nomenclatura consistente y una gestión de errores estandarizada ayudan a los nuevos desarrolladores a comprender rápidamente la arquitectura e integrarse de forma segura sin introducir regresiones.

¿Cómo se derivaron estas reglas de FreeCodeCamp?

Fueron extraídos del análisis de pull requests, cambios de código, hilos de discusión y patrones recurrentes que impactaron la mantenibilidad, legibilidad y estabilidad en toda la base de código.

Asegúrate ahora.

Proteja su código, la nube y el entorno de ejecución en un único sistema central.
Encuentre y corrija vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.