Skip to content

Código Limpio

Código limpio es código que cualquier desarrollador del equipo puede leer, entender y modificar sin necesidad de explicación adicional. No es código que simplemente funciona; es código que comunica su intención claramente, que tiene responsabilidades delimitadas y que puede verificarse mediante tests.

La deuda técnica no se genera por escribir código incorrecto. Se genera por escribir código correcto pero opaco: funciones de 200 líneas, variables llamadas data o temp, lógica de negocio embebida en handlers HTTP, y errores silenciados con _ = err.

Nomenclatura

Los nombres son el primer mecanismo de documentación. Un nombre bien elegido elimina la necesidad de un comentario.

En Go

// Incorrecto — nombres opacos
func proc(d []byte, t int) ([]byte, error) { ... }
// Correcto — nombres que describen intención
func conciliarFacturasERP(datos []byte, timeoutSegundos int) ([]Factura, error) { ... }
// Incorrecto — abreviación innecesaria
type SolRepo struct { db *gorm.DB }
// Correcto
type GormSolicitudRepository struct { db *gorm.DB }
// Variables: sustantivos para datos, verbos para funciones
var solicitudesPendientes []Solicitud // correcto
var sp []Solicitud // incorrecto

Las convenciones de Go son estrictas: CamelCase para exportados, camelCase para internos. No usar snake_case. Los acrónimos en mayúsculas: RFC, UUID, HTTP, no Rfc, Uuid, Http.

En TypeScript/NestJS

// Incorrecto
const d = await this.repo.find({ where: { s: 'active' } });
// Correcto
const solicitudesActivas = await this.solicitudesRepo.findByEstatus('Activa');
// Interfaces con nombre descriptivo, sin prefijo I
interface SolicitudRepository { ... } // correcto
interface ISolicitudRepository { ... } // incorrecto — prefijo innecesario
// Tipos: PascalCase. Variables: camelCase. Constantes: UPPER_SNAKE_CASE solo para config
const MAX_REINTENTOS = 3;
type EstatusSolicitud = 'ListaParaConciliar' | 'EnProceso' | 'Conciliada';

En Dart/Flutter

// Clases: PascalCase. Variables y métodos: camelCase. Constantes: lowerCamelCase
class ConciliarSolicitudInteractor { ... } // correcto
class conciliar_solicitud_interactor { ... } // incorrecto
// Tipos abstractos: sin prefijo
abstract class SolicitudRepository { ... } // correcto
abstract class ISolicitudRepository { ... } // incorrecto
// Nombres de archivo: snake_case
// conciliar_solicitud_interactor.dart // correcto
// ConciliarSolicitudInteractor.dart // incorrecto

Funciones y métodos

Una función hace una sola cosa. Si una función tiene que hacer dos cosas, son dos funciones.

El tamaño no es la regla primaria, pero en la práctica: si una función no cabe en la pantalla, probablemente hace demasiado. El indicador concreto es el número de razones para cambiar: si la función cambiaría tanto si cambia la lógica de negocio como si cambia el protocolo de comunicación, tiene dos responsabilidades.

// Incorrecto — mezcla validación, lógica de negocio e I/O
func (h *Handler) Conciliar(w http.ResponseWriter, r *http.Request) {
var cmd ConciliarCommand
if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil {
http.Error(w, "bad request", 400)
return
}
// ... 80 líneas de lógica de negocio ...
// ... llamadas a la base de datos ...
// ... construcción de la respuesta ...
}
// Correcto — cada función tiene una responsabilidad
func (h *Handler) Conciliar(w http.ResponseWriter, r *http.Request) {
cmd, err := h.parser.DecodeConciliarCommand(r)
if err != nil {
h.responder.BadRequest(w, err)
return
}
result, err := h.useCase.Execute(cmd)
if err != nil {
h.responder.Error(w, err)
return
}
h.responder.JSON(w, http.StatusAccepted, result)
}

Manejo de errores

Los errores son información, no excepciones. Un error mal manejado oculta la causa real del problema y produce bugs silenciosos difíciles de depurar.

En Go — errores como valores

