25 oct 2025·5 min de lectura

Separar un archivo utils con límites de módulo claros e iterativos

Divide un archivo utils sin reescritura riesgosa extrayendo pequeños módulos paso a paso, estableciendo límites claros y manteniendo los cambios seguros para su despliegue.

Separar un archivo utils con límites de módulo claros e iterativos

Por qué un solo archivo utils se convierte en un problema

Un único archivo utils suele empezar con buenas intenciones: poner pequeños helpers en un solo lugar. Luego llegan los plazos, cambia el equipo y cualquier cosa sin un hogar obvio termina en utils. Con el tiempo, “dividir un archivo utils” deja de ser una tarea de limpieza agradable y se convierte en un bloqueo para cambios seguros.

El problema central es la propiedad. Cuando todo vive junto, nada se siente propiedad de nadie. La gente añade “solo un helper más” sin pensar en el nombre, las dependencias o si pertenece a una parte específica de la app. Pronto, un cambio que debería ser simple se vuelve un juego de adivinanzas: ¿quién usa esto y qué se romperá si lo toco?

Los síntomas típicos aparecen rápido:

  • Responsabilidades no relacionadas mezcladas (formateo, llamadas API, auth, base de datos, ajustes de UI)
  • Imports circulares porque muchas partes de la app dependen del mismo archivo
  • Funciones “pequeñas” con efectos secundarios ocultos (leer vars de entorno, escribir en storage, mutar estado global)
  • Tests difíciles de escribir porque importar un helper arrastra media app
  • Lógica sensible a seguridad dispersa en helpers aleatorios (parseo de tokens, reglas de contraseña, manejo de secretos)

El objetivo no es la estética. Es permitir cambios más seguros, pruebas más fáciles y límites claros para que puedas actualizar una zona sin sorprender a otra.

Establece expectativas desde el inicio: esto es una limpieza iterativa, no una reescritura. Estás moviendo código en pasos pequeños manteniendo el comportamiento igual.

Cómo saber si tu archivo utils se ha convertido en un cajón de sastre

Un archivo utils se vuelve un cajón de sastre cuando silenciosamente se convierte en una dependencia para todo lo demás. La mayor señal de advertencia no es el número de líneas. Es que un cambio en un “helper” puede romper la mitad de la app.

Atento a estas señales:

  • Mezcla trabajos no relacionados como checks de auth, queries de base de datos, formateo y llamadas de red.
  • Los helpers importan módulos específicos de la app (models, routes, controllers) en lugar de permanecer genéricos.
  • Las funciones tienen efectos secundarios pero suenan inofensivas por el nombre.
  • Editas utils para lanzar features que no tienen nada que ver con utilidades.
  • Causa fricción constante: conflictos de merge y sorpresas de “¿por qué esto rompió aquello?”.

El tamaño es solo un síntoma. El acoplamiento es el verdadero problema. Cuando un archivo conoce reglas de auth, el esquema de datos y el cliente HTTP, las dependencias ocultas se acumulan. Cada pequeño cambio exige pruebas más amplias.

Helpers puros como capitalize(), clamp() o toSlug() está bien mantenerlos como utilidades. Si una función depende de la base de datos, del estado de auth o de secretos, no es una utilidad. Es un módulo esperando a existir.

Elige límites de módulo claros antes de mover código

Un límite de módulo es una promesa simple: todo lo que está aquí trata sobre un tipo de trabajo, con entradas y salidas predecibles. No necesitas un diseño perfecto. Necesitas que el siguiente movimiento sea obvio y seguro.

Comienza con categorías que la gente ya entiende. Para muchas apps, estos cubos cubren la mayor parte del desorden:

  • strings
  • dates
  • validation
  • api
  • auth

El límite más importante es entre helpers puros y helpers con efectos secundarios.

Los helpers puros son predecibles: misma entrada, misma salida, sin efectos externos. Son los más fáciles de mover primero porque puedes probarlos con ejemplos simples.

Los helpers con efectos secundarios tocan el mundo exterior: variables de entorno, local storage, cookies, tiempo, aleatoriedad, llamadas de red, bases de datos y estado global de la app. También pueden hacer logging, mutar objetos o lanzar excepciones de formas de las que otro código depende. Trátalos como de mayor riesgo y mantenlos agrupados por responsabilidad (por ejemplo, no mezcles auth y comportamiento del cliente API en el mismo módulo).

Mantén la nomenclatura consistente, aunque sea aburrida. Decide pronto si quieres carpetas o archivos planos, y adhiérete a una convención simple para que los revisores detecten módulos “misceláneos” antes de que crezcan.

Haz un mapa rápido de lo que hay adentro (sin sobreplanear)

