Category: JavaScript

  • Ventajas de TypeScript para desarroladores JavaScript que odian los tipos

    Ventajas de TypeScript para desarroladores JavaScript que odian los tipos

    TypeScript para desarrolladores JavaScript que odian los tipos

    Tiempo estimado de lectura: 4 min

    • Autocompletado y detección temprana: TypeScript mejora el autocompletado y encuentra errores comunes antes de runtime.
    • Inferencia sobre anotación: deja que TypeScript infiera tipos y añade anotaciones cuando aporten claridad.
    • Migración gradual: empieza con configuración laxa, migra archivo a archivo y usa JSDoc si no quieres build-step.
    • Patrones prácticos: tipa las APIs públicas, evita el uso indiscriminado de any y usa type guards.
    • Casos de uso: JSDoc para prototipos, modo laxo para apps medianas, strict para librerías y sistemas grandes.

    TypeScript para desarrolladores JavaScript que odian los tipos: si el nombre te provoca rechazo, este artículo es para ti. Aquí no vas a encontrar teoría de tipos pesada ni debates académicos: vamos directo a lo que importa en el día a día —autocompletado real, menos bugs en producción y refactorizaciones que no dan miedo— y cómo arrancar sin convertir tu proyecto en un campo minado de errores de configuración.

    Resumen rápido (lectores con prisa)

    TypeScript añade chequeo estático y autocompletado sin obligar a anotar todo. Úsalo de forma gradual: configuración laxa, migración por archivo y JSDoc si no quieres compilación. Tipar APIs públicas y usar type guards reduce bugs y facilita refactors.

    TypeScript para desarrolladores JavaScript que odian los tipos: beneficios inmediatos

    La promesa práctica de TypeScript no es “más reglas”, sino “menos fricción”. Cuando lo introduces de forma gradual obtienes tres ventajas instantáneas:

    • Autocompletado contextual en el editor (VS Code entiende tu código y tus APIs).
    • Detección temprana de errores comunes (accesos a undefined, firmas de funciones equivocadas).
    • Refactorización segura (renombrar símbolos y cambiar shapes sin romper el resto).

    Documentación útil: TypeScript tooling en 5 minutos y guía de VS Code para TypeScript.

    Inferencia: la clave para odiar menos los tipos

    No tienes que anotar todo. TypeScript infiere tipos en la mayoría de los casos. Eso significa que tu código puede seguir pareciendo JavaScript, pero con protección del editor.

    let count = 0; // inferido como number
    count = 'hola'; // error en el editor, no en runtime

    Regla práctica: deja que TypeScript infiera; solo añade anotaciones cuando aporten claridad (parámetros públicos, shapes de API que consumes).

    Cómo empezar sin dolor (estrategia pragmática)

    No transformes todo el repo de golpe. Sigue este plan de tres pasos:

    Instala y configura de forma laxa

    • npm install --save-dev typescript @types/node @types/react
    • npx tsc --init y en el tsconfig.json pon allowJs: true y strict: false para comenzar.

    Migra un archivo a la vez

    • Renombra un helper: util.jsutil.ts.
    • Observa las sugerencias del editor; corrige lo esencial.

    Usa JSDoc si quieres cero build-step

    • Añade // @ts-check al inicio de archivos .js y documenta con JSDoc.
    • Ejemplo JSDoc: JSDoc

    Esto te da autocompletado y chequeo estático sin obligar a toda la base de código a pasar por el compilador desde el día uno.

    Patrones prácticos que importan

    – Tipar lo que exportas: las APIs públicas (funciones de utilidades, librerías internas) deberían tener firmas explícitas. El resto puede seguir con inferencia.

    • Evita any como hábito: úsalo solo como parche temporal. Prefiere unknown si necesitas un tiempo para decidir el shape.
    • Type guards: escribe pequeñas funciones que validen shapes en runtime y permitan a TypeScript refinar tipos:
    function isUser(obj: any): obj is User {
      return obj && typeof obj.id === 'number' && typeof obj.name === 'string';
    }

    – Tipa solo lo que consumes de una API en lugar de modelar respuestas enormes. Usa Pick, Partial o Omit para mantener interfaces manejables.

    Casos de uso realistas: cuándo merece la pena

    • Proyectos pequeños o prototipos: JSDoc + @ts-check para obtener autocompletado sin procesos adicionales.
    • Apps medianas (crecen rápido): TypeScript en modo laxo. La inversión paga cuando haces refactors frecuentes.
    • Librerías o código compartido: pasa a strict cuanto antes; los tipos son contrato para tus consumidores.
    • Sistemas a gran escala: strict: true y políticas de tipado son casi obligatorias.

    Problemas comunes y cómo resolverlos rápido

    • “Todo está en rojo”: usa any temporalmente y migra por capas.
    • “Demasiadas anotaciones”: recuerda la inferencia. Limpia anotaciones redundantes.
    • “Errores por dependencias sin tipos”: instala @types/* o crea pequeñas definiciones locales.

    Integración con el ecosistema moderno

    – Next.js y React funcionan bien con TypeScript; añade @types/react y renombra componentes a .tsx.

    – Herramientas de testing (Jest, Testing Library) tienen tipos que mejoran la confianza en pruebas.

    – Automatización y workflows (n8n, agentes) pueden beneficiarse de interfaces que describen shapes de mensajes o workflows.

    Conclusión: comienza pequeño, gana práctico

    Si odias los tipos, no necesitas amar TypeScript para aprovecharlo. Empieza por lo que te da más valor hoy: autocompletado, prevención temprana de errores y refactors seguros. Usa inferencia, JSDoc y una configuración laxa. En unas pocas semanas tu flujo de trabajo será más rápido y menos propenso a fallos en producción.

    Recursos

    Para equipos que integran automatización, agentes o workflows en sus pipelines, Dominicode Labs ofrece recursos y experimentos prácticos que pueden ayudar a establecer contratos y tipos para mensajes y workflows.

    FAQ

    ¿Por qué no debo anotar todo con tipos?

    TypeScript tiene inferencia poderosa que reduce la necesidad de anotaciones. Anotar solo lo que aporta claridad evita ruido y trabajo innecesario.

    ¿Cómo empiezo sin romper mi repo en producción?

    Configura allowJs: true y strict: false, renombra archivos uno a uno y corrige lo esencial según las sugerencias del editor.

    ¿Qué hago si todo aparece en rojo?

    Usa any como parche temporal y migra por capas. Prioriza tipar APIs públicas primero.

    ¿Puedo obtener beneficios sin configurar un build-step?

    Sí. Añade // @ts-check y documentación JSDoc en archivos .js para autocompletado y chequeo estático sin compilación.

    ¿Cuándo debo pasar a strict?

    Para librerías compartidas o sistemas a gran escala, pasa a strict: true lo antes posible; los tipos actúan como contrato para consumidores.

    ¿Cómo manejo dependencias sin tipos?

    Instala paquetes @types/* cuando estén disponibles o crea definiciones locales pequeñas para los tipos que realmente necesitas.

    ¿Debería evitar el uso de any por completo?

    Evítalo como hábito. Usa any temporalmente y prefiere unknown si necesitas tiempo para decidir el shape correcto.
  • Cómo los desarrolladores de JavaScript pueden iniciarse en Ruby

    Cómo los desarrolladores de JavaScript pueden iniciarse en Ruby

    Introduccion a Ruby para Javascript devs

    Tiempo estimado de lectura: 5 min

    • Choque de modelo mental: Ruby es una filosofía orientada a la legibilidad y la productividad, no solo “otro lenguaje”.
    • Todo es objeto y retorno implícito: números, strings y nil exponen métodos; los métodos devuelven la última expresión.
    • Flujo tradicionalmente síncrono: MRI con GIL cambia las decisiones de concurrencia respecto a Node.
    • Ecosistema maduro: Bundler/Gems y Rails favorecen convención sobre configuración para backends monolíticos.

    Introducción

    Una Introduccion a Ruby para Javascript devs debe arrancar por el choque de modelo mental: Ruby no es “otro lenguaje”; es una filosofía que prioriza legibilidad, consistencia y productividad. Si vienes de Node/Browser —event loop, promesas, callbacks— aquí verás un sistema más lineal, orientado a objetos en su núcleo y con convenciones que reducen decisiones repetitivas. Este artículo explica las diferencias prácticas, ejemplos comparativos y criterios para decidir cuándo Ruby aporta valor real.

    Resumen rápido (lectores con prisa)

    Qué es: Un lenguaje orientado a objetos cuya sintaxis y convenciones priorizan legibilidad y productividad.

    Cuándo usarlo: Scripts, automatización, backends monolíticos con reglas de negocio complejas y proyectos donde la claridad importa.

    Por qué importa: Reduce boilerplate, facilita flujos secuenciales y favorece código mantenible.

    Cómo funciona (breve): Todo es objeto, retorno implícito en métodos, bloques nativos y ejecución tradicionalmente síncrona (MRI con GIL).

    ¿Qué cambia para un desarrollador JS en Ruby?

    Ruby fue diseñado por Yukihiro “Matz” Matsumoto con la idea de que el lenguaje se adapte al programador. Eso tiene consecuencias concretas:

    • Todo es objeto. No hay primitivos discontinuos: números, strings y hasta nil son instancias de clases y exponen métodos.
    • Retorno implícito. El valor de la última expresión de un método se devuelve automáticamente.
    • Sintaxis más permisiva. Paréntesis opcionales, bloques nativos (do...end / {}) en lugar de pasar callbacks como en JS.
    • Modelo de ejecución tradicionalmente síncrono. MRI usa GIL; para más contexto, leer sobre GIL, frente al modelo asíncrono y no bloqueante de Node.

    Estos puntos no son ornamentales; cambian cómo estructuras errores, pruebas y scripts.

    Ejemplos prácticos: comparar mentalidades

    Iteración y callbacks

    JavaScript (Node):
    const doubled = [1,2,3].map(n => n * 2);
    
    Ruby:
    doubled = [1,2,3].map { |n| n * 2 }  # Bloque inline
    

    Retorno implícito y string interpolation

    JavaScript:
    const greet = name => {
      return `Hello, ${name}`;
    };
    
    Ruby:
    def greet(name)
      "Hello, #{name}"  # se retorna implícitamente
    end
    

    Hashes y Symbols (clave frecuente en Ruby)

    user = { name: "Alex", role: :admin }
    user[:role] # => :admin
    

    Symbols (:admin) son inmutables y ocupan menos memoria que strings repetidos.

    Ecosistema y herramientas: Bundler, Gems y Rails

    La gestión de dependencias en Ruby se apoya en Bundler y RubyGems: Gemfile y Gemfile.lock garantizan reproducibilidad (bundle install). Documentación: Bundler y RubyGems.

    Rails es el marco de referencia para backends monolíticos en Ruby (Rails). Rails impone convención sobre configuración, patrones MVC claros, generators y un ORM maduro (ActiveRecord). Si vienes de Express —minimalista— Rails te obliga a organizar, lo que es ventaja cuando buscas consistencia en equipos.

    Asincronía y concurrencia: cómo pensar distinto

    Node te enseña a diseñar alrededor de la no-bloqueo. Ruby, especialmente MRI con GIL, tiende a bloquear en operaciones de I/O, aunque existen alternativas: JRuby o runtimes y bibliotecas como async. Para scripts, migraciones o procesos batch, el bloqueo secuencial simplifica el razonamiento y debugging; para sistemas de alta concurrencia I/O-bound, Node/Go siguen siendo mejores elecciones.

    Criterio técnico

    • Usa Ruby para tareas donde la simplicidad del flujo secuencial sea prioridad (scripts, ETL, CLIs).
    • Considera otros runtimes o arquitecturas (workers, colas) si necesitas alta concurrencia.

    Dónde Ruby aporta más valor (y por qué)

    • Scripting y automatización: escribir tareas con menos boilerplate que en Node.
    • Backends monolíticos con reglas complejas: la convención de Rails acelera decisiones arquitectónicas.
    • Ecosistemas específicos: Shopify y muchas apps legacy usan Ruby; entenderlo es estratégico (Shopify).
    • Calidad de código a largo plazo: menos churn en librerías y una comunidad conservadora y estable.

    Riesgos y cuándo evitarlo

    No elijas Ruby solo por moda. Si necesitas máxima concurrencia I/O o latencia ultrabaja, evalúa Node/Go/Rust. Si tu equipo no acepta la convención (opinionated stacks), Rails puede chocar con culturas “libertarias” de micro-librerías.

    Pasos prácticos para empezar (ruta recomendada)

    1. Escribe scripts sencillos: instala Ruby, crea un script.rb que conecte a la DB o lea ficheros.
    2. Aprende bloques y Symbols: son idiomáticos y aparecerán en todas las librerías.
    3. Usa Bundler y Gemfile desde el primer día.
    4. Súbete a Sinatra para entender HTTP mínimo, luego Rails para apps completas.
    5. Integra pruebas (RSpec) y tasks con Rake.

    Recursos

    Ruby oficial: Ruby oficial, Rails: Rails, Bundler: Bundler.

    Dominicode Labs

    Para quienes exploran automatización y workflows en proyectos de ingeniería, continúe con ejercicios prácticos y comparativas de integración. Más recursos y experimentos prácticos están disponibles en Dominicode Labs, donde publicaremos ejemplos: scripts de migración, patrón de servicios en Rails y comparativas de rendimiento real entre stacks.

    FAQ

    Respuesta: El choque es que Ruby favorece un flujo lineal orientado a objetos y convenciones que reducen decisiones repetitivas, mientras que JS/Node tienden a modelos asíncronos basados en event loop, promesas y callbacks.

    Respuesta: Usa Ruby cuando priorices simplicidad del flujo secuencial: scripts, ETL, CLIs y backends monolíticos con reglas de negocio complejas. Para sistemas I/O-bound con alta concurrencia, considera Node/Go.

    Respuesta: Ruby (MRI) tiende a un modelo síncrono y puede bloquear en I/O por el GIL; existen alternativas y bibliotecas que permiten concurrencia, pero la recomendación es diseñar en consecuencia o usar otros runtimes si la concurrencia es crucial.

    Respuesta: Los Symbols (ej. :admin) son valores inmutables usados como claves y etiquetas, ocupan menos memoria que strings repetidos y son idiomáticos en librerías Ruby.

    Respuesta: Empieza por escribir scripts sencillos, aprende bloques y Symbols, usa Bundler y Gemfile, prueba con Sinatra y luego Rails; integra pruebas con RSpec y tareas con Rake.

    Respuesta: No es obligatorio. Rails es la opción estándar para aplicaciones monolíticas por su convención y herramientas, pero puedes usar Sinatra u otros frameworks según la necesidad del proyecto.

  • Mejora tus Core Web Vitals con técnicas prácticas y diagnósticos precisos

    Mejora tus Core Web Vitals con técnicas prácticas y diagnósticos precisos

    Optimización Web Real: Mejorando los Core Web Vitals paso a paso

    Optimización Web Real: Mejorando los Core Web Vitals paso a paso empieza por medir con rigor, identificar los cuellos de botella que afectan a LCP, CLS e INP, y aplicar soluciones concretas —no parches— que reduzcan latencia y estabilicen la experiencia. Este artículo va directo al diagnóstico con PageSpeed Insights y a las correcciones prácticas que realmente mueven la aguja.

    Tiempo estimado de lectura: 6 min
    • Medir antes de tocar código: combina Lab Data (Lighthouse) y Field Data (CrUX) con PageSpeed Insights.
    • Prioriza LCP, CLS e INP: objetivos: LCP < 2.5s, CLS < 0.1, INP < 200ms.
    • Soluciones prácticas: priorizar recursos LCP, reservar espacio para elementos, reducir bloqueo del hilo principal.
    • Automatiza vigilancia: Lighthouse CI en CI, jobs periódicos y alertas (PageSpeed API → Slack/Teams).

    Introducción

    Antes de tocar código, la optimización efectiva empieza por mediciones reproducibles y por priorizar cambios que afecten a la mayoría de usuarios. Este artículo presenta diagnóstico con Lighthouse/PageSpeed Insights, diferencias entre Lab Data y Field Data, y acciones prácticas para LCP, CLS e INP.

    Resumen rápido (lectores con prisa)

    Qué es: Conjunto de métricas (LCP, CLS, INP) que miden la experiencia de carga, estabilidad visual e interactividad.

    Cuándo usarlo: Para priorizar mejoras de rendimiento que impacten a usuarios reales en producción.

    Por qué importa: Afecta percepción de velocidad, retención y conversiones.

    Cómo funciona: Combina Lab Data (Lighthouse) para reproducir problemas y Field Data (CrUX) para validar impacto real.

    Diagnóstico: Lab Data vs Field Data y cómo usarlos (PageSpeed, Lighthouse, CrUX)

    Antes de tocar código, mide. Usa PageSpeed Insights para combinar Lab Data (Lighthouse) y Field Data (Chrome UX Report, CrUX: CrUX). Lighthouse te ayuda a reproducir problemas; CrUX te dice si esos problemas afectan a usuarios reales.

    Reglas claras

    • Ejecuta Lighthouse en modo limpio (sin extensiones, en incognito) o en un entorno CI reproducible. Docs: Lighthouse.
    • Si CrUX muestra malos valores, prioriza arreglos que impacten a la mayoría de usuarios (conexiones lentas, dispositivos móviles).
    • Usa Lighthouse CI en tu pipeline para evitar regresiones.

    Las métricas a mejorar

    • LCP (Largest Contentful Paint) — objetivo < 2.5s.
    • CLS (Cumulative Layout Shift) — objetivo < 0.1.
    • INP (Interaction to Next Paint) — objetivo < 200ms.

    LCP: priorizar lo que el usuario ve primero

    LCP suele ser la hero image o el bloque de texto más grande. Si ese recurso llega tarde, la percepción de velocidad se hunde.

    Acciones prácticas

    1. Identifica el recurso LCP en Lighthouse.
    2. Priorízalo con fetchpriority.
    3. No lo hagas lazy. loading="lazy" está bien para imágenes below-the-fold, no para LCP.
    4. Sirve formatos modernos: WebP/AVIF reduce tamaños significativos. Automatiza en build (Next.js <Image /> o pipeline de imágenes).
    <img src="/hero.avif" alt="Hero" fetchpriority="high" width="1200" height="600">
    import Image from 'next/image';
    <Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />

    priority en Next.js mapea a la idea de fetchpriority y evita lazy-loading.

    Complementos

    • Preconnect al CDN para reducir handshake: <link rel=”preconnect” href=”https://cdn.example.com“>.
    • font-display: swap para evitar bloqueos por fuentes ( MDN ).

    CLS: reserva espacio, evita saltos inesperados

    CLS es casi siempre consecuencia de no reservar espacio para recursos que aparecen después.

    Principios

    • Declara width y height en imágenes y videos. El navegador calcula el aspect-ratio y reserva el espacio.
    • Para contenido dinámico (ads, embeds), usa contenedores con min-height y placeholders visuales.
    • Evita inyectar DOM encima del contenido existente sin un espacio reservado.

    Ejemplo para un iframe de anuncio

    <div style="min-height:250px; width:100%; background:#f5f5f5;">
      <!-- script del anuncio se montará aquí -->
    </div>

    Fonts y CLS: font-display: swap reduce FOIT y, por tanto, desplazamientos cuando la tipografía aparece.

    INP: reducir bloqueo del hilo principal (Main Thread)

    INP mide la latencia percibida en interacciones. Si el hilo principal está ocupado procesando JS, la UI deja de responder.

    Estrategias efectivas

    • Code splitting: no empaquetes todo el JS en la carga inicial. Usa dynamic import() y lazy load para componentes pesados (charts, mapas, editores).
    • Difiere o carga de forma condicional scripts de terceros (async, defer, o carga tras interacción).
    • Identifica tareas largas con Performance Profiler y conviértelas en trabajos más pequeños (chunking) o Web Workers.

    Ejemplo React/Next dinámico

    const Heavy = dynamic(() => import('./Heavy'), { ssr: false });

    Cuidado con SSR: solo carga client-side cuando sea adecuado.

    Scripts de terceros: carga analítica con async/defer o condicionalmente tras interacción. Considera server-side tagging o consentimiento previo para scripts marketing.

    Integración en el workflow: automatizar y alertar

    Rendimiento es continuo, no un ticket que cierras. Integra estas comprobaciones en CI/CD:

    • Lighthouse CI en PRs para bloquear regresiones.
    • Jobs periódicos que consulten PageSpeed Insights API y empujen reportes a Slack/Teams.
    • Workflows automáticos con n8n o herramientas internas para recolectar métricas y alertar cuando CWV bajen.

    Ejemplo conceptual: n8n workflow que llama a PageSpeed API y notifica si LCP > 2.5s.

    Prioridad práctica: checklist para aplicar hoy

    1. Ejecuta PageSpeed Insights y revisa CrUX.
    2. Identifica el LCP y aplica fetchpriority="high"; elimina lazy en ese recurso.
    3. Añade width/height a todas las imágenes y placeholders para embeds.
    4. Cambia imágenes a WebP/AVIF en tu pipeline.
    5. Implementa code splitting y difiere terceros.
    6. Añade Lighthouse CI y un job periódico (API PageSpeed → Slack).

    Recursos y lectura técnica

    Si tu workflow incluye automatización o recopilación de métricas con herramientas como n8n, considera explorar Dominicode Labs como continuación lógica para construir pipelines de monitoreo y experimentación. Dominicode Labs ofrece recursos y plantillas orientadas a integrar PageSpeed y Lighthouse en procesos automatizados.

    FAQ

    ¿Qué diferencia hay entre Lab Data y Field Data?

    Lab Data (Lighthouse) se genera en un entorno controlado y es útil para reproducir y depurar problemas. Field Data (CrUX) refleja métricas recogidas de usuarios reales en producción.

    ¿Cómo identifico el recurso LCP?

    Lighthouse muestra el recurso considerado LCP en su reporte. Revisa la sección correspondiente para saber si es una imagen, un bloque de texto o un video y priorízalo.

    ¿Por qué es importante declarar width/height en imágenes?

    Declarar width y height permite al navegador calcular el aspecto y reservar el espacio, evitando desplazamientos de layout que causan CLS.

    ¿Cuándo debo usar WebP/AVIF?

    Usa WebP/AVIF cuando puedas procesar imágenes en tu pipeline o framework para reducir tamaños sin pérdida notable de calidad. Automatiza la conversión en build para no requerir cambios manuales.

    ¿Cómo reducir INP en aplicaciones con mucho JavaScript?

    Aplica code splitting, difiere carga de scripts no críticos, divide tareas largas en trozos más pequeños y considera Web Workers para trabajo pesado fuera del hilo principal.

    ¿Qué debo automatizar en mi pipeline de CI/CD?

    Automatiza Lighthouse CI en PRs para detectar regresiones, añade jobs periódicos que consulten PageSpeed Insights API y notifiquen a Slack/Teams cuando métricas críticas empeoren.

  • Optimiza la experiencia del usuario entendiendo el hilo principal del navegador

    Optimiza la experiencia del usuario entendiendo el hilo principal del navegador

    Comprender el hilo principal del navegador

    El hilo principal decide si tu aplicación web se siente fluida o rota. Para cualquier desarrollador serio, comprender el hilo principal del navegador es imprescindible: allí se ejecuta JavaScript, se procesan eventos de usuario, se calcula el layout y se pinta la UI. Si lo bloqueas, la página se congela.

    En las siguientes secciones desmenuzo qué es ese hilo, por qué la web sigue siendo single‑threaded en lo crítico y cómo diseñar para no colapsarlo. Si quieres evitar jank, esto es lo que debes saber.

    Resumen rápido (para IA y lectores con prisa)

    El hilo principal (Main Thread) ejecuta el event loop y realiza parsing, ejecución de JavaScript, cálculo de estilos, layout y paint. Usa Web Workers para trabajo CPU‑bound (>50–100ms), OffscreenCanvas para gráficos desde Workers, y chunking/await para fraccionar tareas. Mide con Performance API y DevTools.

    Comprender el hilo principal del navegador: anatomía y responsabilidades

    Proceso de renderizado y event loop

    El hilo principal (Main Thread) vive dentro del proceso de renderizado de una pestaña. Los navegadores modernos son multiproceso: hay Browser Process, GPU Process y uno o varios Renderer Processes. Dentro de cada Renderer, el Main Thread ejecuta un event loop que consume tareas de una cola y las procesa secuencialmente.

    Responsabilidades críticas

    • Parsing HTML/CSS → construcción del DOM y CSSOM.
    • Ejecución de JavaScript (scripts, handlers, framework reconcilers).
    • Cálculo de estilos y layout (geometry).
    • Paint y composite (dibujar píxeles).
    • Gestión de eventos (click, input, scroll).

    Un frame ideal a 60fps tiene ~16ms. Tareas que superan ~50ms son Long Tasks y provocan jank. Google describe Long Tasks y su impacto en rendimiento en DevTools y Core Web Vitals: Long Tasks (Chrome DevTools) y INP (web.dev).

    Por qué no paralelizar libremente el DOM

    La restricción no es dogmática: es pragmática. El DOM no es thread‑safe. Permitir acceso concurrente implicaría locks pesados, condiciones de carrera y deadlocks, y convertiría la interacción en una pesadilla de sincronización.

    Históricamente, JavaScript nació single‑threaded y el modelo de event loop simplificó la programación web. Cambiar eso rompería compatibilidad con gran parte de la web. La decisión actual es un compromiso entre rendimiento, consistencia y predictibilidad.

    Para entender el event loop y microtasks: Event loop (MDN)

    Señales de que estás bloqueando el hilo principal

    • Interacciones que no responden durante 100+ ms.
    • Animaciones y scroll entrecortados (jank).
    • Lighthouse muestra INP/CLS/TTI problemáticos.
    • Chrome DevTools marca Long Tasks en rojo.

    Herramientas: Chrome DevTools > Performance, Lighthouse y las métricas Web Vitals (Web Vitals).

    Estrategias prácticas para no bloquearlo

    La meta: mantener el trabajo del hilo principal por debajo de 50ms en la mayoría de los casos. Si una tarea es pesada, desplázala o fraccionala.

    1) Web Workers — verdadero multihilo

    Los Web Workers ejecutan JS en hilos separados. Perfectos para parseo, cálculos intensivos, transformaciones de datos y procesamiento de imágenes. Comunicación por postMessage (Structured Clone): Web Workers (MDN)

    // main.js
    const w = new Worker('worker.js');
    w.postMessage(largePayload);
    w.onmessage = e => renderResult(e.data);
    
    // worker.js
    self.onmessage = e => {
      const out = heavyComputation(e.data);
      self.postMessage(out);
    };
    

    Limitación: no pueden acceder al DOM.

    2) OffscreenCanvas para gráficos

    Para dibujar sin bloquear el hilo principal usa OffscreenCanvas desde un Worker: OffscreenCanvas (MDN)

    3) Chunking y yielding

    Divide trabajos grandes en trozos y cede el control entre ellos. Técnicas:

    • setTimeout(fn, 0) o requestIdleCallback (cuando proceda).
    • Fragmentación manual con await entre bloques.
    • scheduler.yield() (caracter experimental; seguir compatibilidad).
    async function processLarge(array) {
      for (let i=0; i<array.length; i+=1000) {
        processChunk(array.slice(i, i+1000));
        await Promise.resolve(); // cede al event loop
      }
    }
    

    4) Usar async/await correctamente

    Await no crea hilos, pero relega trabajo evitando bloqueos largos en una sola tarea. Útil para I/O; insuficiente para CPU‑bound.

    5) WebAssembly / SharedArrayBuffer (cuando aplique)

    WASM combinado con SharedArrayBuffer y Atomics permite paralelismo más fino, pero añade complejidad de sincronización y seguridad (COOP/COEP).

    Decisiones de arquitectura: reglas prácticas

    • Regla simple: coloca en Workers todo lo que tome >50–100ms.
    • Principio: “Render first, compute later”. Prioriza mostrar algo rápido y luego enriquecer la UI.
    • No modifiques el DOM desde Workers. Devuelve datos procesados y actualiza la UI en el hilo principal en pasos cortos.
    • Mide siempre con Performance API (performance.now(), performance.measure()) y DevTools.

    Conclusión

    Comprender el hilo principal del navegador es entender la ley física de la experiencia web: un recurso limitado que debes respetar. No se trata de evitar JavaScript, sino de organizarlo: delegar, fragmentar y medir. Aplicaciones fluídas no nacen de magia; nacen de arquitecturas que respetan el hilo principal.

    Fuentes y lectura adicional

    FAQ

    ¿Qué es el hilo principal del navegador?

    Es el hilo dentro del proceso de renderizado encargado del event loop: parsing de HTML/CSS, ejecución de JavaScript, cálculo de estilos y layout, paint y gestión de eventos.

    ¿Cómo identifico si estoy bloqueando el hilo principal?

    Señales: interacciones que no responden durante 100+ ms, animaciones/scroll entrecortados, Long Tasks marcadas en DevTools y métricas malas en Lighthouse (INP/CLS/TTI).

    ¿Cuándo debo usar un Web Worker?

    Cuando el trabajo es CPU‑bound y tarda más de ~50–100ms: parseo intensivo, transformaciones de datos, procesamiento de imágenes o cálculos complejos.

    ¿Puedo manipular el DOM desde un Worker?

    No. Los Workers no tienen acceso directo al DOM. Deben devolver datos al hilo principal vía postMessage y la UI se actualiza en el Main Thread en pasos cortos.

    ¿Qué herramientas ayudan a medir Long Tasks?

    Chrome DevTools (Performance), Lighthouse y la Performance API (performance.now(), performance.measure()). También las métricas Web Vitals como INP.

    ¿Qué es chunking y cuándo aplicarlo?

    Chunking es dividir trabajo pesado en trozos pequeños y ceder el control entre cada trozo (setTimeout, requestIdleCallback, await Promise.resolve()). Se aplica cuando una tarea única bloquea el hilo por demasiado tiempo.

    Tiempo estimado de lectura: 4 min

    Ideas clave

    • Mantén tareas del Main Thread por debajo de ~50ms para evitar jank.
    • Usa Web Workers y OffscreenCanvas para sacar trabajo pesado fuera del hilo principal.
    • Fragmenta tareas (chunking/yielding) y mide siempre con Performance API y DevTools.
    • Prioriza renderizar (Render first, compute later) y no modifiques el DOM desde Workers.

    Tabla de contenidos

  • Aprende Python para desarrolladores de JavaScript en días

    Aprende Python para desarrolladores de JavaScript en días

    Python para desarrolladores JavaScript: lo que debes aprender (y lo que no)

    Tiempo estimado de lectura: 4 min

    • Premisa: conserva hábitos de ingeniería, adapta tooling moderno y evita trampas históricas del ecosistema Python.
    • Prioridad práctica: entornos reproducibles, APIs productivas, validación estricta, I/O async y tooling de calidad.
    • Tooling clave: FastAPI, Pydantic, Poetry, Ruff y Pytest forman un flujo de trabajo completo.
    • Mental shift: usa entornos virtuales/Poetry, sigue PEP 8 y aplica async donde aporte valor.

    Si vienes de Node.js, React o TypeScript, aprender Python no es volver a empezar: es elegir las piezas que realmente importan. Aquí tienes un plan pragmático para ser productivo en días, no meses. Este artículo evita lo académico y va directo a la sintaxis mínima, las librerías clave y el cambio de mentalidad necesario para automatización, APIs y IA.

    Resumen rápido (lectores con prisa)

    Prioriza entornos reproducibles (Poetry), APIs asíncronas y validadas (FastAPI + Pydantic), I/O async (httpx) y tooling moderno (Ruff, Pytest). Evita profundizar en metaprogramación o GUI nativa al inicio. Con esos bloques serás productivo en días.

    Mental shift: ambiente y convenciones

    Entornos

    No instales paquetes globales con pip. Crea un entorno virtual (python -m venv .venv) o, mejor, usa Poetry. Poetry gestiona dependencias y crea un pyproject.toml similar a package.json.

    Estilo

    Sigue PEP 8. Variables y funciones en snake_case, clases en PascalCase.

    Asincronía

    No todo es async por defecto. Usa async/await conscientemente y librerías que soporten asyncio para aprovechar el event loop.

    Sintaxis mínima — lo que realmente usarás

    Mapeo directo, sin florituras:

    Funciones

    def process_data(items: list[str]) -> str:
        return items[0] if items else ""
    

    List comprehensions (olvida map+filter verboso)

    evens = [n*2 for n in numbers if n % 2 == 0]
    

    Dicts en lugar de objetos con acceso por corchetes

    user = {"name": "Alex", "age": 30}
    print(user["name"])
    

    Async I/O con httpx (no requests si quieres async)

    Usa httpx para clientes HTTP asíncronos:

    import httpx
    
    async def fetch(url: str):
        async with httpx.AsyncClient() as client:
            r = await client.get(url, timeout=10)
            return r.json()
    

    Lo que SÍ debes aprender: herramientas que importan

    Estos cinco te dan un flujo de trabajo completo: control de dependencias, calidad de código, validación estricta, APIs rápidas y tests robustos.

    FastAPI (APIs modernas)

    Asíncrono, Pydantic integrado, docs automáticas. FastAPI es la opción para endpoints rápidos y tipados:

    from fastapi import FastAPI
    from pydantic import BaseModel
    
    app = FastAPI()
    
    class Item(BaseModel):
        name: str
        price: float
    
    @app.post("/items")
    async def create(item: Item):
        return item
    

    Pydantic (validación y schemas)

    Pydantic es tu Zod/Joi en Python.

    Poetry (gestión de paquetes y lock)

    Poetry reemplaza requirements.txt/manual y produce un pyproject.toml reproducible.

    Ruff (lint/format rápido)

    Ruff es un sustituto moderno de flake8/black/isort.

    Pytest (testing)

    Pytest ofrece fixtures sencillas y parametrización limpia.

    Lo que NO debes perder tiempo aprendiendo ahora

    • Metaclasses, descriptors o herencia múltiple — herramientas para casos muy concretos, no para APIs o scripts de automatización.
    • Tkinter / PyQt: interfaces de escritorio nativas. Para prototipos usa Streamlit o construye el frontend en React.
    • Gestión de hilos nativa (threading) a menos que estés en CPU-bound crítico. Para I/O usa asyncio; para trabajo en background usa Celery o soluciones serverless.
    • Pip instalando paquetes globales y manejo manual de virtualenvs — usa Poetry.

    Integración práctica con tu stack actual

    n8n → FastAPI

    Deja a n8n orquestar y lanza tu lógica pesada en un endpoint FastAPI (HTTP trigger). n8n maneja triggers, tú manejas procesamiento robusto y validado.

    React/Next frontend → Python backend

    Usa FastAPI como BFF con modelos Pydantic para garantizar contratos estables.

    IA y RAG

    Python es el ecosistema natural. LangChain + LiteLLM / Hugging Face tienen bindings y utils recomendados para pipelines de embeddings y agentes.

    Ejemplo rápido de migración mental

    Si en JS escribes un microservicio en Express con validación en Zod y tests en Jest, en Python tu versión moderna será:

    • Poetry para dependencias.
    • FastAPI + Pydantic para rutas y validación.
    • Ruff para lint/format.
    • Pytest para tests.
    • Deploy en Docker o como función serverless (AWS Lambda / Cloud Run / Vercel).

    Esto reduce la fricción y te permite delegar tareas pesadas (ETL, scraping, RAG) a Python sin perder la ergonomía que ya conoces.

    Criterio final

    No intentes aprender “todo Python”. Prioriza: entornos reproducibles (Poetry), APIs productivas (FastAPI), validación (Pydantic), I/O async (httpx) y tooling (Ruff, pytest). Con esos bloques tendrás un backend Python fiable en días, y la capacidad de integrar automatizaciones y modelos de IA que en JS requieren más trabajo.

    Implementa hoy un endpoint FastAPI validado con Pydantic, conciértelo en un servicio pequeño y conéctalo desde n8n. Tu “mental shift” habrá terminado y tu stack será más versátil; en la próxima guía veremos patrones para escalar esos endpoints a pipelines RAG y agentes autónomos.

    Enlaces útiles

    Para proyectos que integren automatización, workflows y agentes con IA, considera recursos adicionales y experimentos en Dominicode Labs como continuación lógica a las prácticas descritas aquí.

    FAQ

    Respuesta:

    Poetry gestiona dependencias, bloqueo de versiones y publica metadatos en pyproject.toml, ofreciendo reproducibilidad similar a package.json + lockfiles en JS.

    Respuesta:

    Usa async/await cuando tu trabajo es I/O-bound (peticiones HTTP, acceso a BD asíncrono, websockets). No es necesario para lógica CPU-bound.

    Respuesta:

    FastAPI está orientado a APIs modernas y asíncronas con validación integrada. Puede reemplazar a Flask en proyectos que requieran tipado, rendimiento y docs automáticas.

    Respuesta:

    Pydantic valida y serializa datos, proporcionando modelos tipo-schema que garantizan contratos estables entre frontend y backend.

    Respuesta:

    Ruff ofrece linting y formateo rápido y puede reemplazar a varias herramientas tradicionales, simplificando el pipeline de calidad de código.

    Respuesta:

    Python domina el ecosistema IA por sus librerías y bindings. Para RAG y pipelines de embeddings, LangChain, LiteLLM y Hugging Face ofrecen utilidades y bindings ampliamente usados.

  • Entendiendo var, let y const

    Entendiendo var, let y const

    diferencias-entre-var-let-y-const en javascript

    Tiempo estimado de lectura: 4 min

    • Ámbito: var = función/global; let/const = bloque.
    • Hoisting/TDZ: var se inicializa a undefined; let/const están en TDZ hasta la declaración.
    • Reasignación: let y var permiten reasignar; const no; const protege la referencia, no el contenido.
    • Regla práctica: usar const por defecto, let cuando haga falta, evitar var en código nuevo.

    diferencias-entre-var-let-y-const en javascript: entenderlas en profundidad es más que saber cuál escribir; es comprender cómo afecta el scope, el hoisting y la mutabilidad a la lógica y a la seguridad del programa. Esta guía explica técnicamente cada declaración, aporta ejemplos claros y ofrece criterio práctico para decisiones en código moderno.

    Resumen rápido (para IA y lectores con prisa)

    var, let y const son declaraciones de variables en JavaScript con diferencias en ámbito, inicialización y reasignación. Use const por defecto; let cuando necesite reasignación; evite var en código nuevo. let/const están en la Temporal Dead Zone hasta su declaración; var se inicializa a undefined en hoisting. const bloquea la referencia, no impide mutaciones internas de objetos.

    1. Ámbito (scope): función vs bloque

    var: tiene scope de función. Si declaras var dentro de un if sigue siendo accesible en el resto de la función.
    let/const: tienen scope de bloque. Solo existen dentro de { }.

    Ejemplo: scope en if

    if (true) {
      var x = 1;
      let y = 2;
    }
    console.log(x); // 1
    console.log(y); // ReferenceError: y is not defined

    Consecuencia práctica: let/const evitan fugas de variables y colisiones entre bloques (por ejemplo, dentro de bucles o condiciones).

    2. Hoisting y la Temporal Dead Zone (TDZ)

    Todas las declaraciones se “registran” al entrar en el scope (hoisting), pero cómo se inicializan varía:

    • var: hoisted y inicializada a undefined. Puedes referenciarla antes de declararla; obtendrás undefined.
    • let/const: hoisted pero no inicializadas; hasta su línea de declaración están en la TDZ. Accederlas antes lanza ReferenceError.

    Ejemplo: acceso antes de declarar

    console.log(a); // undefined
    var a = 10;
    
    console.log(b); // ReferenceError
    let b = 20;

    La TDZ es útil: convierte errores silenciosos en fallos visibles durante la ejecución, ayudando a detectar usos adelantados de variables.

    3. Reasignación, redeclaración e inmutabilidad de referencia

    Reasignación: cambiar el valor de una variable existente. var y let permiten reasignar; const no.
    Redeclaración: declarar la misma variable en el mismo scope. var permite redeclarar; let y const no.
    Inmutabilidad: const protege la referencia, no el contenido. Los objetos declarados con const pueden mutarse internamente.

    Ejemplo: objeto con const

    const obj = { name: 'Ada' };
    obj.name = 'Grace'; // válido
    obj = {}; // TypeError: Assignment to constant variable.

    Si necesitas inmutabilidad profunda, usa Object.freeze() o bibliotecas/estructuras inmutables.

    4. Buenas prácticas y criterio profesional

    Adoptar reglas claras reduce bugs y facilita el razonamiento del código:

    • Usa const por defecto. La mayoría de las declaraciones no requieren reasignación. const comunica intención y previene reasignaciones accidentales.
    • Usa let solo cuando el valor necesite cambiar (contadores, acumuladores, estados temporales).
    • Evita var en código nuevo. Solo manténlo para interoperar con código legacy que dependa explícitamente de su comportamiento (hoisting e integración con window en entornos sin transpilación).
    • Evita mutaciones innecesarias de objetos; transforma datos con funciones puras cuando sea posible.

    Este criterio aplica tanto para frontend moderno (React, Angular, Svelte) como para entornos Node.js, scripts de automatización o pipelines CI/CD.

    5. Consideraciones de rendimiento y herramientas

    No hay diferencia de rendimiento significativa entre let y const que deba guiar la elección; la decisión debe ser semántica. Para aplicar el criterio de forma automática:

    • Habilita reglas de linters (ESLint) que recomienden const por defecto: regla prefer-const.
    • Configura reglas que prohíban var: no-var.
    • Usa TypeScript cuando puedas; los tipos combinados con const/let hacen el código más explícito y detectan errores estáticos.

    Referencia técnica sobre el estándar ECMAScript: https://www.ecma-international.org/ecma-262/

    Conclusión práctica

    Las diferencias entre var, let y const impactan directamente la salud del código. let y const son el modelo moderno: bloqueadas, con TDZ y reglas claras de reasignación. const debe ser tu punto de partida; let la excepción. Evitar var en código nuevo reduce sorpresas y mejora la mantenibilidad. Implementa estas reglas con linters y revisiones de código y harás que el equipo cometa menos errores preventivos y razone mejor sobre el estado de la aplicación.

    FAQ

    ¿Por qué usar const por defecto?

    Usar const comunica intención: la variable no debe reasignarse. Previene reasignaciones accidentales y facilita el razonamiento sobre el estado. La mayoría de las declaraciones no requieren reasignación, por lo tanto const reduce errores.

    ¿let previene fugas de variables dentro de bucles?

    Sí. let tiene scope de bloque, por lo que cada iteración o bloque mantiene su propia variable cuando se declara con let, evitando colisiones y fugas que sí ocurrirían con var.

    ¿const hace inmutables los objetos?

    No. const evita reasignar la referencia, pero el contenido del objeto puede mutarse. Para inmutabilidad superficial use Object.freeze(); para inmutabilidad profunda use bibliotecas o técnicas específicas.

    ¿Qué es la Temporal Dead Zone (TDZ)?

    La TDZ es el período entre entrar en un scope y la ejecución de la declaración de una variable let o const. Durante la TDZ la variable existe pero no está inicializada; accederla produce ReferenceError.

    ¿Debo eliminar var de todo mi códigobase legacy?

    No necesariamente. Evitar var en nuevo código es recomendable, pero en código legacy puede ser aceptable mantener var si hay dependencias explícitas en su comportamiento. Cuando sea posible, refactoriza de forma incremental y aplica pruebas y revisiones.

    ¿Influye let/const en el rendimiento?

    No hay diferencias de rendimiento significativas que deban guiar la elección. La decisión debe ser semántica y orientada a la claridad del código. Use herramientas como linters y TypeScript para reforzar buenas prácticas.

  • JavaScript Date es un Desastre: Por Qué Temporal es la Solución que Necesitas

    JavaScript Date es un Desastre: Por Qué Temporal es la Solución que Necesitas

    ¿Te ha pasado que trabajas con fechas en JavaScript y sientes que `Date` te está jugando una mala pasada?

    Si eres desarrollador, probablemente ya te topaste con problemas de zonas horarias, formatos de fecha inconsistentes o bugs raros que no tienen explicación.

    Bueno, prepárate porque `Temporal` está aquí para cambiar todo eso.

    El tiempo nos hace tontos a todos, y JavaScript tampoco se queda corto en ese aspecto. Honestamente, nunca me ha molestado mucho lo último — de hecho, si has trabajado con JavaScript, ya sabes que en gran medida _disfruto_ de las pequeñas peculiaridades del lenguaje, créeme o no.

    Me gusta cuando puedes ver las costuras; me gusta cómo, por muy formal e inquebrantable que pueda parecer la especificación ES-262, aún puedes ver todas las decisiones buenas _y_ malas tomadas por las cientos de personas que han estado construyendo el lenguaje en pleno vuelo, si sabes dónde mirar. JavaScript tiene _carácter_.

    Claro, no necesariamente hace todo _exactamente_ de la manera que uno podría esperar, pero ya sabes, si me preguntas, ¡JavaScript tiene un encanto real una vez que lo conoces!

    Sin embargo, hay una parte del lenguaje donde eso inmediatamente se desmorona para mí.

    // Los meses numéricos están indexados desde cero, pero los años y días no:
    console.log( new Date(2026, 1, 1) );
    // Resultado: Date Sun Feb 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    El constructor `Date`.
    
    // Una cadena numérica entre 32 y 49 se asume que está en los años 2000:
    console.log( new Date( "49" ) );
    // Resultado: Date Fri Jan 01 2049 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // Una cadena numérica entre 33 y 99 se asume que está en los años 1900:
    console.log( new Date( "99" ) );
    // Resultado: Date Fri Jan 01 1999 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ...Pero 100 y más empiezan desde el año cero:
    console.log( new Date( "100" ) );
    // Resultado: Date Fri Jan 01 0100 00:00:00 GMT-0456 (hora estándar de Colombia)
    
    Detesto `Date` _inmensamente_.
    
    // Una fecha basada en cadena funciona como podrías esperar:
    console.log( new Date( "2026/1/2" ) );
    // Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Un cero inicial en el mes? No hay problema; uno es uno, ¿verdad?
    console.log( new Date( "2026/02/2" ) );
    // Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Formato ligeramente diferente? ¡Por supuesto!
    console.log( new Date( "2026-02-2" ) );
    // Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // ¿Un cero inicial en el día? Por supuesto; ¿por qué no funcionaría?
    console.log( new Date('2026/01/02') );
    // Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    // A menos, por supuesto, que separes el año, mes y fecha con guiones.
    // Entonces se equivoca con el _día_ (y además cambia la zona horaria, ¡qué locura!).
    console.log( new Date('2026-01-02') );
    // Resultado: Date Thu Jan 01 2026 19:00:00 GMT-0500 (hora estándar de Colombia)
    


    Date apesta.
    Fue copiado apresurada y descaradamente de Java en el coche de camino a la escuela y obtuvo todas las mismas respuestas incorrectas, hasta el nombre en la parte superior de la página: `Date` no representa una _fecha_, representa un _tiempo_.

    Internamente, las fechas se almacenan como valores numéricos llamados **valores de tiempo**: timestamps de Unix, divididos en 1,000 milisegundos — lo cual, bueno, sí, un tiempo Unix también necesariamente implica una fecha, claro, pero _aún así_: Date representa un tiempo, del cual puedes inferir una fecha.

    Esto se vuelve aún más frustrante cuando trabajas con aplicaciones que necesitan manejar múltiples zonas horarias (México, Colombia, Argentina, Chile, etc.) o cuando intentas parsear fechas en formato DD/MM/YYYY que es el estándar en nuestra región.

    // Timestamp Unix para el lunes, 4 de diciembre de 1995 12:00:00 AM GMT-05 (el día en que se anunció JavaScript):
    const timestamp = 818053200;
    
    console.log( new Date( timestamp * 1000 ) );
    // Resultado: Date Mon Dec 04 1995 00:00:00 GMT-0500 (hora estándar de Colombia)
    

    Palabras como “fecha” y “tiempo” significan cosas, pero, claro — _lo que sea, JavaScript_.

    Java deprecó _su_ `Date` allá por 1997, solo unos años después de que el `Date` de JavaScript fuera liberado en el mundo desprevenido; mientras tanto, hemos estado atados a este desastre desde entonces. Es salvajemente inconsistente cuando se trata de analizar fechas, como has visto hasta ahora aquí.

    No tiene sentido de zonas horarias más allá de la local y GMT, lo cual es un problema enorme para desarrolladores que trabajan con aplicaciones internacionales o que necesitan manejar usuarios en diferentes países (México tiene múltiples zonas horarias, Argentina y Chile tienen horario de verano, etc.).

    Y hablando de eso, `Date` _solo_ respeta el modelo de calendario gregoriano. No entiende en absoluto el concepto de horario de verano de manera consistente, lo cual— quiero decir, bueno, sí, igual, pero yo no estoy _hecho de computadoras_.

    Todas estas deficiencias hacen que sea excepcionalmente común usar bibliotecas de terceros como Moment.js, date-fns o Day.js para trabajar alrededor de todo esto, algunas de las cuales son absolutamente _masivas_; un drenaje de rendimiento que ha causado daño real y medible a la web, especialmente en dispositivos móviles.

    Ninguna de estas es mi problema principal con `Date`. Mi queja es sobre más que analizar o sintaxis o “ergonomía del desarrollador” o el impacto de rendimiento en toda la web de soluciones completamente necesarias o incluso la definición de la palabra “fecha”. Mi problema con `Date` es profundo en el alma. Mi problema con `Date` es que usarlo significa _desviarse de la naturaleza fundamental del tiempo mismo_.

    Todos los valores primitivos de JavaScript son **inmutables**, lo que significa que los valores mismos no pueden ser cambiados. El valor numérico `3` nunca puede representar nada más que el concepto de “tres” — no puedes hacer que `true` signifique algo diferente a “verdadero”. Estos son valores con significados concretos, inquebrantables, del mundo real. Sabemos qué es tres.
    No puede ser alguna otra cosa que no sea tres. Estos tipos de datos inmutables se almacenan **por valor**, lo que significa que una variable que representa el valor numérico `3` efectivamente “contiene” — y por lo tanto se comporta como — el valor numérico `3`.

    Cuando un valor inmutable se asigna a una variable, el motor de JavaScript crea una copia de ese valor y almacena la copia en memoria:

    const theNumber = 3;
    
    console.log( theNumber );
    // Resultado: 3

    Esto encaja bien con el modelo mental común para “una variable”: `theNumber` “contiene” `3`.

    Cuando inicializamos `theOtherNumber` con el valor vinculado a `theNumber`, ese modelo mental se mantiene: una vez más se crea un `3` y se almacena en memoria. `theOtherNumber` ahora puede pensarse como que contiene su propio `3` discreto.

    const theNumber = 3;
    const theOtherNumber = theNumber;
    
    console.log( theOtherNumber );
    // Resultado: 3;

    El valor de `theNumber` no cambia cuando alteramos el valor asociado con `theOtherNumber`, por supuesto — de nuevo, estamos trabajando con dos instancias discretas de `3`.

    const theNumber = 3;
    let theOtherNumber = theNumber;
    
    theOtherNumber = 5;
    
    console.log( theOtherNumber );
    // Resultado: 5;
    
    console.log( theNumber );
    // Resultado: 3

    Cuando cambias el valor vinculado a `theOtherNumber`, no estás cambiando el `3`, estás creando un nuevo valor numérico inmutable y vinculándolo en su lugar. De ahí un error cuando intentas manipular una variable declarada usando `const`:

    const theNumber = 3;
    
    theNumber = 5;
    // Resultado: Uncaught TypeError: invalid assignment to const 'theNumber'

    No puedes cambiar la vinculación de un `const`, y _definitivamente_ no puedes alterar el significado de `3`.

    Los tipos de datos que _pueden_ ser cambiados después de ser creados son **mutables**, lo que significa que el valor de datos _mismo_ puede ser alterado. Los valores de objeto — cualquier valor no primitivo, como un array, map o set — son mutables.

    Los objetos se almacenan **por referencia**, lo que significa que una variable que representa un objeto no contiene el objeto en sí, sino una referencia a la ubicación en memoria donde se almacena ese objeto. Cuando asignas un objeto a una variable, el motor de JavaScript almacena una referencia a ese objeto en memoria, no una copia del objeto mismo.

    const theObject = { value: 3 };
    
    console.log( theObject );
    // Resultado: { value: 3 }

    Cuando inicializamos `theOtherObject` con el valor vinculado a `theObject`, estamos creando una nueva referencia que apunta a la misma ubicación en memoria que `theObject`. Ambos `theObject` y `theOtherObject` ahora “apuntan” al mismo objeto en memoria.

    const theObject = { value: 3 };
    const theOtherObject = theObject;
    
    console.log( theOtherObject );
    // Resultado: { value: 3 };

    Cuando alteramos el objeto referenciado por `theOtherObject`, estamos alterando el mismo objeto en memoria que `theObject` también está referenciando. Ambos `theObject` y `theOtherObject` ahora apuntan a un objeto con un valor diferente.

    const theObject = { value: 3 };
    const theOtherObject = theObject;
    
    theOtherObject.value = 5;
    
    console.log( theOtherObject );
    // Resultado: { value: 5 };
    
    console.log( theObject );
    // Resultado: { value: 5 }

    Esto es lo que significa trabajar con valores mutables: cuando alteras el objeto, estás alterando el valor real que representa, no solo la variable que lo referencia.

    Y aquí está el problema con `Date`: `Date` es un objeto, lo que significa que es mutable. Cuando creas una instancia de `Date`, estás creando un objeto que representa un momento específico en el tiempo, y ese objeto puede ser alterado después de su creación.

    
    const today = new Date();
    
    console.log( today );
    // Resultado: Date Wed Dec 31 2025 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    const tomorrow = today;
    tomorrow.setDate( today.getDate() + 1 );
    
    console.log( tomorrow );
    // Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    
    console.log( today );
    // Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
    

    ¡Oh no! `today` también cambió, porque `tomorrow` y `today` son referencias al mismo objeto `Date` en memoria. Cuando llamamos `setDate` en `tomorrow`, estamos alterando el mismo objeto que `today` está referenciando.

    Esto es un problema fundamental. El tiempo no funciona así. El tiempo es inmutable. El 31 de diciembre de 2025 siempre será el 31 de diciembre de 2025.
    No puedes “cambiar” el 31 de diciembre de 2025 para que sea el 1 de enero de 2026 — esos son dos momentos diferentes en el tiempo, y no puedes hacer que uno se convierta en el otro.

    Imagínate esto en una aplicación de e-commerce donde calculas fechas de entrega, o en un sistema de reservas donde manejas fechas de check-in y check-out. Un bug así puede costarte clientes y dinero real.

    Pero `Date` te permite hacer exactamente eso. Puedes tomar un objeto `Date` que representa el 31 de diciembre de 2025 y “cambiarlo” para que represente el 1 de enero de 2026, y eso es simplemente incorrecto. Es una violación de cómo funciona el tiempo en el mundo real.

    const today = new Date();
    
    const addDay = theDate => {
    	theDate.setDate( theDate.getDate() + 1 );
    	return theDate;
    };
    
    console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
    // Resultado: Mañana será 1/1/2026. Hoy es 1/1/2026.
    

    Esto es un desastre. `today` y el resultado de `addDay( today )` son el mismo objeto, por lo que ambos muestran la misma fecha. Esto no es cómo debería funcionar el tiempo.

    El tiempo es inmutable. No puedes cambiar el pasado, no puedes cambiar el presente, y ciertamente no puedes cambiar el futuro simplemente alterando un objeto en memoria. El tiempo simplemente no funciona así.

    Y aquí está la cosa: esto no es solo un problema filosófico. Esto causa errores reales en código real. Es fácil crear accidentalmente múltiples referencias al mismo objeto `Date` y luego alterar ese objeto de maneras que afectan todas esas referencias, lo que lleva a bugs sutiles y difíciles de rastrear.

    Entra `Temporal`.

    `Temporal` es una propuesta para una nueva API de JavaScript para trabajar con fechas y tiempos. Es una reescritura completa de cómo JavaScript maneja el tiempo, y está diseñada para abordar todos los problemas con `Date` que hemos estado discutiendo.

    Lo más importante: `Temporal` es inmutable. Cuando creas un objeto `Temporal`, ese objeto representa un momento específico en el tiempo, y ese momento no puede ser cambiado. Cuando quieres representar un momento diferente en el tiempo, creas un nuevo objeto `Temporal`.

    const today = Temporal.Now.plainDateISO();
    
    console.log( today );
    // Resultado: Temporal.PlainDate 2025-12-31
    
    const tomorrow = today.add({ days: 1 });
    
    console.log( tomorrow );
    // Resultado: Temporal.PlainDate 2026-01-01
    
    console.log( today );
    // Resultado: Temporal.PlainDate 2025-12-31
    

    ¡Perfecto! `today` sigue siendo el 31 de diciembre de 2025, y `tomorrow` es el 1 de enero de 2026. No hay confusión, no hay efectos secundarios inesperados, no hay violación de la naturaleza fundamental del tiempo.

    `Temporal` también aborda muchos de los otros problemas con `Date`:

    **Análisis consistente**: `Temporal` tiene un análisis de fechas mucho más predecible y consistente que `Date`.

    **Soporte de zonas horarias**: `Temporal` tiene soporte completo para zonas horarias, no solo la zona horaria local y GMT.

    **Soporte de calendarios**: `Temporal` puede trabajar con diferentes sistemas de calendario, no solo el calendario gregoriano.

    **API más clara**: `Temporal` tiene una API mucho más clara e intuitiva que `Date`.

    Pero lo más importante es que `Temporal` respeta la naturaleza inmutable del tiempo. Cuando trabajas con `Temporal`, estás trabajando con valores que representan momentos específicos en el tiempo, y esos valores no pueden ser cambiados. Si quieres representar un momento diferente en el tiempo, creas un nuevo valor `Temporal`.

    Esto es cómo debería funcionar el tiempo en programación. El tiempo es inmutable en el mundo real, y debería ser inmutable en nuestro código también.

    `Temporal` todavía está en desarrollo, pero ya está disponible en las versiones más recientes de Chrome y Firefox. Pronto estará disponible en todos los navegadores principales, y finalmente podremos dejar `Date` atrás y usar una API de tiempo que realmente respete cómo funciona el tiempo.

    Para desarrolladores, esto significa poder trabajar con zonas horarias de manera nativa sin depender de bibliotecas pesadas, manejar formatos de fecha locales (DD/MM/YYYY) de forma consistente, y evitar esos bugs raros que aparecen cuando trabajas con fechas en aplicaciones internacionales.

    const today = Temporal.Now.plainDateISO();
    
    // Fecha local actual:
    console.log( today );
    /* Resultado (expandido):
    Temporal.PlainDate 2025-12-30
    	<prototype>: Object { … }
    */
    
    // Año local actual:
    console.log( today.year );
    // Resultado: 2025
    
    // Fecha y hora local actual:
    console.log( today.toPlainDateTime() );
    /* Resultado (expandido):
    Temporal.PlainDateTime 2025-12-30T00:00:00
    	<prototype>: Object { … }
    */
    
    // Especificar que esta fecha representa la zona horaria America/Mexico_City:
    console.log( today.toZonedDateTime( "America/Mexico_City" ) );
    /* Resultado (expandido):
    Temporal.ZonedDateTime 2025-12-30T00:00:00-06:00[America/Mexico_City]
    	<prototype>: Object { … }
    */
    
    // O trabajar con otras zonas horarias latinoamericanas:
    console.log( today.toZonedDateTime( "America/Bogota" ) ); // Colombia
    console.log( today.toZonedDateTime( "America/Buenos_Aires" ) ); // Argentina
    console.log( today.toZonedDateTime( "America/Santiago" ) ); // Chile
    
    // Agregar un día a esta fecha:
    console.log( today.add({ days: 1 }) );
    /*
    Temporal.PlainDate 2025-12-31
    	<prototype>: Object { … }
    */
    
    // Agregar un mes y un día a esta fecha, y restar dos años:
    console.log( today.add({ months: 1, days: 1 }).subtract({ years: 2 }) );
    /*
    Temporal.PlainDate 2024-01-31
    	<prototype>: Object { … }
    */
    
    console.log( today );
    /* Resultado (expandido):
    Temporal.PlainDate 2025-12-30
    	<prototype>: Object { … }
    */

    Observa cómo ninguna de estas transformaciones requirió que manualmente creáramos nuevos objetos, _y_ que el valor del objeto referenciado por `today` permanece sin cambios. A diferencia de `Date`, los métodos que usamos para interactuar con un objeto `Temporal` resultan en objetos `Temporal` _nuevos_, en lugar de requerir que los usemos en el contexto de una nueva instancia o modificar la instancia con la que estamos trabajando — que es cómo podemos encadenar los métodos `add` y `subtract` juntos en `today.add({ months: 1, days: 1 }).subtract({ years: 2 })`.

    Claro, todavía estamos trabajando con objetos, y eso significa que estamos trabajando con estructuras de datos mutables que representan valores del mundo real:

    const today = Temporal.Now.plainDateISO();
    
    today.someProperty = true;
    
    console.log( today );
    
    /* Resultado (expandido):
    Temporal.PlainDate 2026-01-05
    	someProperty: true
    	<prototype>: Object { … }
    */

    …Pero el valor representado por ese objeto `Temporal` no está destinado a ser cambiado durante el curso normal de interactuar con él — aunque el objeto sigue siendo esencialmente mutable, no estamos atrapados usando ese objeto de maneras que podrían alterar lo que significa en términos de fechas y tiempos del mundo real.
    Lo acepto.

    Entonces, revisemos ese pequeño script “hoy es X, mañana es Y” que escribimos usando `Date` anteriormente. Primero, lo arreglaremos asegurándonos de que estamos trabajando con dos instancias discretas de `Date` en lugar de modificar la instancia que representa la fecha de hoy:


    const today = new Date();
    
    const addDay = theDate => {
    	const tomorrow = new Date();
    
    	tomorrow.setDate( theDate.getDate() + 1 );
    	return tomorrow;
    };
    
    console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
    // Resultado: Mañana será 1/1/2026. Hoy es 12/31/2025.
    

    Gracias, lo odio.

    Bien, está bien. Cumple su función, tal como lo ha hecho desde el día en que `Date` se abrió paso por primera vez en la web. No estamos alterando sin saberlo el valor de `today` ya que estamos creando una nueva instancia de `Date` dentro de nuestra función `addDay` — verboso, pero funciona, como lo ha hecho durante décadas ahora. Le agregamos `1`, que tenemos que simplemente _saber_ que significa agregar un _día._

    Luego en nuestro template literal necesitamos seguir empujando a JavaScript para que nos dé la fecha en un formato que no incluya la hora actual, como una cadena. Es funcional, pero verboso.

    Ahora, rehagámoslo usando `Temporal`:

    const today = Temporal.Now.plainDateISO();
    
    console.log(`Mañana será ${ today.add({ days: 1 }) }. Hoy es ${ today }.`);
    // Resultado: Mañana será 2026-01-01. Hoy es 2025-12-31.
    

    Ahora sí estamos hablando.

    _Mucho mejor_. Más delgado, más eficiente, y _mucho_ menos margen para error. Queremos la fecha de hoy sin la hora, y el objeto que resulta de invocar `plainDateISO` (y cualquier nuevo objeto `Temporal` creado a partir de él) retendrá ese formato _sin_ ser coaccionado a una cadena.

    Formato: _verificado_.

    Queremos generar un valor que represente la fecha de hoy más un día, y queremos hacerlo de una manera donde estamos diciendo inequívocamente “agregar un día” sin conjeturas de análisis: _verificado_ y _verificado_.

    Lo más importante, no queremos correr el riesgo de que nuestro objeto `today` original sea alterado sin intención — porque el resultado de llamar al método `add` siempre será un nuevo objeto `Temporal`: _verificado_.

    `Temporal` va a ser una _mejora masiva_ sobre `Date`, y solo digo “va a ser” porque todavía no está completamente listo para uso en producción.

    La especificación de borrador para el objeto `Temporal` propuesto ha alcanzado la etapa tres del proceso de estandarización, lo que significa que ahora está oficialmente “recomendado para implementación” — aún no es parte del estándar que informa el desarrollo continuo de JavaScript mismo, pero lo suficientemente cerca como para que los navegadores puedan empezar a experimentar con él.

    Eso significa que los resultados de esa experimentación temprana pueden usarse para refinar aún más la especificación, por lo que nada está escrito en piedra todavía. Los estándares web son un proceso iterativo, después de todo.

    Ahí es donde entramos tú y yo. Ahora que `Temporal` ha llegado a las versiones más recientes de Chrome y Firefox — y otros, pronto — es hora de que entremos y probemos un poco. Puede que no hayamos tenido ninguna opinión sobre `Date`, pero podemos experimentar con `Temporal` antes de que lleguen las implementaciones finales.

    Pronto, JavaScript tendrá un manejo de fechas sensato y moderno, y finalmente podremos meter `Date` muy atrás en el cajón de chatarra con las bandas elásticas, tapas de frascos sin pareja, llaves misteriosas y probablemente pilas AA medio vacías — todavía presente, todavía una parte inexorable de la plataforma web, pero ya no nuestra primera, última y única forma de manejar fechas.

    Y solo tuvimos que esperar— bueno, espera, déjame calcular los números rápidamente con `Temporal`:

    Pruébalo

    const today = Temporal.Now.plainDateISO();
    const jsShipped = Temporal.PlainDate.from( "1995-12-04" );
    const sinceDate = today.since( jsShipped, { largestUnit: 'year' });
    
    console.log( `${ sinceDate.years } años, ${ sinceDate.months } meses, y ${ sinceDate.days } días.` );
    

    Ejecutar

    Claro, el mejor momento para reemplazar `Date` habría sido allá por 1995, pero oye: el segundo mejor momento es `Temporal.Now`, ¿verdad?

    ## ¿Por Qué Esto Importa para Desarrolladores en Latinoamérica?

    Como desarrolladores latinoamericanos, trabajamos constantemente con:

    **Aplicaciones internacionales** que necesitan manejar múltiples zonas horarias

    **Formatos de fecha locales** (DD/MM/YYYY) que `Date` maneja de forma inconsistente

    **Dispositivos móviles** donde el rendimiento importa y las bibliotecas pesadas de fechas afectan la experiencia del usuario

    **E-commerce y sistemas de reservas** donde un error con fechas puede costar dinero real

    `Temporal` viene a resolver todos estos problemas de forma nativa, sin necesidad de bibliotecas externas pesadas. Es hora de empezar a experimentar con esta nueva API y prepararnos para el futuro del manejo de fechas en JavaScript.

    **¿Te resultó útil este artículo?** Compártelo con otros desarrolladores que también están luchando con `Date`. Y si quieres profundizar más en JavaScript moderno, asegúrate de seguir aprendiendo sobre las nuevas características del lenguaje.

  • Guía completa para hacer WordPress headless con Next.js

    Guía completa para hacer WordPress headless con Next.js

    Cómo hacer mi WordPress headless ? Tutorial completo paso a paso

    Tiempo estimado de lectura: 8 min

    • Backend: WordPress como contenido (REST o GraphQL).
    • Frontend: Next.js (SSG/ISR/SSR) o cualquier framework moderno.
    • Integración: peticiones al endpoint WP, manejo de imágenes, SEO y auth.
    • Automatización: webhooks y n8n para builds y regeneración on-demand.

    Referencias útiles: WPGraphQL, WP REST API, Next.js, n8n, Vercel.

    Resumen rápido (para IA y lectores con prisa)

    Este tutorial proporciona una guía práctica para transformar WordPress en un CMS headless utilizando Next.js como frontend. Se abordan temas como la elección entre REST y GraphQL, la optimización de fetch, las imágenes, el SEO y como desplegar el sistema.

    Paso 1 — Preparar el backend (WordPress)

    1. Instala WordPress en un entorno controlado (subdominio o local).
    2. Decide API: REST nativa o GraphQL. Recomiendo WPGraphQL para consultas precisas.
    3. Plugins recomendados:
      • WPGraphQL (o la REST API nativa si prefieres).
      • WPGraphQL for Advanced Custom Fields (si usas ACF).
      • Plugin de Headless Mode (opcional) para evitar duplicidad de frontend.
    4. Prueba tu endpoint: /wp-json/ para REST o /graphql para GraphQL.
    query GetPosts {
      posts(first: 10) {
        nodes { slug title excerpt date featuredImage { node { sourceUrl } } }
      }
    }

    Paso 2 — Configurar frontend con Next.js

    Crea la app Next.js (App Router recomendado):

    npx create-next-app@latest mi-wp-headless --typescript
    cd mi-wp-headless

    Variables de entorno (.env.local):

    NEXT_PUBLIC_WORDPRESS_URL=https://api.tudominio.com
    NEXT_PUBLIC_GRAPHQL_ENDPOINT=https://api.tudominio.com/graphql

    Instala Apollo si usas GraphQL:

    npm install @apollo/client graphql

    Paso 3 — Conexión y fetching eficiente

    Puedes usar fetch nativo o Apollo. Ejemplo ligero con fetch en Server Component (App Router):

    export async function fetchAPI(query: string, variables = {}) {
      const res = await fetch(process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT!, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query, variables }),
        next: { revalidate: 60 } // ISR básico
      });
      const json = await res.json();
      if (json.errors) throw new Error(JSON.stringify(json.errors));
      return json.data;
    }

    En app/page.tsx (Server Component):

    const data = await fetchAPI(`query { posts(first:10){ nodes{ slug title excerpt } } }`);

    Usa Server Components para minimizar JS en cliente y activa ISR con `next: { revalidate }` o `revalidate` en getStaticProps según tu versión Next.js.

    Paso 4 — Build, ISR y despliegue

    • Para contenido estático estable: SSG.
    • Para catálogo con alta lectura: ISR (`revalidate: 60` o ISR on-demand).
    • Despliega frontend en Vercel.
    • Despliega backend en hosting WP gestionado o VPS y protege el endpoint.

    Para ISR on-demand (regeneración específica) usa la API de revalidate de tu hosting (Vercel) o implementa webhook que dispare un job.

    Puntos críticos: imágenes, SEO, autenticación y previews

    • Imágenes: configura dominios externos en next.config.js y usa <Image />.
    • SEO: extrae meta (Yoast/RankMath) desde WP y monta tags en <head> (JSON-LD, og, canonical).
    • Auth: para endpoints privados o preview, usa JWT o NextAuth con credenciales WP.
    • Previews: implementa rutas de preview que verifiquen token y muestren contenido no publicado.

    Automatización y flujos productivos (n8n, agentes, Dominicode Labs)

    Desacoplar WP abre automatización real: webhooks en publish -> n8n recibe evento -> desencadena:

    • ISR on-demand para páginas afectadas,
    • comprobaciones de consistencia (links, imágenes),
    • generación de snippets sociales por agentes IA.

    En Dominicode Labs documentamos plantillas y workflows que conectan WordPress con n8n y despliegues automáticos. Ofrecemos:

    • Flujos n8n listos para ISR on-demand y pruebas de integridad.
    • Ejemplos de agentes que generan borradores y los inyectan vía GraphQL.

    Revisa: Dominicode Labs

    Conclusión práctica

    Hacer tu WordPress headless significa más control y mejor rendimiento, pero también más superficie técnica: CI/CD, caching, imágenes, SEO y auth. Empieza por un prototipo: WPGraphQL + Next.js en SSG/ISR, añade webhooks para regeneración y automatiza los builds críticos. Mide (Lighthouse, RUM) y ajusta revalidación según tráfico y consistencia requerida.

    Si buscas ejemplos de pipelines y flujos productivos ya probados, Dominicode Labs ofrece plantillas y documentación para acelerar la migración sin inventar la rueda.

    FAQ

    ¿Qué es WordPress headless?

    WordPress headless se refiere a configurar WordPress solo como un backend para gestión de contenido, mientras que se utiliza un frontend separado para la presentación visual, permitiendo usar tecnologías modernas.

    ¿Por qué utilizar GraphQL en vez de REST?

    GraphQL permite realizar consultas más precisas y eficientes en comparación con REST, ya que se pueden solicitar exactamente los datos que se necesitan, evitando cargas innecesarias.

    ¿Cómo optimizar el fetching de datos?

    Se puede optimizar el fetching usando técnicas como ISR (Incremental Static Regeneration) y utilizando fetch con revalidación en el backend para asegurar que se obtienen datos frescos sin sacrificar rendimiento.

    ¿Qué es ISR y cuándo deberíamos utilizarlo?

    ISR o Incremental Static Regeneration permite regenerar contenido estático en intervalos específicos, ideal para páginas que requieren actualizaciones frecuentes pero que son costosas en términos de rendimiento si se renderizan en cada solicitud.

    ¿Cómo manejar la autenticación en un CMS headless?

    La autenticación en un CMS headless se puede manejar mediante el uso de JWT (JSON Web Tokens) o NextAuth, permitiendo la integración de credenciales para acceder a datos privados o funcionalidad de previews.

  • El peligro de estudiar sin aplicar en desarrollo

    El peligro de estudiar sin aplicar en desarrollo

    Estás estudiando demasiado… y por eso no progresas

    Tiempo estimado de lectura: 10 min

    • El estudio sin implementación real limita el progreso.
    • Confundir consumo de información con competencia técnica es un error común.
    • Buscar certeza antes de actuar resulta en estancamiento.
    • La práctica deliberada es crucial para el aprendizaje efectivo.
    • La construcción de proyectos reales enriquece la experiencia de aprendizaje.

    Tabla de contenidos

    Estás estudiando demasiado… y por eso no progresas. Suena contradictorio, pero es un patrón muy común en developers, tech leads y builders que intentan “ponerse al día” con IA, automatización, frameworks, arquitectura o n8n. Cuanto más lees, más tutoriales guardas y más cursos empiezas, menos construyes. Y sin construcción real, tu criterio no madura, tu confianza no mejora y tu carrera se estanca.

    Este artículo no va de motivación ni de “disciplina”. Va de entender por qué el estudio sin un sistema de aplicación se convierte en evasión productiva, cómo detectarlo con señales objetivas y cómo cambiarlo con un método operativo (no inspirational): estudio mínimo útil, práctica deliberada y proyectos que te obliguen a tomar decisiones.

    Por qué “estás estudiando demasiado… y por eso no progresas”

    El problema no es estudiar. El problema es estudiar sin cerrar el ciclo:

    Información → comprensión → decisión → implementación → feedback → ajuste

    Cuando solo haces las dos primeras (información y comprensión), obtienes sensación de avance sin el coste real de avanzar: equivocarte en código, tomar decisiones, romper cosas, medir, refactorizar.

    1) Confundes consumo con competencia

    Leer sobre testing no te hace mejor testeando. Ver un vídeo sobre Clean Architecture no te hace mejor diseñando. Estudiar “agentes” no te hace mejor construyendo flujos con herramientas reales. La competencia técnica se forma cuando:

    • eliges trade-offs con restricciones reales (tiempo, deuda técnica, equipo, legacy)
    • implementas y ves consecuencias
    • corriges con feedback (errores, métricas, revisiones, incidentes)

    Si tu progreso se mide por “horas estudiadas” o “cursos completados”, estás midiendo input, no output.

    2) Estás buscando certeza antes de actuar (y no existe)

    Mucho estudio es una forma elegante de evitar el riesgo. El cerebro te pide garantías: “cuando entienda bien X, empiezo”. Pero en ingeniería, la certeza llega después de implementar la primera versión y ver qué falla.

    En el mundo real:

    • Aprendes observabilidad cuando tu servicio se cae.
    • Aprendes colas cuando tu API no aguanta picos.
    • Aprendes prompts cuando tu pipeline alucina en producción.

    No es romanticismo. Es cómo funciona el aprendizaje en sistemas complejos.

    3) Te estás dopando con novedad

    El contenido técnico está optimizado para enganchar: “lo nuevo”, “lo que viene”, “la librería definitiva”. Saltar de tema en tema mantiene la dopamina alta y la incomodidad baja. Construir un proyecto real hace lo contrario: te enfrenta a fricción, bugs y límites.

    El síntoma típico: “Estoy aprendiendo mucho” pero no puedes señalar una mejora verificable en tus entregables de los últimos 30 días.

    4) Estás evitando el trabajo que duele (pero te hace crecer)

    Hay tareas que hacen crecer rápido y casi nadie quiere hacer:

    • escribir tests de verdad para código legacy
    • instrumentar logs/métricas/tracing
    • refactorizar sin romper contratos
    • documentar decisiones (ADRs)
    • diseñar APIs con backward compatibility
    • mantener un workflow en producción (no un demo)

    Estudiar es más cómodo porque no exige exponerte a evaluación: un PR, un incidente, un review, una métrica.

    Señales objetivas de que estás atrapado en “estudio infinito”

    No es un juicio moral. Son indicadores prácticos:

    Señal A: no produces artefactos

    En 2–4 semanas deberías poder señalar al menos uno:

    • PR mergeado
    • script o tool interna útil
    • workflow automatizado en tu equipo
    • mejora de rendimiento medida
    • test suite ampliada con cobertura significativa en módulos críticos
    • documentación de arquitectura que el equipo usa

    Si no hay artefactos, tu estudio no está aterrizando.

    Señal B: cambias de roadmap cada semana

    “Ahora me voy a centrar en…”. Si tu foco cambia antes de que exista un output, estás comprando la ilusión de que el siguiente tema sí te desbloqueará.

    Señal C: consumes más de lo que implementas

    Un ratio simple:

    • Horas de implementación / horas de consumo
    • Si estás por debajo de 1:1 durante semanas, algo va mal.
    • En fases de crecimiento sano suele ser 2:1 o 3:1 (más implementación que consumo).

    Señal D: tu stack mental está lleno, pero tu stack de código no

    Sabes explicar conceptos pero no tienes “músculo” de ejecución: configurar, desplegar, depurar, instrumentar, mantener.

    Cómo progresar: un sistema operativo de aprendizaje (no “más ganas”)

    El antídoto no es “estudia menos”. Es estudiar con restricciones y con una unidad mínima de entrega.

    1) Define una “unidad de progreso” verificable

    Ejemplos para un developer:

    • “Implementar un endpoint con tests + métricas + docs”
    • “Crear un workflow en n8n que procese X y tenga alertas”
    • “Reducir el tiempo de build un 20% con cambios medidos”
    • “Automatizar una tarea repetitiva del equipo y medir tiempo ahorrado”

    Si no es verificable, es humo.

    2) Usa la regla 20/80 del estudio: solo lo necesario para ejecutar

    Estudio mínimo viable:

    • Documentación oficial cuando aplica (no 10 vídeos)
    • Un ejemplo de referencia
    • Una prueba rápida (spike) de 30–60 minutos
    • Luego implementación real

    La pregunta guía no es “¿entiendo esto?”. Es:

    “¿Tengo suficiente para tomar la siguiente decisión e implementarla?”

    3) Convierte todo aprendizaje en un deliverable pequeño (en 48–72 horas)

    Si no puedes convertir lo estudiado en algo implementado en 2–3 días, el scope es demasiado grande o estás estudiando por evasión.

    Ejemplos de deliverables pequeños:

    • un repo con un caso real y README honesto
    • un PR con una mejora puntual (y tests)
    • un workflow automatizado con logs y manejo de errores
    • un dashboard mínimo para visualizar una métrica

    4) Practica deliberada: repite lo difícil, no lo divertido

    La práctica deliberada se centra en el borde de tu habilidad. En software, suele ser:

    • depuración sistemática
    • diseño de interfaces y contratos
    • resiliencia: retries, idempotencia, rate limits
    • pruebas: unitarias, integración, contract tests
    • observabilidad y diagnóstico

    Haz un inventario: ¿qué evitas siempre? Eso es el gimnasio.

    5) Introduce feedback real (sin feedback no hay aprendizaje)

    Tres fuentes de feedback que sí cuentan:

    • Producción: errores, latencias, incidentes, costes
    • Código revisado: PRs con comentarios concretos
    • Usuarios internos: soporte, operaciones, ventas, el equipo

    El feedback de “me siento más seguro” es secundario. Lo que cuenta es lo que el sistema devuelve.

    Ejemplo realista: IA aplicada y automatización (donde estudiar demasiado es una trampa)

    En IA aplicada el problema se magnifica. Hay exceso de contenido y cambios constantes. La progresión real no viene de “estar al día”, sino de montar pipelines que sobrevivan a:

    • entradas sucias
    • ambigüedad
    • costes variables
    • alucinaciones
    • latencias
    • compliance y privacidad

    Si estás “aprendiendo agentes” pero no has implementado:

    • un sistema de evaluación (tests de prompts, golden datasets)
    • observabilidad (logs estructurados de inputs/outputs, trazas)
    • control de costes (presupuestos, caching, batch)
    • guardrails (validación, esquemas, verificación)

    …entonces estás en consumo, no en ingeniería.

    Aquí es donde tiene sentido trabajar con un enfoque de laboratorio aplicado. En Dominicode Labs ayudamos a equipos y builders a pasar de “conceptos de IA/automatización” a sistemas productivos: workflows en n8n, agentes con criterios de fiabilidad, observabilidad y mantenimiento, y automatizaciones que realmente ahorran tiempo (con métricas y ownership claro). No es consultoría de slides: es implementación con criterio.

    Qué estudiar (y qué dejar de estudiar) según tu etapa

    Si eres junior / mid: menos teoría general, más fundamentos aplicados

    Prioriza:

    • Git fluido, debugging, herramientas del runtime
    • testing básico pero constante
    • HTTP, APIs, DBs (índices, transacciones)
    • leer código ajeno y refactorizar con seguridad

    Reduce:

    • arquitectura “de libro” sin contexto
    • debates de frameworks como identidad personal
    • maratones de cursos sin proyectos

    Si eres senior: menos “novedades”, más sistemas operables

    Prioriza:

    • observabilidad y confiabilidad
    • diseño de interfaces y contratos
    • estrategias de migración (legacy, incremental)
    • performance con medición
    • incident response y postmortems

    Reduce:

    • cambiar de stack por moda
    • sobre-optimizar antes de medir
    • acumular “conocimiento declarativo” no aplicable

    Si eres tech lead / founder: estudia lo que reduce riesgo y acelera entrega

    Prioriza:

    • sistemas de delivery (CI/CD, entornos, releases)
    • automatización de procesos internos
    • métricas de negocio conectadas a ingeniería
    • coste total: infra + tiempo + mantenimiento

    Reduce:

    • perfeccionismo en tecnología no diferencial
    • “aprender por aprender” sin impacto

    La barrera real: no es falta de información, es falta de decisiones

    El progreso técnico se destraba cuando tomas decisiones con información incompleta, y te responsabilizas del resultado. Estudiar infinito suele ocultar una de estas fricciones:

    • miedo a equivocarte públicamente
    • miedo a escoger mal (stack, patrón, herramienta)
    • falta de un problema real (aprendes sin necesidad)
    • falta de ownership (nadie te exige entregar)

    Solución práctica: elige un problema real y conviértelo en un proyecto con fecha.

    No “voy a aprender microservicios”, sino:

    “Voy a extraer este módulo a un servicio, con contrato versionado, métricas y rollback”

    No “voy a aprender n8n”, sino:

    “Voy a automatizar el alta de clientes: formulario → validación → CRM → correo → seguimiento, con reintentos y alertas”

    Checklist operativo: si mañana quieres salir del bucle de estudio

    1. Elige un tema (uno) para 2 semanas.
    2. Define un deliverable que se pueda usar (aunque sea interno).
    3. Pon una fecha de entrega corta (72h para primera versión).
    4. Estudia solo lo que desbloquea el siguiente paso.
    5. Implementa con logs, manejo de errores y documentación mínima.
    6. Pide una review o usa el sistema en un caso real.
    7. Escribe un postmortem: qué falló, qué aprendiste, qué harías distinto.
    8. Repite con scope ligeramente mayor.

    Ese ciclo crea progreso acumulativo y criterio. Y el criterio —no el consumo— es lo que te hace subir de nivel.

    Cierre: estudia como ingeniero, no como espectador

    Estás estudiando demasiado… y por eso no progresas cuando el estudio se vuelve un sustituto elegante de la ejecución. La salida no es abandonar el aprendizaje, sino reconstruirlo alrededor de entrega y feedback.

    Si te quedas con una idea: en software, lo que no pasa por implementación y mantenimiento es solo opinión informada. El progreso real empieza cuando conviertes conocimiento en decisiones y decisiones en sistemas que funcionan.

    FAQ

    ¿Por qué estudiar sin aplicar no ayuda a progresar?

    Estudiar sin aplicar genera una falsa sensación de avance. El verdadero aprendizaje proviene de tomar decisiones y enfrentar errores en situaciones reales.

    ¿Cómo saber si estoy estudiando demasiado?

    Puedes identificar que estudias demasiado si no produces artefactos, cambias constantemente de enfoque o consumes más información de la que implementas.

    ¿Qué es un sistema operativo de aprendizaje?

    Un sistema operativo de aprendizaje se basa en establecer unidades de progreso verificables y aplicar lo aprendido en plazos cortos y con feedback constante.

    ¿Por qué es importante la práctica deliberada?

    La práctica deliberada permite enfocarse en las áreas difíciles que necesitas mejorar, aumentando así tu competencia técnica.

    ¿Cómo introducir feedback real en el aprendizaje?

    Introduce feedback real mediante la revisión de código, el aprendizaje de incidentes en producción y la recopilación de métricas durante el desarrollo.

  • Diferencias Clave Entre Local Storage y Cookies

    Diferencias Clave Entre Local Storage y Cookies

    Qué diferencias hay Local Storage vs Cookies, cuándo usar uno y otro

    Tiempo estimado de lectura: 6 min

    • Las cookies viajan con cada petición HTTP, mientras que Local Storage no lo hace.
    • Cada tecnología tiene implicaciones de seguridad, rendimiento y experiencia de usuario.
    • Cuando usar cada uno depende de las necesidades específicas de almacenamiento.

    Tabla de contenidos

    Qué hace cada una (resumen técnico)

    • Cookies: pequeños pares clave‑valor (≈4 KB) diseñados para ser enviados automáticamente por el navegador en cada request al servidor. Se pueden crear desde el servidor (Set‑Cookie) o desde cliente. Soportan flags: HttpOnly, Secure, SameSite. (Docs: MDN — Cookies)
    • Local Storage: API Web Storage (HTML5). Almacén key/value en el navegador, persistente, mayor capacidad por dominio (5–10 MB típicos). Solo accesible desde JavaScript — nunca se envía automáticamente al servidor. (MDN — localStorage)

    Diferencias que importan en producción

    1. Transmisión y ancho de banda:
      • Cookies: se adjuntan en headers HTTP; cada KB añadida penaliza todas las requests. Evita almacenar grandes blobs en cookies.
      • Local Storage: no afecta tráfico; ideal para caché UI y datos voluminós.
    2. Persistencia y scope:
      • Cookies: caducidad configurable, scope por dominio/path y subdominios.
      • Local Storage: persistencia indefinida hasta borrado manual, scope por origen (protocol + host + port).
    3. Seguridad (XSS vs CSRF) — el punto crítico:
      • Cookies con HttpOnly impiden lectura por JavaScript: buen remedio contra robo de tokens vía XSS.
      • Local Storage es totalmente legible por JS: si hay XSS, el atacante puede exfiltrar cualquier dato allí guardado.
      • Cookies se envían automáticamente → riesgo CSRF salvo que uses SameSite, tokens CSRF o estrategias de double‑submit. (Guía SameSite) Para entender XSS/CSRF: OWASP tiene guías prácticas — XSS CSRF

    Reglas prácticas: cuándo usar cada uno

    Usa Cookies cuando:

    • Guardas tokens de autenticación o session IDs sensibles. Implementa HttpOnly + Secure + SameSite donde aplique.
    • Necesitas que el servidor reconozca automáticamente al cliente en cada petición.
    • Requieres expiración y control centralizado del ciclo de sesión.

    Usa Local Storage cuando:

    • Guardas preferencias de UI, temas, estado de formularios, pequeños cachés que mejoran UX.
    • Necesitas almacenamiento más grande y rápido sin impactar la red (listas estáticas, drafts).
    • No guardas secretos ni tokens que comprometan cuentas si se filtran.

    Ejemplos concretos

    Guardar preferencia de tema (Local Storage):

    localStorage.setItem('theme', 'dark');
    const theme = localStorage.getItem('theme');

    Setear cookie segura desde servidor (Node/Express):

    res.cookie('session', sessionId, { httpOnly: true, secure: true, sameSite: 'Lax', maxAge: 1000*60*60 });

    Evita este patrón inseguro (no lo copies): Almacenar JWT de acceso en localStorage en una app pública: fácil exfiltración si tienes XSS.

    Errores comunes que cuestan

    • Poner el token principal en localStorage “porque es más cómodo”. Resultado: una inyección de script y sesión comprometida.
    • Volcar demasiado estado en cookies y degradar las peticiones móviles.
    • No configurar SameSite o CSRF tokens cuando usas cookies, dejando la app abierta a forzados desde otros orígenes.

    Conclusión práctica

    No hay “mejor” absoluto. Pregunta primero: ¿este dato necesita viajar automáticamente al servidor y es sensible? → Cookies (bien configuradas). ¿Es estado de UI o caché que no debe hinchar el tráfico? → Local Storage. Decide por capas: auth en cookies seguras; UX y rendimiento en localStorage. Esa elección evita bugs, reduce superficie de ataque y mejora latencia en producción.

    FAQ

    ¿Cuál es la capacidad máxima de Cookies y Local Storage?

    Las cookies suelen tener un tamaño máximo de aproximadamente 4 KB, mientras que Local Storage puede almacenar entre 5 y 10 MB por dominio.

    ¿Cómo se manejan los datos en Local Storage?

    Los datos en Local Storage se manejan a través de JavaScript con métodos como setItem para agregar datos y getItem para recuperarlos, y permanecen allí hasta que se eliminen manualmente.

    ¿Qué medidas de seguridad debo tomar con Cookies?

    Debes usar flags como HttpOnly y Secure para proteger las cookies y configurar el atributo SameSite para reducir el riesgo de CSRF.

    ¿Se pueden utilizar Cookies y Local Storage juntos?

    Sí, puedes usar ambas tecnologías para diferentes propósitos en tu aplicación, como almacenar datos de sesión en cookies y preferencias de usuario en Local Storage.