Aikido

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 abierto con miles de colaboradores y millones de líneas de JavaScript y TypeScript. Gestionar un proyecto tan complejo requiere patrones de arquitectura coherentes, 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 coherente y una gestión de errores robusta pueden ayudar a los grandes proyectos a mantenerse organizados y reducir el riesgo de errores sutiles a lo largo del tiempo.

Los retos

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

Entre los principales retos se incluyen patrones de codificación incoherentes, módulos heredados estrechamente acoplados, cobertura de pruebas desigual, desviación de la documentación y un elevado volumen de pull requests.

Unas normas claras, la validación automatizada y una cuidadosa revisión del código ayudan a mantener el código base estable y escalable a medida que crece el proyecto.

Por qué son importantes estas normas

Unas reglas de revisión del código coherentes mejoran la capacidad de mantenimiento al imponer una estructura de módulos uniforme, convenciones de nomenclatura y flujos de datos predecibles, lo que hace 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, la gestión de errores y los efectos secundarios controlados, al tiempo que aceleran la incorporación al ayudar a los nuevos colaboradores a comprender rápidamente las responsabilidades de los módulos y los puntos de integración.

Contexto puente para estas normas

Estas reglas se extraen del repositorio de FreeCodeCamp y de pull requests, reflejando problemas recurrentes como flujo de datos poco claro, falta de gestión de errores y pruebas inconsistentes que impactan en la estabilidad y la mantenibilidad.

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

1. Evitar el uso excesivo de cualquier tipo en TypeScript

Evita usar cualquier tipo en TypeScript. Define siempre tipos precisos y explícitos para variables, parámetros de funciones y valores de retorno para garantizar la seguridad de tipos y evitar 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: Usar any desactiva la comprobación de tipos de TypeScript, lo que puede permitir que se produzcan 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. Prefiera nombres de variables descriptivos a abreviaturas

Utiliza 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 usuario = getUser();

Por qué es importante: Los nombres descriptivos de las variables facilitan la lectura, la comprensión y el mantenimiento del código. 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 aplanar la lógica.

❌ No conforme:

