Aquí hay una verdad que me llevó años apreciar completamente: cómo se ve tu código importa casi tanto como lo que hace. El código bien formateado se revisa más rápido, tiene menos bugs y es dramáticamente más fácil de mantener. Y del otro lado, el código correctamente minificado carga más rápido y ahorra ancho de banda a tus usuarios.
Hablemos de ambos lados de esta moneda.
Por qué el formateo importa más de lo que piensas
He visto equipos perder horas en revisiones de código discutiendo sobre tabs vs espacios, punto y coma sí o no, o dónde poner las llaves. Es tiempo que no se dedica a entregar funcionalidades. El formateo consistente elimina estos debates por completo.
Pero no es solo armonía del equipo. El código formateado de manera uniforme hace que los bugs sean más fáciles de detectar. Considera esto:
Esa llamada a sendNotification() se ejecuta siempre, no solo para admins — la indentación engaña. Con llaves obligatorias (que la mayoría de los formateadores imponen), este bug es obvio:
Mejores prácticas de formateo
Elige un estilo y automatízalo. No confíes en que los humanos formateen de manera consistente. Usa Prettier — tiene opiniones fuertes, y ese es el punto. Configúralo para que se ejecute al guardar, y nunca más pienses en formateo.
Indentación: 2 espacios es la convención de JavaScript/TypeScript. La guía de estilo de Google, la de Airbnb y Standard coinciden en esto.
Punto y coma: Úsalos. Sí, JavaScript tiene ASI (Automatic Semicolon Insertion), pero tiene trampas bien documentadas. Simplemente agrega los punto y coma.
Longitud de línea: Mantén las líneas bajo 80-100 caracteres. Las líneas largas causan scroll horizontal y hacen los diffs más difíciles de leer.
Cómo funciona realmente la minificación
La minificación es lo opuesto al formateo — hace el código lo más pequeño posible para producción. Esto es lo que hace un minificador como Terser:
- Elimina espacios en blanco y comentarios — Todos esos espacios, tabs, saltos de línea y comentarios
// TODO: fix later? Eliminados. - Acorta nombres de variables —
userAccountBalancese convierte ena.calculateMonthlyPaymentse convierte enb. Solo variables locales — no renombrará cosas que código externo pueda referenciar. - Elimina código muerto — Si una función nunca se llama, se elimina.
- Pre-calcula constantes —
const TAX_RATE = 0.2; total * (1 + TAX_RATE)se convierte entotal*1.2.
¿El resultado? Típicamente 50-70% de reducción en tamaño de archivo. Aquí un antes/después:
Antes (legible, 147 bytes):
Después (minificado, 62 bytes):
Misma funcionalidad, 58% más pequeño.
No olvides los Source Maps
El código minificado es ilegible, lo que hace doloroso depurar errores en producción. Los source maps resuelven esto mapeando el código minificado de vuelta a tu fuente original. Cada herramienta de build moderna los genera — asegúrate de subirlos a tu servicio de seguimiento de errores (Sentry, Bugsnag, etc.).
El flujo de trabajo práctico
El flujo de trabajo práctico
En desarrollo: escribe código formateado y legible. Usa Prettier + ESLint para formateo y linting automático. En producción: minifica todo a través de tu herramienta de build (webpack, Vite, esbuild).
Configurar Prettier + ESLint en 60 segundos
Si aún no has automatizado el formateo, aquí está la configuración más rápida:
Luego agrega un script de formateo a tu package.json: "format": "prettier --write src/**/*.{js,ts,jsx,tsx}". Ejecútalo una vez para formatear toda tu base de código, y configura tu editor para formatear al guardar en adelante. El diff inicial puede ser enorme, pero después de eso, los debates de formateo se acabaron para siempre.
Tree Shaking vs Minificación
La gente a menudo confunde estos dos, pero son técnicas de optimización diferentes que trabajan juntas:
- Minificación hace archivos individuales más pequeños eliminando espacios en blanco, acortando nombres y pre-calculando expresiones. No elimina exports no usados.
- Tree shaking elimina código no usado a través de módulos. Si solo importas
{ debounce }de lodash-es, tree shaking elimina todo lo demás del bundle.
Ambos son esenciales para builds de producción. Bundlers modernos como Vite y esbuild hacen ambos automáticamente — solo asegúrate de usar imports de ES modules (import) en lugar de CommonJS (require) para que tree shaking pueda analizar tu grafo de dependencias.
Trampas comunes de la minificación
1. Depender de function.name en producción. Los minificadores renombran funciones, así que myFunction.name devolverá "a" o algo igualmente inútil en producción. Si necesitas nombres de función estables (para logging, seguimiento de errores o reflexión), usa identificadores de string explícitos.
2. Minificar código que usa eval(). eval() puede referenciar cualquier variable por nombre, así que el minificador no puede renombrar nada de forma segura en ese scope. La mayoría de los minificadores omitirán el renombrado en funciones que contienen eval(), pero esto limita la optimización significativamente. Evita eval() completamente si es posible.
3. No probar builds minificados. Algunos bugs solo aparecen después de la minificación — especialmente con el mangling de nombres de propiedades. Siempre ejecuta tu suite de pruebas contra el build de producción, no solo el de desarrollo.
Midiendo el tamaño de tu bundle
La minificación solo importa si sabes con qué estás empezando. Aquí hay formas rápidas de revisar el tamaño de tu bundle:
Estas herramientas generan treemaps visuales mostrando exactamente qué dependencias están consumiendo tu bundle. Te sorprenderás — una vez encontré un proyecto donde los locales de moment.js representaban 500KB del bundle final. Cambiar a dayjs ahorró 490KB instantáneamente.
Prettier vs ESLint: No son lo mismo
Bien, necesito sacarme esto del pecho porque veo esta confusión *en todas partes*: Prettier y ESLint no son la misma herramienta. Ni siquiera están haciendo el mismo trabajo. Y aun así, constantemente veo desarrolladores tratándolos como intercambiables. Déjame explicarlo.
Prettier es un formateador de código. Le importa cómo *se ve* tu código — espacios en blanco, saltos de línea, punto y coma, comas finales, estilo de comillas, indentación. Eso es todo. No sabe ni le importa si tu código realmente funciona. Podrías tener una función que borra toda tu base de datos y Prettier solo se aseguraría de que esté bien indentada.
ESLint, por otro lado, es un linter. Le importa la *calidad* del código — variables no usadas, código inalcanzable, bugs potenciales, manejo de errores faltante, problemas de accesibilidad. Atrapa las cosas que te morderán a las 2 AM un sábado cuando te están llamando por una alerta.
Pero aquí está el tema — ESLint *también* tiene algunas reglas de formateo incorporadas, y ahí es donde las cosas se complican. Si ejecutas ambas herramientas sin configurarlas correctamente, pelearán entre sí. Prettier formatea tu código de una manera, ESLint se queja de que debería ser de otra, lo arreglas para ESLint, Prettier lo reformatea... es un bucle infinito de frustración.
La solución es muy simple: usa eslint-config-prettier. Desactiva todas las reglas de ESLint que conflictúan con Prettier, para que ESLint se enfoque en la calidad del código y Prettier maneje el formateo. No más peleas.
Nota cómo "prettier" es el último elemento en el array extends? Eso es crucial — necesita sobrescribir cualquier regla de formateo de plugins listados arriba. He visto equipos pasar horas depurando conflictos ESLint/Prettier solo para descubrir que tenían el orden equivocado. Confía en mí en esto.
Mi configuración recomendada: deja que Prettier maneje todo lo cosmético, y configura reglas de ESLint que realmente atrapen bugs. No desperdicies ESLint quejándose de punto y coma cuando Prettier ya maneja eso.
El gran debate de Tabs vs Espacios (resuelto)
Muy bien, hablemos de la guerra santa de la programación. ¿Tabs o espacios? He visto amistades terminar por este debate. He visto hilos de Slack que duraron *días*. Personalmente presencié a un desarrollador senior escribir una página de 2.000 palabras en Confluence defendiendo los tabs. Fue magnífico y completamente innecesario.
Veamos los datos reales. La Encuesta de Desarrolladores de Stack Overflow ha mostrado consistentemente que los espacios son más populares — aproximadamente 60-65% de los desarrolladores prefieren espacios. El propio análisis de GitHub de repos públicos cuenta una historia similar.
Pero aquí está lo interesante: varía enormemente por lenguaje. Go usa tabs — eso ni siquiera es un debate, gofmt impone tabs y nadie discute con gofmt. Python usa 4 espacios — PEP 8 lo dice, y no se discute con PEP 8 tampoco. ¿JavaScript y TypeScript? La comunidad se ha decidido mayormente por 2 espacios, y prácticamente toda guía de estilo importante está de acuerdo.
El argumento de accesibilidad a favor de los tabs es realmente convincente — los tabs permiten que cada desarrollador establezca su ancho visual preferido, lo cual importa para desarrolladores con discapacidades visuales. Esa es una razón real y legítima para preferir tabs.
Pero ¿sabes qué? Aquí está la respuesta *realmente* correcta, y lo digo sinceramente: usa lo que tu formateador imponga y deja de discutir. Si tu proyecto usa Prettier con tabWidth: 2 y useTabs: false, entonces usas 2 espacios. Punto. El formateador toma la decisión, tú la aceptas, y gastas tu energía en cosas que realmente importan — como si tu app se cae cuando alguien pone un emoji en la caja de búsqueda.
La vida es demasiado corta para discusiones de formateo. Deja que los robots decidan.
Minificación moderna: esbuild, SWC y la revolución de velocidad
Durante años, Terser fue el rey de la minificación de JavaScript. Reemplazó a UglifyJS, estaba probado en batalla, funcionaba genial. ¿El único problema? Es lento. Realmente *muy* lento en bases de código grandes. Y no me refiero a "ve por un café" lento — me refiero a "reconsidera tus decisiones de carrera mientras miras la pipeline de CI" lento.
Entonces apareció esbuild y cambió todo. Escrito en Go, esbuild es 10-100x más rápido que Terser. No exagero — los benchmarks son casi cómicos. ¿Un proyecto que a Terser le toma 30 segundos minificar? esbuild lo hace en 300 milisegundos. La primera vez que lo vi, genuinamente pensé que algo estaba roto porque terminó tan rápido.
SWC es otro contendiente, escrito en Rust. No es tan rápido como esbuild para minificación pura, pero es una toolchain más completa — maneja transpilación, bundling y minificación todo en uno. Si usas Next.js, ya estás usando SWC bajo el capó.
Aquí una comparación aproximada en un proyecto mediano (~500 archivos JS):
| Tool | Language | Minification Time | Notes |
| Terser | JavaScript | ~25s | Battle-tested, most compatible |
| esbuild | Go | ~0.3s | Blazing fast, some edge cases |
| SWC | Rust | ~0.8s | Full toolchain, great ecosystem |
¿Realmente importa la diferencia de velocidad? Honestamente, para un proyecto pequeño con un build de 5 segundos, probablemente no. Pero cuando ejecutas CI/CD en un monorepo grande y tu pipeline de build corre 50 veces al día, esos minutos se acumulan rápido. He visto equipos recortar 10+ minutos de sus tiempos de CI solo cambiando de Terser a esbuild.
La buena noticia es que si usas Vite, ya estás obteniendo esbuild para builds de desarrollo y Rollup (con Terser o esbuild) para producción. Next.js usa SWC. Angular también ha estado experimentando con esbuild. El ecosistema se mueve rápido aquí.
Una palabra de precaución: esbuild y SWC no siempre producen output byte-por-byte idéntico a Terser. En casos raros, las optimizaciones más agresivas de Terser producen bundles ligeramente más pequeños. Pero hablamos de quizás 1-2% de diferencia — totalmente vale la pena por la mejora de velocidad de 100x en la mayoría de los casos.
Minificación de CSS y HTML también
Hemos estado hablando de JavaScript, pero no descuides la minificación de CSS y HTML. En serio, he visto proyectos donde el bundle de CSS era *más grande* que el de JavaScript. Especialmente si usas un framework utility-first como Tailwind (antes de que PurgeCSS haga lo suyo).
Para CSS, cssnano es la herramienta principal. Hace más que solo eliminar espacios en blanco — también fusiona reglas duplicadas, convierte colores a formatos más cortos (#ff0000 se convierte en #f00 o incluso red), elimina propiedades redundantes y optimiza expresiones calc(). En una hoja de estilos típica, puedes esperar 30-50% de ahorro.
Para HTML, html-minifier-terser elimina espacios en blanco entre etiquetas, elimina etiquetas de cierre opcionales, minifica CSS y JS inline, elimina comentarios HTML y colapsa atributos booleanos. Es sorprendentemente efectivo — he visto 20-30% de reducción en páginas con mucho HTML.
Ahora viene lo bueno: si usas Angular, React, Vue o prácticamente cualquier framework moderno con un paso de build, esto ya está pasando automáticamente. Tu herramienta de build maneja la minificación de CSS y HTML como parte del build de producción. Pero saber qué pasa bajo el capó es genuinamente útil — especialmente cuando necesitas depurar por qué tu HTML de producción no coincide con tu fuente, o cuando intentas optimizar esa última ruta de render crítica.
Formateo de código en pipelines CI/CD
Mira, aquí está el tema con "formatear al guardar" — solo funciona si *cada persona en el equipo* lo tiene configurado correctamente. Y esto me ha quemado antes. Conoces el escenario: alguien se une al equipo, clona el repo, empieza a hacer cambios, y envía un PR con 400 cambios de formateo mezclados con sus 5 líneas de código real. Revisar ese PR es una pesadilla.
¿La solución? Impón el formateo en CI. Haz imposible mergear código sin formatear. Así es cómo:
Paso 1: Agregar un check de CI. Agrega prettier --check . a tu pipeline de CI. Sale con código 1 si algún archivo no coincide con el formateo de Prettier. Sin argumentos, sin debates — el CI es la ley.
Paso 2: Agregar pre-commit hooks. Atrapar problemas de formateo en CI es genial, pero es aún mejor atraparlos *antes* de que el código sea commiteado. Ahí es donde entran Husky y lint-staged.
Lo bello de lint-staged es que solo corre en los archivos que realmente cambiaste, no en toda la base de código. Así que el pre-commit hook toma 1-2 segundos en lugar de 30. Nadie va a desactivar un hook que toma 1 segundo.
He visto equipos pasar de "el formateo es una fuente constante de fricción en PRs" a "literalmente nunca pensamos en formateo" dentro de una semana de configurar esto. Es una de esas cosas donde la configuración de 15 minutos se paga mil veces.
Casos de estudio de tamaño de bundle del mundo real
Hablemos de números reales, porque el consejo abstracto sobre "mantener los bundles pequeños" no es muy útil sin contexto. He visto estos escenarios exactos en proyectos de producción, y las diferencias son genuinamente impactantes.
Caso 1: lodash vs lodash-es. Este es el clásico. Si haces import _ from 'lodash' y solo usas _.debounce, estás importando la biblioteca completa — 71.5 KB minificado + gzipped. Cambia a import { debounce } from 'lodash-es' y con tree shaking, estás viendo aproximadamente 1.5 KB solo para esa función. Eso es una reducción del 98%. Compruébalo tú mismo en Bundlephobia.
Caso 2: moment.js vs dayjs. Moment.js fue el estándar de oro para manejo de fechas durante años, pero viene con 72.1 KB minificado + gzipped — y eso incluye todos sus datos de locale por defecto. Day.js tiene una API casi idéntica pero pesa 2.9 KB. No es un error de tipeo. 72 KB vs 3 KB para básicamente la misma funcionalidad. El equipo de Moment.js mismo ahora recomienda alternativas.
Caso 3: Bibliotecas de iconos. Esto atrapa a la gente todo el tiempo. Si haces import { FaHome } from 'react-icons/fa', estás bien — eso es tree-shakeable. Pero algunas bibliotecas de iconos no están configuradas para tree shaking, y terminas importando cientos de iconos SVG cuando solo necesitas 5. He visto importaciones de iconos agregar 200+ KB a bundles. Siempre verifica que tu biblioteca de iconos soporte tree shaking, o importa iconos individualmente.
Caso 4: Bibliotecas de formato de fechas. ¿Necesitabas formatear una fecha en una zona horaria específica? Una vez vi un proyecto que importó moment-timezone (la versión completa con todos los datos de zona horaria) — eso son 97 KB minificado + gzipped — solo para formatear una sola fecha. La API nativa Intl.DateTimeFormat maneja la mayoría del formateo de zonas horarias nativamente sin costo de bundle.
La lección aquí no es "nunca uses dependencias" — eso sería tonto. La lección es: sabe qué estás importando y cuánto cuesta. Ejecuta npx source-map-explorer build/static/js/*.js o revisa Bundlephobia antes de agregar esa nueva biblioteca brillante. Tus usuarios en conexiones móviles lentas te lo agradecerán.
Pruébalo tú mismo
¿Necesitas limpiar rápidamente algo de código desordenado? Pégalo en nuestro JavaScript Formatter para una salida instantánea y legible. ¿Listo para reducirlo para producción? El JavaScript Minifier lo reduce al mínimo. Y si no estás seguro de que tu código sea válido, pásalo primero por el JavaScript Validator.