Antes de dividir un archivo utils, obtén una imagen rápida y honesta de lo que contiene. No necesitas documentación perfecta. Necesitas suficiente claridad para mover código sin adivinar.

Comienza con un inventario de exports. Lista cada función exportada y dónde se importa. Si un helper se usa en 30 lugares, necesita cuidado extra. Si se usa una sola vez, es un gran candidato temprano.

Un inventario ligero suele ser suficiente:

  • Nombre de la función y una descripción de una línea
  • Dónde se importa (unos pocos llamadores principales)
  • Efectos secundarios (env, storage, logging, red)
  • Dónde debería vivir (date, auth, formatting, db)
  • Nivel de riesgo: bajo (puro), medio (config), alto (con estado o seguridad)

Una vez que puedas ver la lista, busca un primer clúster de extracción: funciones puras que no leen estado global y no hacen I/O. Mover esas primero te da victorias rápidas y un patrón limpio que repetir.

Paso a paso: extraer módulos de forma iterativa y con bajo riesgo

Desenreda los helpers de auth
Deja de adivinar qué se rompe: aislamos de forma segura los helpers de token, sesión y almacenamiento.

La forma más segura de dividir un archivo utils es avanzar en rebanadas pequeñas que sean fáciles de revisar y revertir.

Elige un tema (dates, formatting, validation, helpers de auth) y trátalo como piloto. Buscas una rutina repetible, no una arquitectura perfecta.

Un bucle de extracción de bajo riesgo

  • Crea un nuevo archivo de módulo para el tema que elegiste.
  • Mueve solo 1 a 3 funciones estrechamente relacionadas. Si algo se siente medio relacionado, déjalo para después.
  • Mantén los exports estables reexportando temporalmente las funciones movidas desde el archivo utils original.
  • Actualiza imports en un área pequeña de la app (una página, una feature, un servicio). Haz commit.
  • Elimina las reexportaciones temporales solo después de que la mayoría de los imports se hayan migrado.

Esto mantiene cada commit pequeño: mueve unas pocas funciones, actualiza unos pocos imports y para.

Ejemplo concreto

Si utils.ts incluye formatMoney, parseCurrency, roundToCents, además de helpers no relacionados como slugify, sleep y fetchWithRetry, empieza por money. Crea utils/money.ts, mueve las funciones de dinero y reexportalas desde utils.ts para que nada se rompa. Luego migra imports en el flujo de checkout primero y expande desde allí.

Mantén el comportamiento igual mientras refactorizas

El mayor riesgo no es mover código. Es el “ya que estoy” que cambia silenciosamente los resultados. Trátalo como mover cajas a una habitación nueva: etiqueta, carga, coloca. No redecores a mitad del traslado.

Empieza con funciones puras. Antes de mover una, escribe una prueba pequeña que fije el comportamiento actual, incluso si es extraño. Conocido-pero-raro vence a limpio-pero-diferente.

Los helpers de formateo son un buen ajuste para tablas de entrada-salida simples. Elige un puñado de casos representativos, incluyendo casos límite como valores vacíos, espacios extra y caracteres no ASCII.

Si no tienes infraestructura de tests, usa verificaciones rápidas en tiempo de ejecución en lugar de adivinar. Una aserción temporal o un pequeño log alrededor de un punto de llamada puede confirmar que las salidas no cambiaron después del movimiento. Elíminalo cuando estés confiado.

Un error común: “mejorar” formatPrice(amount) mientras lo mueves. Si ese helper afecta facturas o correos, un cambio de redondeo o símbolo puede crear totales desajustados o confusión en clientes. Congela la salida, muévelo y programa las mejoras como un cambio separado y explícito.

Maneja con cuidado los helpers con efectos secundarios y sensibles a seguridad

Las partes más riesgosas de un archivo utils rara vez son los helpers de strings. Son las funciones que tocan el mundo exterior: llamadas de red, storage, bases de datos, tiempo, IDs aleatorios y variables de entorno.

Mantén los helpers puros separados del código con efectos secundarios. Mezclarlos es la forma en que obtienes bugs como llamadas API duplicadas, cabeceras faltantes o datos guardados en el lugar equivocado.

Coloca el código con efectos secundarios en módulos con nombres obvios. Por ejemplo: auth (lectura/escritura de tokens, refresh, logout), configuración del cliente API (base URL, reintentos, headers), wrappers de storage y acceso a env.

Para mantener a los llamadores estables mientras mueves código, introduce interfaces pequeñas. En lugar de llamar localStorage.getItem('token') por todas partes, crea un tokenStore con getToken() y setToken(). Más adelante puedes cambiar cómo se almacenan los tokens sin editar media app.