// Incorrecto — error ignorado
sol, _ := repo.FindByUUID(uuid)
// Incorrecto — error genérico sin contexto
if err != nil {
return errors.New("error")
}
// Correcto — error con contexto, tipo específico
sol, err := repo.FindByUUID(uuid)
if err != nil {
return fmt.Errorf("conciliar solicitud %s: %w", uuid, err)
}
// Errores de dominio tipados para que el caller pueda distinguirlos
type ErrSolicitudNotFound struct { UUID string }
func (e ErrSolicitudNotFound) Error() string {
return fmt.Sprintf("solicitud %s no encontrada", e.UUID)
}

En TypeScript — Either o excepciones tipadas

// Con excepciones tipadas
export class SolicitudNotFoundException extends Error {
constructor(uuid: string) {
super(`Solicitud ${uuid} no encontrada`);
this.name = 'SolicitudNotFoundException';
}
}
// Con Either (patrón funcional)
async findByUUID(uuid: string): Promise<Either<SolicitudNotFound, Solicitud>> {
const entity = await this.repo.findOneBy({ uuid });
if (!entity) return left(new SolicitudNotFound(uuid));
return right(SolicitudMapper.toDomain(entity));
}

En Dart — Either obligatorio

// Correcto — todos los errores se retornan como Left, nunca se lanzan
Future<Either<Failure, Solicitud>> findByUUID(String uuid) async {
try {
final response = await _dio.get('/solicitudes/$uuid');
return Right(SolicitudMapper.fromJson(response.data));
} on DioException catch (e) {
if (e.response?.statusCode == 404) {
return Left(SolicitudNotFoundFailure(uuid));
}
return Left(ServerFailure(e.message));
}
}

Comentarios

Los comentarios no compensan el código opaco. Si un comentario explica qué hace el código, el código necesita ser renombrado o refactorizado. Los comentarios válidos explican el por qué, no el qué.

// Incorrecto — el código ya dice qué hace
// Itera sobre las facturas y las agrega al índice
for _, f := range facturas {
index[f.UUID] = f
}
// Correcto — explica por qué se toma esta decisión
// El SAT usa UUID como clave de deduplicación.
// Usar map en lugar de slice reduce la complejidad de búsqueda de O(n) a O(1)
// en la etapa de cruce, donde el dataset puede superar 50,000 facturas.
index := make(map[string]Factura, len(facturas))
for _, f := range facturas {
index[f.UUID] = f
}

Estructura de paquetes y módulos

El código limpio no es solo código limpio en funciones individuales: es código limpio a nivel de organización de paquetes y módulos.

La regla en Go: los paquetes son sustantivos (solicitud, conciliacion, empresa), no verbos ni categorías genéricas (utils, helpers, common). Un paquete utils con 40 funciones no relacionadas es el equivalente a un cajón de sastre que viola el SRP a nivel de módulo.

// Incorrecto
pkg/
utils/
string_utils.go
date_utils.go
error_utils.go
// Correcto — cada paquete tiene cohesión semántica
domain/
shared/
uuid.go // Generación y validación de UUIDs
periodo.go // Cálculos de período fiscal
errors.go // Tipos base de error de dominio

Resumen

  • Los nombres son la primera forma de documentación. Un nombre opaco requiere un comentario; un nombre claro no.
  • Las funciones hacen una sola cosa. El indicador es el número de razones para cambiar, no las líneas de código.
  • Los errores son valores con contexto y tipo. Ignorar errores con _ = err o silenciarlos sin log es deuda técnica.
  • Los comentarios explican el por qué, no el qué. Si explican el qué, el código necesita refactoring.
  • La organización en paquetes y módulos sigue las mismas reglas: cohesión semántica, sin cajones de sastre tipo utils.

alt text

No se debe malinterpretar el concepto de código limpio como un código que simplemente funciona o se ve “bonito” a simple vista. El código limpio es una idea que siempre debe quedar grabada en la cabeza, y es visualizar a futuro el mantenimiento del código, la escalabilidad del mismo y la facilidad de lectura para otros desarrolladores. No caer en la deuda técnica, no caer en la trampa de la arrogancia, no caer en la trampa de la ignorancia. Olvidar la pasión del momento por dejar listo un feature y no analizar el codigo que se esta escribiendo.

Asi que aqui hablaremos de mucha teoria, pero tambien de mucha practica, porque no se puede hablar de codigo limpio sin practicar el codigo limpio.

Grabe en su cabeza que estos son principios, sugerencias, no reglas. No se sienta atado a ellos, pero si sienta la responsabilidad de seguirlos, porque el codigo limpio es un trabajo en equipo, es un trabajo en comunidad, es un trabajo en futuro.

Principios de código limpio

