Category: Blog

Your blog category

  • Scripts en Python para optimizar tu productividad semanal

    Scripts en Python para optimizar tu productividad semanal

    Scripts en Python que te ahorran horas cada semana (Casos reales)

    Tiempo estimado de lectura: 4 min

    • Automatiza lo repetitivo: pequeñas tareas con scripts te devuelven horas cada semana.
    • Enfoque pragmático: ejemplos listos para copiar, adaptar y ejecutar hoy mismo.
    • Integración: combina Python con n8n o FastAPI para orquestación sin perder flexibilidad.
    • Producción segura: logging, retries, timeouts, tests y entornos reproducibles como pasos mínimos.

    Introducción

    Si buscas “Scripts en Python que te ahorran horas cada semana (casos reales)”, estás en el lugar correcto. En las primeras líneas: este artículo muestra scripts prácticos —limpieza de datos, renombrado masivo, scraping controlado y generación de informes— que puedes copiar, adaptar y ejecutar hoy mismo para recuperar horas semanales.

    No es teoría: son patrones productivos con código mínimo, buenas prácticas y enlaces a la documentación oficial para escalar a producción.

    Resumen rápido (lectores con prisa)

    Qué es: Colección de scripts Python prácticos para tareas repetitivas.

    Cuándo usarlo: Cuando procesos manuales consumen tiempo semanalmente.

    Por qué importa: Scripts reproducibles reducen errores humanos y liberan tiempo.

    Cómo funciona: Pequeños scripts con dependencias claras, logging y orquestación via n8n o FastAPI.

    Scripts en Python que te ahorran horas cada semana: cuatro casos reales

    1) Limpieza y normalización de datos (Pandas)

    Problema: CSVs semanales con fechas mixtas, espacios, duplicados. Resultado: 30 minutos manuales por archivo.

    Solución: usar pandas para normalizar columnas, coercionar fechas y eliminar duplicados. Dependencias: pandas.

    import pandas as pd
    
    def clean_sales(input_path, output_path):
        df = pd.read_csv(input_path)
        df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
        if 'fecha_venta' in df:
            df['fecha_venta'] = pd.to_datetime(df['fecha_venta'], errors='coerce').dt.date
        df.drop_duplicates(subset=['email'], inplace=True)
        df.dropna(subset=['monto', 'email'], inplace=True)
        df.to_csv(output_path, index=False)
    

    Por qué funciona: debug determinista, reproducible y scriptable desde cron o n8n.

    2) Renombrado y organización masiva de archivos (Pathlib)

    Problema: carpeta Downloads repleta de facturas, imágenes y PDFs sin orden.

    Solución: Pathlib + shutil para mover por fecha de modificación o por patrón. Pathlib docs.

    from pathlib import Path
    import shutil
    from datetime import datetime
    
    src = Path.home() / "Downloads"
    dst = Path.home() / "Documents/Invoices"
    
    for f in src.glob("*.pdf"):
        ts = datetime.fromtimestamp(f.stat().st_mtime)
        target = dst / str(ts.year) / f"{ts.month:02d}"
        target.mkdir(parents=True, exist_ok=True)
        shutil.move(str(f), str(target / f.name))
    

    Impacto: cientos de archivos organizados en segundos, sin errores humanos.

    3) Scraping controlado y monitoreo (Requests + BeautifulSoup)

    Problema: comprobar precios o disponibilidad manualmente cada mañana.

    Solución: Requests + BeautifulSoup para sitios estáticos. Respecta robots.txt y usa headers.

    import requests
    from bs4 import BeautifulSoup
    
    url = "https://ejemplo.com/producto"
    headers = {"User-Agent": "bot@miempresa.com"}
    r = requests.get(url, headers=headers, timeout=10)
    r.raise_for_status()
    soup = BeautifulSoup(r.text, "html.parser")
    price = soup.select_one(".price").get_text(strip=True)

    Cuando la web es dinámica, usa Playwright.

    Buenas prácticas: backoff en reintentos, pausas entre requests y registro estructurado.

    4) Generación automática de informes (Jinja2 → Email/HTML/PDF)

    Problema: compilar métricas de distintas fuentes y formatearlas manualmente cada semana.

    Solución: Jinja2 para plantilla HTML + render + envío por SMTP o vía n8n. Jinja2 docs.

    from jinja2 import Environment, FileSystemLoader
    env = Environment(loader=FileSystemLoader("templates"))
    tpl = env.get_template("weekly.html")
    html = tpl.render(metrics=metrics_dict)

    Patrón:

    • Recoger datos (DB / API)
    • Renderizar plantilla Jinja2
    • Guardar HTML o convertir a PDF (weasyprint / wkhtmltopdf)
    • Enviar por correo o subir a un canal Slack

    Automatización: conviértelo en endpoint FastAPI o ejecútalo desde n8n.

    Cómo convertir un script útil en una automatización confiable

    • Logging estructurado: usa logging/structlog, evita prints.
    • Configuración externa: variables de entorno (.env), no claves hardcodeadas.
    • Retries y backoff: tenacity o patrones de retry para redes inestables.
    • Timeouts y cancelación: define límites en requests/Playwright.
    • Tests y tipo: type hints + pytest para evitar regresiones.
    • Entornos reproducibles: poetry/venv y Dockerfile.
    • Observabilidad: métricas de ejecución y alertas (errores, latencias).

    Integración práctica con n8n y workflow orquestado

    Patrón recomendado: n8n orquesta (triggering, branching, notificaciones) → tu servicio Python ejecuta la tarea pesada → devuelve JSON → n8n sigue con distribución y persistencia. Esto mantiene la UX del no-code y la potencia de Python para lo que importa.

    n8n Execute Command o un endpoint HTTP (FastAPI) son las formas más sencillas de integrar.

    Conclusión: dónde empezar hoy

    Elige la tarea que te roba más tiempo y conviértela en script. Empieza por:

    • limpieza de datos si trabajas con CSVs;
    • organización de archivos si pierdes tiempo buscando;
    • scraping si haces comprobaciones manuales diarias;
    • reportes si dedicas horas a consolidar métricas.

    Implementa logging, pruebas mínimas y despliega como función serverless o servicio ligero. Un script que hoy te ahorre 30 minutos semanales será un activo que multiplica su valor con el tiempo.

    Recursos

    Si trabajas con automatización, n8n o workflows y quieres continuar experimentando con patrones y pruebas, visita Dominicode Labs para recursos adicionales y experimentos prácticos. Es una continuación lógica para quienes combinan no-code y código en producción.

    FAQ

    ¿Qué dependencias necesito?

    Depende de la tarea: para limpieza de datos, pandas. Para scraping estático, Requests y BeautifulSoup. Para render dinámico, Playwright. Para plantillas, Jinja2.

     

    ¿Cómo integrarlo con n8n?

    Usa n8n como orquestador: trigger → ejecutar comando → recibir JSON. Puedes usar Execute Command o un endpoint HTTP (FastAPI) que reciba peticiones desde n8n y devuelva resultados procesables.

     

    ¿Qué pruebas debo escribir?

    Tests unitarios básicos con pytest y pruebas de integración para las interacciones de red o filesystem. Añade type hints y pruebas que cubran casos esperados y errores comunes (fechas inválidas, campos faltantes, timeouts).

     

    ¿Cómo manejar secretos y configuración?

    Usa variables de entorno, managers de secretos o servicios de vault. Nunca hardcodees claves en el repositorio; utiliza .env para desarrollo y soluciones seguras en producción.

     

    ¿Qué hacer si el scraping requiere JS?

    Pasa a herramientas headless como Playwright o Puppeteer. Define timeouts, espera selectores y respetar robots.txt y límites de requests.

     

    ¿Cómo convertir HTML a PDF?

    Renderiza tu plantilla Jinja2 a HTML y usa weasyprint o wkhtmltopdf para la conversión. Alternativamente genera HTML y envíalo por correo si PDF no es estrictamente necesario.

  • Aprende FastAPI: Del Frontend al Backend sin Complicaciones

    Aprende FastAPI: Del Frontend al Backend sin Complicaciones

    FastAPI explicado para devs que vienen del frontend

    Tiempo estimado de lectura: 4 min

    • Pydantic trae tipado y validación automática al backend (como Zod/TypeScript).
    • Depends ofrece inyección de dependencias limpia y reutilizable (como hooks/contexts).
    • /docs genera documentación y clientes a partir de OpenAPI automáticamente.
    • Estructura por feature (routers, servicios, schemas) evita sobre‑arquitectura temprana.
    • Async vs sync: escoge coherentemente según las librerías de I/O que uses.

    Introducción

    FastAPI explicado para devs que vienen del frontend: si trabajas con React, Next.js o Angular, lo vas a entender rápido. En pocas líneas: FastAPI trae al backend la ergonomía que ya conoces del frontend —tipado, validación automática y feedback inmediato— pero con la potencia del ecosistema Python para datos y automatización.

    Esta guía va al grano: qué partes importan de verdad, cómo estructurar proyectos reales y cómo evitar la sobre‑arquitectura que paraliza más que ayuda.

    Resumen rápido (lectores con prisa)

    FastAPI es un framework web Python que ofrece validación automática con Pydantic, inyección de dependencias con Depends y documentación OpenAPI automática. Úsalo cuando quieras productividad, tipado y APIs mantenibles sin complejidad innecesaria.

    FastAPI explicado para devs que vienen del frontend: los conceptos que importan

    Pydantic: tu Zod/TypeScript en el backend

    Pydantic define esquemas (models) que validan entrada y salida, generan JSON y alimentan la documentación OpenAPI automática. Piensa en él como Zod del servidor.

    from pydantic import BaseModel
    
    class UserCreate(BaseModel):
        username: str
        email: str
        is_admin: bool = False
    

    Si el cliente envía un string donde va un número, FastAPI responde 422. Más seguridad y menos if inútiles. Docs: Docs

    Depends: inyección limpia (como hooks)

    Depends es el equivalente a usar un hook o un context provider. Te permite inyectar autenticación, sesiones DB o configuración sin repetir código.

    from fastapi import Depends
    
    async def get_current_user(token: str):
        # decodifica JWT, consulta DB...
        return user
    
    @app.get("/me")
    async def me(user=Depends(get_current_user)):
        return {"username": user.username}
    

    Esto hace tus rutas pequeñas, testables y legibles.

    Docs automáticas: /docs es tu Postman actualizado

    FastAPI genera Swagger UI y ReDoc desde tus modelos Pydantic. Para frontend, eso significa menos sincronización manual y clientes generados a partir del esquema OpenAPI. FastAPI docs: FastAPI docs

    Cómo estructurar un proyecto (sin volverte loco)

    Estructura recomendada

    La regla: agrupa por dominio/feature, no por tipo de archivo. Es más fácil de escalar y de entender cuando el proyecto crece.

    /project
    ├─ main.py           # instancia FastAPI y monta routers
    ├─ core/
    │  ├─ config.py      # Pydantic Settings
    │  └─ db.py          # sesión/engine DB
    └─ modules/
       └─ users/
          ├─ router.py   # endpoints
          ├─ schemas.py  # Pydantic models
          ├─ service.py  # lógica de negocio
          └─ models.py   # tablas (SQLModel/SQLAlchemy)
    

    router.py solo maneja HTTP.
    service.py contiene lógica pura (sin dependencias HTTP).
    schemas.py es tu contrato con el frontend.

    Usa SQLModel si quieres combinar Pydantic + SQLAlchemy con menor fricción: SQLModel

    Async vs sync: cuándo usar async def

    Eres de frontend, ya conoces async/await. En Python:

    • Usa async def si tus librerías son asíncronas (httpx async, asyncpg).
    • Usa def si dependes de librerías bloqueantes (requests, muchos ORMs syncronous).

    FastAPI ejecuta funciones sync en un threadpool, pero mezclar async con llamadas bloqueantes puede congelar la app. Elige coherentemente la capa de I/O. httpx (async) es una buena alternativa a requests.

    Cómo no sobre‑arquitectar: reglas prácticas

    1. No crees capas de abstracción antes de necesitarlas. Si no vas a cambiar de DB a corto plazo, no inventes un IRepository.
    2. Evita micro‑paquetes para cada función. Agrupa por feature y refactoriza cuando el módulo crezca.
    3. No conviertas cada operación en un task queue desde el día uno. Añade background jobs (Celery, RQ) cuando el throughput lo exija.
    4. Usa Pydantic Settings para configuración validada y .env en desarrollo: Pydantic Settings
    5. Empieza con tests simples en service.py: funciones puras son triviales de testear con pytest.

    Deploy mínimo reproducible

    Dockerfile simple:

    FROM python:3.11-slim
    WORKDIR /app
    COPY pyproject.toml poetry.lock ./
    RUN pip install poetry && poetry install --no-root --only main
    COPY . .
    CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
    

    Usa Uvicorn para prod. Plataformas como Railway, Render o Fly.io son opciones rápidas con SSL automático.

    Pequeño checklist antes de merge

    • Modelos Pydantic definidos para input/output.
    • Dependencias reutilizables con Depends.
    • Rutas cortas: lógica en service.py, no en router.
    • Logs estructurados y errores manejados correctamente.
    • Tests para la lógica (no solo integración).

    Cierre

    FastAPI te permite pasar de frontend a backend sin perder la mentalidad de DX y tipado que ya dominas. Aprende Pydantic, usa Depends con juicio y estructura por feature. No abuses de patrones hasta que el problema los justifique. En 10 minutos tendrás un endpoint funcional; en unas pocas iteraciones, una API mantenible y profesional.

    Si te interesa explorar integraciones, automatizaciones y flujos avanzados relacionados con APIs y agentes, visita Dominicode Labs para recursos y experimentos prácticos.

    FAQ

    ¿Por qué usar Pydantic en FastAPI?

    Pydantic valida entrada y salida, genera JSON y alimenta la documentación OpenAPI. Reduce checks manuales y errores de tipo, mejorando la seguridad y la ergonomía del desarrollo.

    ¿Cuándo debo usar Depends?

    Usa Depends para autenticación, sesiones DB, configuración u otras dependencias reutilizables. Mantiene las rutas limpias y facilita testing.

    ¿Qué diferencia hay entre async y sync en FastAPI?

    Usa async def si tus librerías de I/O son asíncronas; usa def si son bloqueantes. Mezclar ambos sin cuidado puede bloquear la app.

    ¿Debo empezar con SQLModel o SQLAlchemy?

    Si quieres integrar Pydantic y ORM con menor fricción, SQLModel es una opción práctica. Si necesitas control avanzado, SQLAlchemy puro puede ser más adecuado.

    ¿Cómo evito sobre‑arquitectar mi backend?

    No crees abstracciones ni micro‑paquetes antes de necesitarlos. Agrupa por feature y añade capas solo cuando el problema lo justifique.

    ¿Qué pongo en los tests iniciales?

    Empieza por tests unitarios en service.py (funciones puras) y algunos tests de integración básicos para rutas críticas.

  • Por qué deberías evitar enums en TypeScript para tus proyectos

    Por qué deberías evitar enums en TypeScript para tus proyectos

    ¿Debemos evitar enums en TypeScript?

    Tiempo estimado de lectura: 3 min

    • Los enums generan código en runtime y rompen la promesa de tipos que desaparecen.
    • Alternativas preferibles: union types para tipos puros; objetos as const para valores en runtime.
    • const enum inlining es frágil y rompe flujos modernos como isolatedModules.
    • Plan práctico: encontrar enums, priorizar UI/API, sustituir y añadir regla ESLint.

    Introducción

    ¿De verdad sigues escribiendo enums en tus proyectos nuevos? Si tu respuesta es sí, hay que hablar en serio.

    En TypeScript los enums son una característica heredada con promesas bonitas y costes escondidos. La pregunta no es moral: es práctica. ¿Te importa el tamaño del bundle, la previsibilidad del runtime y la compatibilidad con herramientas modernas?
    Entonces sí: evita enums por defecto.

    Resumen rápido (lectores con prisa)

    Los enums generan código JavaScript en runtime; prefieres union types si sólo necesitas tipos, y objetos as const si necesitas valores en runtime. const enum hace inlining pero es frágil con herramientas modernas.

    ¿Debemos evitar enums en TypeScript? — la explicación breve

    TypeScript existe para dar tipos que desaparecen al compilar. Interfaces y type unions se evaporan. Los enums no: generan código JavaScript en runtime. Eso rompe la promesa de “tipos que no pesan”.

    Mira esto:

    enum Status { Active, Inactive, Pending }

    Se transforma en una IIFE que crea un objeto y mapas reversos. Resultado: bytes extra, más complejidad para los bundlers y más superficie para errores silenciosos.

    Si quieres leer la doc oficial.

    Por qué los enums son problemáticos hoy

    • Generan código en runtime. No es sólo “feo”: es carga real en producción.
    • Tree-shaking menos efectivo. Esa IIFE puede sobrevivir al proceso de eliminación de código muerto (ver guía de Webpack).
    • Los enums numéricos permiten reverse mapping y asignaciones inesperadas: puedes acabar con números inválidos sin que TypeScript te lo grite.
    • const enum hace inlining, pero rompe flujos modernos con isolatedModules (esbuild, SWC, tsup) — herramientas que usamos para acelerar builds (esbuild, Vite).

    Qué usar en su lugar (patrones que sí funcionan)

    No te mando a la guerra sin armas. Hay alternativas simples, robustas y alineadas con el ecosistema JS.

    1) Union Types (cuando sólo quieres limitar valores)

    type Status = 'active' | 'inactive' | 'pending';
    function setStatus(s: Status) { /* ... */ }
    • Cero runtime.
    • Tipado claro y autocumplido en IDE.
    • Perfecto para props, APIs y funciones.

    2) Objetos as const (cuando necesitas valores en runtime)

    const STATUS = {
      ACTIVE: 'active',
      INACTIVE: 'inactive',
      PENDING: 'pending'
    } as const;
    
    type Status = typeof STATUS[keyof typeof STATUS];
    • Tienes objeto para iterar (Object.values).
    • Tipos literales extraídos automáticamente.
    • JavaScript simple en runtime: nada de IIFEs raras.

    3) Branded Types (si necesitas nominalidad)

    Cuando realmente quieres que dos tipos similares no sean intercambiables, usa branded types. Es más verboso, pero evita los problemas del tipado nominal que los enums intentan resolver con coste.

    `const enum`: la promesa rota

    Sí, const enum elimina el objeto y hace inlining. Suena perfecto. En la práctica es frágil:

    • Rompe con isolatedModules.
    • Empeora sourcemaps y debugging.
    • Depende del flujo de compilación; en monorepos o builds parciales, es una bomba de tiempo.

    Conclusión: no es solución universal. Evitarlo también es sensato.

    Qué hacer en tu codebase (plan práctico)

    1. Busca: grep por enum en tu repo.
    2. Prioriza: enums usados en UI y API surface primero.
    3. Sustituye:
      • Si no necesitas iteración: cambia por union type.
      • Si necesitas runtime: cambia por objeto as const.
    4. Añade regla ESLint para evitar futuros enums:
      {
        "@typescript-eslint/no-restricted-syntax": [
          "error",
          {
            "selector": "TSEnumDeclaration",
            "message": "Usa union types u objetos as const en lugar de enums"
          }
        ]
      }
    5. Prueba, mide bundle y sourcemaps. Verás la diferencia.

    ¿Cuándo sí considerar un enum?

    Muy raras veces. Casos válidos:

    • Integración con código legacy que usa enums.
    • SDKs donde los valores numéricos son parte del contrato público (protocolos).
    • Interoperabilidad con librerías externas que exigen enums.

    Pero si arrancas un proyecto nuevo, el path claro es evitar enums.

    Cierre con criterio

    Los enums ya no son la herramienta “obvia” que eran. Son una reliquia que introduce deuda técnica donde no la necesitas. Prefiere union types o as const. Son más predecibles, más rápidos y más amables con las herramientas modernas.

    Revisa tu código: haz una búsqueda rápida de enum. Si aparecen, planifica una migración por sprint. Tu bundle —y tu tranquilidad— lo agradecerán.

    FAQ

     

    ¿Por qué los enums generan código en runtime?

    Porque TypeScript traduce los enums a objetos JavaScript (a menudo via una IIFE) que existen en el runtime para soportar features como reverse mapping y valores numéricos.

    ¿No puedo usar const enum para evitar el código extra?

    const enum hace inlining eliminando el objeto, pero es frágil: rompe con isolatedModules, complica sourcemaps y depende de flujos de compilación coherentes en monorepos y builds parciales.

    ¿Qué gana mi bundle evitando enums?

    Menos bytes en el runtime, mejor posibilidad de tree-shaking y menos complejidad para los bundlers, lo que suele traducirse en bundles más pequeños y builds más predecibles.

    ¿Los objetos as const permiten iteración?

    Sí. Un objeto declarado con as const se puede iterar con Object.values() o métodos similares y además puedes extraer los tipos literales automáticamente con typeof.

    ¿Cómo detecto enums en un repo grande?

    Haz una búsqueda por la palabra clave enum (por ejemplo con grep) y prioriza los usos en la UI y la surface de la API.

    ¿Cuándo debo mantener un enum existente?

    Cuando hay integración con código legacy que depende de enums, cuando los valores numéricos son parte de un contrato público (SDKs/protocolos) o para interoperabilidad con librerías externas que exigen enums.

  • 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.

  • Server Actions en Next.js y su Impacto en el Reclutamiento

    Server Actions en Next.js y su Impacto en el Reclutamiento

    Server Actions en Next.js: ¿El fin de las APIs REST tradicionales?

    Tiempo estimado de lectura: 4 min

    Ideas clave

    • Server Actions son ideales para mutaciones originadas en la UI de Next.js y mejoran la DX reduciendo boilerplate.
    • No reemplazan REST para webhooks, clientes externos o arquitecturas desacopladas.
    • Trata cada Server Action como un endpoint público: valida, autentica y aplica rate limits.
    • Usa Route Handlers (APIs REST) para interoperabilidad, streaming binario y contratos estables entre servicios.

    Introducción

    Server Actions en Next.js permiten ejecutar funciones del servidor invocadas desde el cliente. Next.js hace la fontanería (serialización, endpoint POST, transporte). Documentación oficial: Documentación oficial y análisis en Vercel: análisis en Vercel.

    Lo digo rápido y con claridad: no son el fin de las APIs REST tradicionales. Pero cambian radicalmente cómo gestionas mutaciones internas. Si entiendes cuándo usar cada patrón, ahorras horas de debugging y deuda técnica.

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

    Qué es: Server Actions son funciones marcadas con 'use server' que Next.js ejecuta en el servidor cuando se invocan desde el cliente.

    Cuándo usarlo: Mutaciones originadas en la UI de Next.js (formularios, botones, CRUD pequeño).

    Por qué importa: Reduce boilerplate, facilita revalidación y mejora la DX compartiendo tipos entre cliente y servidor.

    Cómo funciona (en una línea): Next.js convierte la llamada en una petición POST y ejecuta la función en el servidor.

    Server Actions vs APIs REST — visión general

    Sí aparecen como sustituto natural dentro del dominio de la UI. No sustituyen REST fuera del dominio de la aplicación. Dicho de otra forma: son fantásticos para mutaciones internas; son inútiles para webhooks, clientes externos y servicios desacoplados.

    A continuación comparo ambos enfoques con ejemplos y criterio práctico.

    Cómo funcionan, en dos líneas

    Server Action

    Función marcada con 'use server' que Next.js ejecuta en el servidor cuando la invocas desde un formulario o handler.

    Route Handler (API REST)

    Endpoint explícito en app/api/.../route.ts que responde a cualquier cliente HTTP.

    Bajo el capó, una Server Action es una petición HTTP POST generada por Next.js, pero con menos boilerplate para ti.

    Ejemplo: crear un post (Route Handler)

    Backend (app/api/posts/route.ts):

    import { NextResponse } from 'next/server';
    import { db } from '@/lib/db';
    
    export async function POST(request: Request) {
      const body = await request.json();
      // validar con Zod aquí
      const post = await db.post.create({ data: body });
      return NextResponse.json(post, { status: 201 });
    }

    Frontend (cliente):

    'use client';
    async function handleSubmit(e: React.FormEvent) {
      e.preventDefault();
      const data = Object.fromEntries(new FormData(e.currentTarget));
      await fetch('/api/posts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
    }

    Control total sobre headers, status y streaming. Compatible con cualquier cliente (mobile, cron jobs, n8n).

    Ejemplo: crear un post (Server Action)

    Acción (app/actions.ts):

    'use server';
    import { db } from '@/lib/db';
    import { revalidatePath } from 'next/cache';
    
    export async function createPost(formData: FormData) {
      const title = String(formData.get('title') ?? '');
      const content = String(formData.get('content') ?? '');
      // validar y auth aquí
      await db.post.create({ data: { title, content } });
      revalidatePath('/posts');
    }

    Frontend:

    import { createPost } from '@/app/actions';
    
    export default function Form() {
      return (