Trata estos helpers como de alto riesgo incluso si parecen pequeños:

  • Manejo de tokens (parseo de JWT, refresh, checks de expiración)
  • Secretos (API keys, tokens de servicio, URLs privadas)
  • Lógica de contraseñas (hashing, comparaciones, flujos de reset)
  • Sanitización de input y construcción de queries

Si dudas sobre una extracción, deja la función antigua como un wrapper fino que llame al nuevo módulo y elimínalo solo después de haberlo enviado con seguridad.

Trampas comunes que convierten un refactor en una reescritura

Obtén un plan práctico de reparación
Explícanos qué se rompe y proponemos el plan más pequeño y seguro para arreglarlo.

Los refactors explotan cuando los cambios son demasiado grandes para razonar. El objetivo son extracciones pequeñas en las que puedas confiar.

Patrones de fallo comunes:

  • Movimientos tipo big-bang: mover docenas de helpers a la vez mata tu capacidad de aislar fallos.
  • Mezclar limpieza con cambios de comportamiento: renombrar y tocar lógica en el mismo commit oculta regresiones.
  • Crear un nuevo depósito de desechos: shared/ o common/ demasiado pronto suele convertirse en utils v2.
  • Romper imports sin plan de migración: prefiere un periodo puente con reexportaciones o alias.
  • Dependencias ocultas: helpers que leen vars de entorno o singletons globales pueden romper por cambio en el orden de inicialización.

Un ejemplo simple: formatDate() parece inofensivo, pero depende de una configuración global de locale y de una env var de timezone. Después de moverlo, los tests locales pasan pero en producción hay otra env var, y ahora los recibos muestran el día equivocado.

Dos guardarraíles ayudan: mantén cada extracción lo bastante pequeña para revisarse en minutos, y mantén el comportamiento idéntico hasta que el límite sea estable.

Comprobaciones rápidas antes de enviar cada extracción

Trata cada extracción como un pequeño release.

Asegúrate de que el nuevo módulo tenga un trabajo claro y una superficie pequeña. Si no puedes describir lo que hace en una frase, aún hace demasiado. También sé deliberado sobre los exports. Muchos helpers eran “públicos por accidente” cuando vivían en un archivo grande.

Una lista de verificación previa al merge corta:

  • Propósito claro: nombre y exports coinciden con un dominio
  • Exports intencionales: exporta solo lo que los llamadores necesitan
  • No hay imports circulares
  • No hay secretos incluidos en bundles cliente
  • La build pasa sin warnings que sugieran rutas duplicadas o imports muertos

Luego haz una prueba de humo de uso real y pequeña. Incluso con tests unitarios, vale la pena verificar que tu flujo central siga funcionando (auth, la acción principal de crear/guardar y una página que haga una llamada API y renderice datos reales).

Ejemplo: dividir un archivo utils mixto en una app real

Divide utils de forma segura
Diagnosticamos importaciones circulares y refactorizamos hacia módulos claros sin reescribir todo.

Una startup tiene un solo utils.ts que empezó pequeño y creció hasta 900 líneas. Contiene helpers de auth (almacenamiento de tokens, checks de sesión), llamadas API (fetchWithAuth) y formateo de UI (fechas, moneda, nombres para mostrar). Aparecen bugs en lugares extraños porque todo importa de todo.

Mantienen el trabajo de features en marcha extrayendo pequeños módulos en un orden seguro:

  • Mover primero helpers puros de formateo.
  • Extraer luego el wrapper del cliente API y mantener los exports antiguos como wrappers finos.
  • Separar auth en módulos separados de token y sesión, aislando todo lo que toca storage.
  • Terminar con una pasada de limpieza: eliminar reexports, renombrar funciones poco claras y eliminar helpers muertos que puedes demostrar que no se usan.

Lo que mejora rápido es práctico: menos regresiones, propiedad más clara (cambios de auth no afectan al formateo), revisiones de seguridad más fáciles y onboarding más rápido porque los archivos coinciden con conceptos reales de la app.

Siguientes pasos: convertir el plan en progreso constante

La forma más rápida de dividir un archivo utils es tratarlo como una serie de movimientos pequeños y seguros.

Un plan simple de una semana:

  • Día 1: Elige un límite (date/time, strings, money, validation) y mueve 3–5 funciones puras. Añade tests rápidos.
  • Día 2: Mueve las siguientes 3–5 dentro del mismo límite.
  • Día 3: Extrae un módulo con efectos secundarios (storage, cookies, wrappers de fetch). Mantén wrappers finos.
  • Día 4: Migra imports en un puñado de archivos. Añade una regla simple para desalentar nuevos imports desde el viejo catch-all.
  • Día 5: Limpieza: renombra módulos, añade comentarios cortos y documenta qué sigue viviendo en el archivo viejo.