1. Mantener el código simple

El código debe ser simple y fácil de entender. No debe ser complicado ni confuso. Debe ser fácil de leer y fácil de mantener. Debe ser fácil de modificar y fácil de extender. Debe ser fácil de probar y fácil de depurar. Debe ser fácil de refactorizar y fácil de reutilizar.

2. Mantener el código limpio

El código debe ser limpio y ordenado. Debe seguir las convenciones de codificación establecidas por la comunidad de desarrollo de software. Debe ser consistente y uniforme. Debe ser fácil de leer y fácil de entender. Debe ser fácil de mantener y fácil de colaborar.

3. Mantener el código eficiente

El código debe ser eficiente y rápido. No debe ser lento ni ineficiente. Debe ser optimizado y escalable. Debe ser eficaz y productivo. Debe ser eficiente y rentable.

4. Mantener el código seguro

El código debe ser seguro y confiable. No debe ser vulnerable ni inseguro. Debe ser protegido y privado.

5. Mantener el código documentado

El código debe ser documentado y comentado. No debe ser oscuro ni misterioso. Debe ser claro y explicativo.

6. Mantener el código probado

El código debe ser probado y verificado. No debe ser defectuoso ni incorrecto. Debe ser preciso y exacto.

7. Mantener el código versionado

El código debe ser versionado y controlado. No debe ser desorganizado ni caótico. Debe ser ordenado y organizado.

8. Mantener el código colaborativo

El código debe ser colaborativo y compartido. No debe ser egoísta ni exclusivo. Debe ser inclusivo y participativo.

9. Mantener el código sostenible

El código debe ser sostenible y mantenible. No debe ser obsoleto ni desactualizado. Debe ser actualizado y modernizado.

10. Mantener el código ético

El código debe ser ético y responsable. No debe ser inmoral ni ilegal.

Ventajas

Son muchas las ventajas de los principios del código limpio, y debe tener en cuenta que estos principios no son reglas, son sugerencias, son guias, son consejos, son herramientas, son poderosas herramientas que le permitiran a usted y a su equipo de desarrollo de software crear sistemas de software de alta calidad.

  • Facilidad de lectura: Un código limpio es fácil de leer y fácil de entender. Esto facilita la colaboración entre los miembros del equipo de desarrollo de software y permite que los desarrolladores se centren en la resolución de problemas en lugar de en la comprensión del código.
  • Facilidad de mantenimiento: Un código limpio es fácil de mantener y fácil de modificar. Esto facilita la corrección de errores, la adición de nuevas funcionalidades y la refactorización del código.
  • Facilidad de depuración: Un código limpio es fácil de depurar y fácil de probar. Esto facilita la identificación y corrección de errores, y permite que los desarrolladores prueben el código de forma rápida y eficiente.
  • Facilidad de colaboración: Un código limpio es fácil de colaborar y fácil de compartir. Esto facilita la colaboración entre los miembros del equipo de desarrollo de software y permite que los desarrolladores trabajen juntos de forma eficiente.
  • Facilidad de escalabilidad: Un código limpio es fácil de escalar y fácil de extender. Esto facilita la adición de nuevas funcionalidades y la adaptación del código a los cambios en los requisitos del sistema.
  • Facilidad de reutilización: Un código limpio es fácil de reutilizar y fácil de compartir. Esto facilita la reutilización de código y permite que los desarrolladores compartan soluciones a problemas comunes.

Desventajas

Aunque se pueda pensar que el código limpio es una panacea, no es así. Existen desventajas en el código limpio, y es importante tenerlas en cuenta para evitar caer en la trampa de la arrogancia y la ignorancia.

  • Complejidad: Un código limpio puede ser complejo y difícil de entender. Esto puede dificultar la colaboración entre los miembros del equipo de desarrollo de software y dificultar la resolución de problemas.
  • Costo: Un código limpio puede ser costoso y consumir recursos. Esto puede dificultar la adopción de los principios del código limpio y dificultar la implementación de las mejores prácticas de programación.
  • Tiempo: Un código limpio puede llevar tiempo y esfuerzo. Esto puede dificultar la adopción de los principios del código limpio y dificultar la implementación de las mejores prácticas de programación.
  • Flexibilidad: Un código limpio puede ser inflexible y rígido. Esto puede dificultar la adaptación del código a los cambios en los requisitos del sistema y dificultar la extensión del código.

Referencias