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 opacosfunc proc(d []byte, t int) ([]byte, error) { ... }
// Correcto — nombres que describen intenciónfunc conciliarFacturasERP(datos []byte, timeoutSegundos int) ([]Factura, error) { ... }
// Incorrecto — abreviación innecesariatype SolRepo struct { db *gorm.DB }
// Correctotype GormSolicitudRepository struct { db *gorm.DB }
// Variables: sustantivos para datos, verbos para funcionesvar solicitudesPendientes []Solicitud // correctovar sp []Solicitud // incorrectoLas 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
// Incorrectoconst d = await this.repo.find({ where: { s: 'active' } });
// Correctoconst solicitudesActivas = await this.solicitudesRepo.findByEstatus('Activa');
// Interfaces con nombre descriptivo, sin prefijo Iinterface SolicitudRepository { ... } // correctointerface ISolicitudRepository { ... } // incorrecto — prefijo innecesario
// Tipos: PascalCase. Variables: camelCase. Constantes: UPPER_SNAKE_CASE solo para configconst MAX_REINTENTOS = 3;type EstatusSolicitud = 'ListaParaConciliar' | 'EnProceso' | 'Conciliada';En Dart/Flutter
// Clases: PascalCase. Variables y métodos: camelCase. Constantes: lowerCamelCaseclass ConciliarSolicitudInteractor { ... } // correctoclass conciliar_solicitud_interactor { ... } // incorrecto
// Tipos abstractos: sin prefijoabstract class SolicitudRepository { ... } // correctoabstract class ISolicitudRepository { ... } // incorrecto
// Nombres de archivo: snake_case// conciliar_solicitud_interactor.dart // correcto// ConciliarSolicitudInteractor.dart // incorrectoFunciones 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/Ofunc (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 responsabilidadfunc (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 ignoradosol, _ := repo.FindByUUID(uuid)
// Incorrecto — error genérico sin contextoif err != nil { return errors.New("error")}
// Correcto — error con contexto, tipo específicosol, 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 distinguirlostype 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 tipadasexport 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 lanzanFuture<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 índicefor _, 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.
// Incorrectopkg/ utils/ string_utils.go date_utils.go error_utils.go
// Correcto — cada paquete tiene cohesión semánticadomain/ 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 dominioResumen
- 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
_ = erro 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.

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
- Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin
- The Clean Coder: A Code of Conduct for Professional Programmers by Robert C. Martin
- Clean Architecture: A Craftsman’s Guide to Software Structure and Design by Robert C. Martin
- Refactoring: Improving the Design of Existing Code by Martin Fowler