if (usuario) {
   si (usuario.estáActivo) {
    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é 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 tempranos hace que el código sea más fácil de razonar, mejora la cobertura de las pruebas y reduce la posibilidad de errores ocultos en casos extremos.

4. Garantizar un tratamiento coherente de los errores en todo el código base

Aplique siempre un tratamiento coherente de los errores. 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é es importante: La gestión coherente de errores facilita la depuración, evita comportamientos inesperados y garantiza la fiabilidad en toda la aplicación.

5. Evitar la codificación rígida de los valores de configuración

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

❌ No conforme:

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

✅ Conforme:

const apiUrl = proceso.entorno.API_URL;

Por qué es importante: Los valores codificados reducen la flexibilidad, hacen que el código sea menos seguro y complican el despliegue en diferentes entornos. El uso de configuraciones garantiza la mantenibilidad y la seguridad.

6. Mantener las funciones centradas en una única responsabilidad

Asegúrese de que cada función realiza una tarea única y bien definida. Evite las funciones que se encargan de múltiples responsabilidades, ya que pueden generar confusión y dificultades de 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(usuario) {
  // lógica de guardado
}

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

Por qué 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. Evite utilizar números mágicos

Sustituya los números mágicos por constantes con nombre para mejorar la claridad y la facilidad de mantenimiento del código.

❌ No conforme:

const área = longitud * 3.14159 * radio * radio;

✅ Conforme:

const PI = 3.14159;
const área = longitud * PI * radio * radio;

Por qué es importante: Los números mágicos pueden oscurecer el significado del código y hacer que 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é es importante: Las variables globales pueden crear dependencias ocultas y efectos secundarios impredecibles. Dificultan el seguimiento de la procedencia de los datos o de cómo cambian en la base de 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 literales de plantilla a la concatenación de cadenas para mejorar la legibilidad y el rendimiento.

❌ No conforme:

const mensaje = 'Hola, ' + usuario.nombre + ¡'! Tienes ' + usuario.notificaciones + ' 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 cuando se trata de cadenas complejas o contenido multilínea.

10. Aplicar una validación de entrada adecuada

Valide siempre las entradas de los usuarios para evitar que entren en el sistema datos no válidos y 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é es importante: La validación de entradas es crucial para evitar errores, garantizar la integridad de los datos y proteger frente a vulnerabilidades de seguridad como los ataques de inyección.

11. Mantener un cambio lógico por pull request

Asegúrese de que cada pull request (PR) implementa un único cambio o característica lógica; evite combinar correcciones, refactorizaciones y adiciones de características no relacionadas en un 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>

Cumple: (dif)

# 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 PR pequeños y específicos simplifican la revisión del 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 cambian archivos, módulos o dominios no relacionados en el mismo PR, algo que los linters no pueden determinar.

12. Utilizar una nomenclatura alineada con el dominio para las API y los servicios

Nombra las API, los servicios y los módulos de acuerdo con el dominio de negocio (por ejemplo, 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é es importante: La nomenclatura alineada con el dominio hace que el código sea autodocumentable, ayuda a la claridad para los nuevos colaboradores y se alinea con la lógica empresarial. Solo una IA consciente del contexto semántico (nombres de entidades, capas de servicios) puede detectar una nomenclatura mal alineada o genérica entre módulos.

13. Garantizar que las pruebas cubren los fallos y los casos extremos

Escriba pruebas no sólo para el "camino feliz", sino también para condiciones de error, casos límite y valores límite; confirme que cada módulo crítico tiene pruebas positivas y 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é es importante: La lógica crítica para el negocio a menudo se rompe cuando se omiten casos extremos, como entradas no válidas, tiempos de espera o inicios de sesión fallidos. Probar tanto las rutas de éxito como las de fracaso garantiza que la aplicación se comporte de forma fiable en condiciones reales y evita regresiones difíciles de detectar más adelante.

14. Evite mezclar capas: Los componentes de interfaz de usuario no deben realizar lógica de negocio

Mantenga los componentes de interfaz de usuario (React, front-end) libres de lógica empresarial y llamadas a bases de datos/servicios; delegue estas tareas en servicios o ganchos 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 lógica de negocio en componentes de interfaz de usuario difumina los límites entre capas y hace que los cambios futuros sean arriesgados. También obliga a los desarrolladores de front-end a entender la lógica de back-end y ralentiza la colaboración. Mantener las responsabilidades separadas garantiza que cada capa pueda evolucionar de forma independiente y reduce el riesgo de introducir errores sutiles al refactorizar.

Conclusión

Analizando 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 las aplique ninguna herramienta automatizada.

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

La revisión del código es algo más que comprobar la sintaxis. Se trata de mantener la calidad y la integridad del 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 código base.

Preguntas frecuentes

¿Tiene alguna pregunta?

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

Imponen una gestión coherente de las entradas y salidas, un comportamiento predecible de las funciones y una separación clara de las preocupaciones, lo que reduce el acoplamiento y facilita la refactorización y las pruebas de los módulos.

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

Al exigir pruebas unitarias y de integración para los casos extremos, la gestión de errores y la lógica empresarial crítica, garantizan que las regresiones se detecten pronto y que las pruebas automatizadas reflejen con precisión el comportamiento del sistema.

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

Proporcionan patrones para refactorizar módulos estrechamente acoplados u obsoletos, manteniendo la compatibilidad con versiones anteriores y minimizando el riesgo de regresiones.

¿Pueden estas normas evitar problemas de seguridad en FreeCodeCamp?

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

¿Cómo pueden beneficiarse de estas normas los nuevos contribuyentes?

La claridad en las responsabilidades de los módulos, la coherencia en la nomenclatura y la estandarización de la gestión de errores ayudan a los nuevos desarrolladores a comprender rápidamente la arquitectura y a integrarla de forma segura sin introducir regresiones.

¿Cómo se derivaron estas normas de FreeCodeCamp?

Se extrajeron del análisis de solicitudes de extracción, cambios en el código, hilos de discusión y patrones recurrentes que afectaban a la mantenibilidad, legibilidad y estabilidad en todo el código base.

Asegúrese gratis

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

No requiere tarjeta de crédito | Escanea resultados en 32segs.