Codigo Limpio
El codigo limpio es codigo que es facil de leer, entender y modificar. Un buen programador no solo escribe codigo que funciona, sino codigo que otros (y tu mismo en el futuro) pueden mantener facilmente.
Nombres significativos
Los nombres son la forma mas importante de comunicar la intencion de tu codigo. Un buen nombre elimina la necesidad de comentarios.
Variables descriptivas
<?php
declare(strict_types=1);
// MAL: nombres crípticos
$d = 30;
$u = getUsuarios();
$t = 0;
foreach ($u as $x) {
if ($x['a'] > $d) {
$t++;
}
}
// BIEN: nombres que revelan intencion
$diasDeInactividad = 30;
$usuarios = getUsuarios();
$usuariosInactivos = 0;
foreach ($usuarios as $usuario) {
if ($usuario['diasSinLogin'] > $diasDeInactividad) {
$usuariosInactivos++;
}
}
Funciones con nombres de accion
<?php
declare(strict_types=1);
// MAL: nombres vagos
function procesar(array $datos): void { }
function hacer(string $cosa): void { }
function data(): array { }
// BIEN: verbos que describen la accion
function enviarEmailBienvenida(Usuario $usuario): void { }
function calcularDescuento(Pedido $pedido): float { }
function obtenerUsuariosActivos(): array { }
Booleanos como preguntas
<?php
declare(strict_types=1);
// MAL
$flag = true;
$status = false;
// BIEN: se leen como preguntas
$estaActivo = true;
$tienePermiso = false;
$puedeEditar = $usuario->esAdmin();
// En metodos
class Usuario
{
public function esAdmin(): bool { }
public function tieneAcceso(string $recurso): bool { }
public function puedePublicar(): bool { }
}
Funciones pequenas y enfocadas
Una funcion debe hacer una sola cosa y hacerla bien. Si tu funcion hace varias cosas, dividela en funciones más pequeñas.
<?php
declare(strict_types=1);
// MAL: funcion que hace demasiadas cosas
function procesarPedido(array $datos): void
{
// Validar datos
if (empty($datos['email']) || !filter_var($datos['email'], FILTER_VALIDATE_EMAIL)) {
throw new Exception('Email invalido');
}
if (empty($datos['productos'])) {
throw new Exception('No hay productos');
}
// Calcular total
$total = 0;
foreach ($datos['productos'] as $producto) {
$total += $producto['precio'] * $producto['cantidad'];
}
// Aplicar descuento
if ($total > 100) {
$total *= 0.9;
}
// Guardar en base de datos
$db = new PDO('...');
$stmt = $db->prepare('INSERT INTO pedidos...');
$stmt->execute([...]);
// Enviar email
mail($datos['email'], 'Pedido confirmado', '...');
}
<?php
declare(strict_types=1);
// BIEN: funciones pequenas y enfocadas
class ProcesadorPedidos
{
public function procesar(array $datos): void
{
$this->validarDatos($datos);
$total = $this->calcularTotal($datos['productos']);
$totalConDescuento = $this->aplicarDescuento($total);
$this->guardarPedido($datos, $totalConDescuento);
$this->enviarConfirmacion($datos['email']);
}
private function validarDatos(array $datos): void
{
if (!$this->esEmailValido($datos['email'] ?? '')) {
throw new InvalidArgumentException('Email invalido');
}
if (empty($datos['productos'])) {
throw new InvalidArgumentException('No hay productos');
}
}
private function esEmailValido(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
private function calcularTotal(array $productos): float
{
return array_reduce(
$productos,
fn(float $total, array $p) => $total + ($p['precio'] * $p['cantidad']),
0.0
);
}
private function aplicarDescuento(float $total): float
{
return $total > 100 ? $total * 0.9 : $total;
}
private function guardarPedido(array $datos, float $total): void { }
private function enviarConfirmacion(string $email): void { }
}
Evitar la anidacion excesiva
El codigo muy anidado es dificil de seguir. Usa early returns para reducir niveles de indentacion.
<?php
declare(strict_types=1);
// MAL: piramide de la muerte
function procesarUsuario(?array $usuario): string
{
if ($usuario !== null) {
if (isset($usuario['activo']) && $usuario['activo']) {
if (isset($usuario['email'])) {
if (filter_var($usuario['email'], FILTER_VALIDATE_EMAIL)) {
return "Usuario valido: {$usuario['email']}";
} else {
return 'Email invalido';
}
} else {
return 'Sin email';
}
} else {
return 'Usuario inactivo';
}
} else {
return 'Usuario no encontrado';
}
}
// BIEN: early returns (clausulas de guarda)
function procesarUsuario(?array $usuario): string
{
if ($usuario === null) {
return 'Usuario no encontrado';
}
if (!($usuario['activo'] ?? false)) {
return 'Usuario inactivo';
}
if (!isset($usuario['email'])) {
return 'Sin email';
}
if (!filter_var($usuario['email'], FILTER_VALIDATE_EMAIL)) {
return 'Email invalido';
}
return "Usuario valido: {$usuario['email']}";
}
Evitar números mágicos
Los numeros sin contexto en el codigo son dificiles de entender. Usa constantes con nombres descriptivos.
<?php
declare(strict_types=1);
// MAL: que significa 86400? y 3? y 0.21?
if (time() - $ultimoAcceso > 86400) {
bloquearCuenta();
}
if ($intentosFallidos > 3) {
bloquearCuenta();
}
$precioFinal = $precio * 1.21;
// BIEN: constantes descriptivas
class Configuracion
{
public const SEGUNDOS_POR_DIA = 86400;
public const MAX_INTENTOS_LOGIN = 3;
public const IVA = 0.21;
}
if (time() - $ultimoAcceso > Configuracion::SEGUNDOS_POR_DIA) {
bloquearCuenta();
}
if ($intentosFallidos > Configuracion::MAX_INTENTOS_LOGIN) {
bloquearCuenta();
}
$precioFinal = $precio * (1 + Configuracion::IVA);
Comentarios: cuando y como
El mejor comentario es el codigo que no necesita comentarios. Usa comentarios solo cuando el código no puede explicarse por si mismo.
<?php
declare(strict_types=1);
// MAL: comentarios obvios o redundantes
// Incrementar contador
$contador++;
// Obtener usuarios de la base de datos
$usuarios = $this->obtenerUsuarios();
// Si el usuario es admin
if ($usuario->esAdmin()) {
// Dar acceso total
$usuario->darAccesoTotal();
}
// BIEN: sin comentarios innecesarios
$contador++;
$usuarios = $this->obtenerUsuarios();
if ($usuario->esAdmin()) {
$usuario->darAccesoTotal();
}
Comentarios utiles son aquellos que explican el por que, no el que:
<?php
declare(strict_types=1);
// BIEN: explica decisiones de negocio o tecnicas no obvias
// Limite impuesto por la API de pagos externa
public const MAX_MONTO_TRANSACCION = 10000;
// Usamos sleep porque la API tiene rate limiting de 1 req/seg
sleep(1);
// Formato requerido por el sistema legacy de contabilidad
$fecha = $pedido->fecha->format('Ymd');
// TODO: Refactorizar cuando migremos a PostgreSQL
$query = "SELECT * FROM usuarios WHERE BINARY nombre = ?";
DRY: No te repitas
Si ves codigo duplicado, probablemente puedas extraerlo en una funcion o clase reutilizable.
<?php
declare(strict_types=1);
// MAL: logica duplicada
function crearUsuario(array $datos): void
{
if (empty($datos['email']) || !filter_var($datos['email'], FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email invalido');
}
// ...
}
function actualizarEmail(int $id, string $email): void
{
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email invalido');
}
// ...
}
// BIEN: logica extraida
function validarEmail(string $email): void
{
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email invalido');
}
}
function crearUsuario(array $datos): void
{
validarEmail($datos['email'] ?? '');
// ...
}
function actualizarEmail(int $id, string $email): void
{
validarEmail($email);
// ...
}
Ejercicios
Ejercicio 1: Refactorizar nombres
Mejora los nombres en el siguiente código:
<?php
declare(strict_types=1);
function calc(array $arr): float
{
$t = 0;
foreach ($arr as $i) {
$t += $i['p'] * $i['q'];
}
if ($t > 100) {
$t = $t * 0.9;
}
return $t;
}
Ver solucion
<?php
declare(strict_types=1);
function calcularTotalCarrito(array $productos): float
{
$total = 0.0;
foreach ($productos as $producto) {
$total += $producto['precio'] * $producto['cantidad'];
}
$minimoParaDescuento = 100;
$porcentajeDescuento = 0.9;
if ($total > $minimoParaDescuento) {
$total *= $porcentajeDescuento;
}
return $total;
}
Ejercicio 2: Eliminar anidacion
Refactoriza este código usando early returns:
<?php
declare(strict_types=1);
function puedeComprar(array $usuario, array $producto): bool
{
if ($usuario['activo']) {
if ($usuario['verificado']) {
if ($producto['stock'] > 0) {
if ($usuario['saldo'] >= $producto['precio']) {
return true;
}
}
}
}
return false;
}
Ver solucion
<?php
declare(strict_types=1);
function puedeComprar(array $usuario, array $producto): bool
{
if (!$usuario['activo']) {
return false;
}
if (!$usuario['verificado']) {
return false;
}
if ($producto['stock'] <= 0) {
return false;
}
if ($usuario['saldo'] < $producto['precio']) {
return false;
}
return true;
}
Ejercicio 3: Extraer funcion
Divide esta función larga en funciones más pequeñas:
<?php
declare(strict_types=1);
function registrarUsuario(string $nombre, string $email, string $password): array
{
// Validar nombre
$nombre = trim($nombre);
if (strlen($nombre) < 2) {
throw new InvalidArgumentException('Nombre muy corto');
}
// Validar email
$email = strtolower(trim($email));
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email invalido');
}
// Validar password
if (strlen($password) < 8) {
throw new InvalidArgumentException('Password muy corta');
}
// Crear usuario
return [
'nombre' => $nombre,
'email' => $email,
'password' => password_hash($password, PASSWORD_DEFAULT),
'creado' => date('Y-m-d H:i:s'),
];
}
Ver solucion
<?php
declare(strict_types=1);
function validarNombre(string $nombre): string
{
$nombre = trim($nombre);
if (strlen($nombre) < 2) {
throw new InvalidArgumentException('Nombre muy corto');
}
return $nombre;
}
function validarEmail(string $email): string
{
$email = strtolower(trim($email));
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email invalido');
}
return $email;
}
function validarPassword(string $password): void
{
if (strlen($password) < 8) {
throw new InvalidArgumentException('Password muy corta');
}
}
function registrarUsuario(string $nombre, string $email, string $password): array
{
$nombreValidado = validarNombre($nombre);
$emailValidado = validarEmail($email);
validarPassword($password);
return [
'nombre' => $nombreValidado,
'email' => $emailValidado,
'password' => password_hash($password, PASSWORD_DEFAULT),
'creado' => date('Y-m-d H:i:s'),
];
}
Has encontrado un error o tienes una sugerencia para mejorar esta leccion?
EscribenosTe está gustando el curso?
Tenemos cursos premium con proyectos reales y soporte personalizado.
Descubrir cursos premium