Para cuando pares, el archivo utils restante debería ser en su mayoría wrappers de compatibilidad y glue realmente compartida, no el lugar donde todo acaba.

Si heredaste una app generada por IA y utils parece poseer todo el producto, FixMyMess (fixmymess.ai) puede ayudar diagnosticando la base de código, aislando helpers riesgosos (auth, secretos, builders de queries propensos a inyección) y convirtiendo la separación en una secuencia segura y desplegable en lugar de una reescritura.

Preguntas Frecuentes

¿Cómo sé si mi archivo utils es realmente un problema y no solo “grande”?

Si el archivo mezcla trabajos no relacionados y un pequeño cambio rompe muchas funciones, ya es un catch-all. La señal real es el acoplamiento: los helpers importan módulos específicos de la app, tienen efectos secundarios ocultos o se usan en todas partes.

El número de líneas importa menos que cuántas partes de la app dependen de él.

¿Qué límites de módulo debo elegir antes de empezar a mover código?

Apunta a cubos simples de “una sola responsabilidad” que coincidan con cómo la gente piensa sobre la app, como strings, dates, validation, api y auth. Mantén los helpers puros (sin I/O ni estado global) separados de los que tienen efectos secundarios (storage, env, network, database).

Si no puedes describir un módulo en una frase, el límite todavía es demasiado borroso.

¿Qué debería mover fuera de utils primero?

Empieza con helpers puros y de bajo riesgo que sean fáciles de probar y no lean estado global. También prioriza helpers con pocos puntos de llamada, porque puedes migrarlos rápidamente.

Deja para después las piezas de alto riesgo como auth, manejo de tokens, acceso a env y construcción de queries hasta que tengas un patrón de extracción seguro.

¿Cómo refactorizo sin cambiar accidentalmente el comportamiento?

No cambies el comportamiento mientras mueves código. Escribe una pequeña prueba (o una verificación en tiempo de ejecución) que fije la salida actual, aunque sea un poco rara.

Trata el movimiento como trasladar cajas: primero mover, luego mejorar en un cambio separado y explícito.

¿Cuál es la forma más segura paso a paso para dividir un archivo utils?

Crea el nuevo módulo, mueve 1–3 funciones relacionadas y mantén los viejos exports funcionando reexportando temporalmente desde el archivo utils original. Luego migra imports en una parte pequeña de la app y haz commit.

Cuando la mayoría de los puntos de llamada estén actualizados, elimina las reexportaciones temporales y repite con el siguiente clúster.

¿Cómo evito importaciones circulares cuando extraigo módulos?

Las importaciones circulares suelen significar que el archivo “utils” no es realmente utilidades; está haciendo trabajo de dominio real y depende de internas de la app. Separa por responsabilidad y mantén las dependencias en una sola dirección.

Si hace falta, crea un módulo de bajo nivel (helpers puros) que los módulos de más alto nivel puedan importar, en lugar de que todos importen de todos.

¿Qué funciones de utils son las más sensibles en términos de seguridad?

Cualquier cosa que toque tokens, secretos, contraseñas, env vars, almacenamiento o construcción de queries debe considerarse de alto riesgo. Mueve eso a módulos claramente nombrados (como auth, env, storage, db) en lugar de dejarlo disperso entre helpers inocentes.

También asegúrate de que el código que lee secretos no termine empacado en bundles cliente por accidente.

¿Debo encapsular localStorage, cookies y acceso a env?

Sí, pero hazlo de forma intencional. Crea un wrapper pequeño como tokenStore.getToken() y tokenStore.setToken() para que el resto de la app no llame a localStorage o cookies directamente.

Eso facilita cambios futuros y reduce la posibilidad de un manejo inconsistente de tokens en la app.

¿Cómo migro imports sin romper todo?

Prefiere un periodo puente corto: mantiene los imports viejos funcionando vía reexportaciones o wrappers finos mientras migras call sites gradualmente. Mantén cada extracción lo bastante pequeña para que un revisor la entienda rápido.

Evita movimientos tipo big-bang o commits mixtos que renombren, muevan y cambien lógica a la vez.

¿Cuándo debería pedir ayuda para dividir un archivo utils desordenado en una app generada por IA?

Cuando heredas código generado por IA, un archivo utils gigante a menudo oculta flujos de auth rotos, secretos expuestos y efectos secundarios impredecibles que hacen que cualquier cambio sea riesgoso. Si necesitas la separación rápida sin convertirla en una reescritura, FixMyMess (fixmymess.ai) puede ayudar haciendo una auditoría gratuita del código y aislando y reparando los helpers riesgosos (auth, cliente API, storage, builders de queries) con arreglos verificados por humanos.

La mayoría de los proyectos pueden estabilizarse en 48–72 horas una vez identificados y separados limpiamente los helpers de mayor riesgo.