Category: Blog

Your blog category

  • Cómo usar Map.getOrInsert() en JavaScript para mejorar tu código

    Cómo usar Map.getOrInsert() en JavaScript para mejorar tu código

    Map.getOrInsert(): el método que siempre quisiste en JavaScript

    Tiempo estimado de lectura: 4 min

    • Menos código, más intención: getOrInsert/getOrInsertComputed expresan “devuelve o crea” en una llamada atómica.
    • Mejor tipado en TypeScript 6.0: el retorno es T, no T | undefined, reduciendo aserciones peligrosas.
    • Uso práctico: útil en cachés, agrupaciones, contadores y construcción de grafos; evita inicializaciones innecesarias.

    Resumen rápido (lectores con prisa)

    Map.getOrInsert y Map.getOrInsertComputed permiten obtener un valor existente o insertar uno nuevo en una sola operación. En TypeScript 6.0, con target: "esnext" y lib: ["esnext"], estos métodos están tipados para devolver T en vez de T | undefined. Use getOrInsert cuando quiera expresar “devuelve o crea” de forma atómica; use la variante computada para evitar inicializaciones costosas salvo que sean necesarias.

    Map.getOrInsert(): el método que siempre quisiste en JavaScript (qué es y cómo funciona)

    Map.getOrInsert(): el método que siempre quisiste en JavaScript aparece en la primera línea porque no es una mejora menor: es la forma de expresar, de manera atómica y tipada, la intención que antes requería tres operaciones verbosas. TypeScript 6.0 ya expone los tipos vía esnext y la propuesta ECMAScript que la introduce ha alcanzado Stage 4. Aquí te explico qué hace, por qué importa y cómo integrarlo con criterio en código de producción.

    Qué hace y cómo funciona

    El patrón clásico —buscar, insertar si no existe, y luego usar— se repite en cachés, agrupaciones, contadores y grafos. Tradicionalmente escribíamos:

    if (!map.has(key)) map.set(key, compute());
    const v = map.get(key)!;

    Tres operaciones, doble lookup y una aserción no nula que silencia al compilador. getOrInsert y su variante perezosa getOrInsertComputed resuelven eso en una sola llamada:

    • map.getOrInsert(key, defaultValue): devuelve el valor si existe, si no inserta y devuelve el defaultValue.
    • map.getOrInsertComputed(key, () => value): ejecuta la función sólo si la clave no existía, evitando inicializaciones innecesarias y efectos secundarios.

    En TypeScript 6.0, usando target: "esnext" y lib: ["esnext"], el compilador tipa estos métodos de forma que el retorno es T (no T | undefined). El contrato refleja la intención: si llamas, obtienes un valor seguro.

    Por qué esto no es solo “azúcar sintáctico”

    Tres razones prácticas:

    1. Menos errores de tipo

    Con el patrón manual el compilador ve get() como T | undefined. Con getOrInsert obtienes T. Menos comprobaciones defensivas, menos ! peligrosos.

    2. Menos overhead y operaciones atomizadas

    Evitas doble lookup (has + get). Aunque la mejora de rendimiento es pequeña en muchos casos, en bucles grandes o inicializaciones masivas suma.

    3. Menos ruido cognitivo

    La intención queda expresada: “devuelve o crea”. El lector del código no necesita reconstruir el propósito tras tres líneas.

    Casos reales donde cambia la vida

    – Cachés con inicialización costosa: evita ejecutar la función de cálculo salvo que sea estrictamente necesario.

    – Agrupaciones: Map<string, T[]> ya no requiere crear arrays temporales.

    – Contadores y histogramas: inicializar un contador a 0 en una sola línea es más legible.

    – Construcción de grafos (listas de adyacencia): evita boilerplate en algoritmos BFS/DFS.

    Ejemplo típico

    const groups = new Map<string, User[]>();
    
    function addUser(role: string, user: User) {
      groups.getOrInsertComputed(role, () => []).push(user);
    }

    Sencillo, explícito y sin crear arrays inútiles.

    Integración práctica en repositorios TypeScript

    1. Habilita esnext en tsconfig:
      {
        "compilerOptions": {
          "target": "esnext",
          "lib": ["esnext"],
          "strict": true
        }
      }
    2. Revisa compatibilidad runtime:

      TypeScript te da la comprobación estática. La API como tal puede requerir polyfill o verificar la versión de Node/Bun/deno si no está implementada nativamente en tu motor.

    3. Refactor incremental:

      Busca utilidades internas con nombres como getOrCreate, getOrInit o computeIfAbsent. Reemplaza utilidades por el método nativo en PRs pequeños. Añade pruebas unitarias para casos bordes (excepciones en la función de cálculo, concurrencia en entornos compartidos).

    4. No abuses de getOrInsertComputed con side effects:

      La función de cálculo se invoca solo cuando hace falta, pero debe ser determinista y preferiblemente sin efectos secundarios que el mapa pueda registrar de forma incompleta si ocurre una excepción.

    Consideraciones de diseño y criterio Dominicode

    Estándar sobre conveniencia: si ya tienes utilidades internas, migrar a getOrInsert reduce mantenimiento y mejora la expresividad.

    No sustituyas estructuras lógicas: usar Map con getOrInsert no es excusa para diseños pobres. Sigue definiendo responsabilidades y límites de módulo.

    Audita y documenta: añade esta patrón a las guías internas y revisiones de código para homogenizar su uso.

    Fuentes y lectura adicional

    Adoptar Map.getOrInsert() hoy es reducir ruido, reforzar tipos y expresar intención. No es una moda: es una higiene mínima que facilita mantener bases de código grandes sin perder claridad. Si eres Tech Lead, estandariza su uso; si eres desarrollador, empieza a buscar esos utils/mapGetOrCreate.ts y reemplázalos por la API del lenguaje.

    FAQ

    Respuesta — ¿Qué devuelve getOrInsert si la clave existe?

    Devuelve el valor existente asociado a la clave. El contrato asegura que el retorno es T, no T | undefined.

    Respuesta — ¿En qué se diferencia getOrInsert de getOrInsertComputed?

    getOrInsert recibe un valor por defecto ya construido; getOrInsertComputed recibe una función que se ejecuta solo si la clave no existe, evitando costes de inicialización cuando no son necesarios.

    Respuesta — ¿Necesito un polyfill para usarlo en producción?

    Depende del runtime. TypeScript ofrece comprobación estática cuando apuntas a esnext, pero la API puede no estar presente en versiones antiguas de Node/Bun/deno; en esos casos se requiere polyfill o verificación de la versión del motor.

    Respuesta — ¿Cómo mejora el tipado en TypeScript 6.0?

    Con target: "esnext" y lib: ["esnext"], los tipos para estos métodos hacen que el retorno sea T, evitando la necesidad de aserciones (!) y reduciendo comprobaciones manuales de undefined.

    Respuesta — ¿Es seguro usarlo en entornos concurrentes?

    El método evita doble lookup, pero la seguridad en entornos concurrentes depende del runtime y del modelo de concurrencia. Añade pruebas y, si corresponde, mecanismos de sincronización en entornos compartidos.

    Respuesta — ¿Debo reemplazar todas mis utilidades internas?

    No necesariamente. Migra en PRs pequeños y cuando aporte claridad o reduzca mantenimiento. Prioriza casos donde el beneficio sea evidente (cachés, agrupaciones, contadores).

    Respuesta — ¿Qué precauciones al usar funciones con efectos secundarios?

    La función de cálculo en getOrInsertComputed debe ser determinista y preferiblemente sin efectos secundarios. Si lanza una excepción, el mapa podría quedar en un estado intermedio; maneja errores y añade pruebas para esos casos.

  • Cómo crear una librería npm tipada en TypeScript para desarrolladores

    Cómo crear una librería npm tipada en TypeScript para desarrolladores

    Cómo hacer una librería npm tipada con TypeScript

    Tiempo estimado de lectura: 5 min

    • Entrega dual: código ejecutable + declaraciones de tipo (.d.ts) para buena DX.
    • Configuración esencial: tsconfig con declaration: true y salida limpia en dist/.
    • Empaquetado: mapear main/module/types en package.json y validar con npm pack y tsc en proyecto consumidor.
    • Exportaciones claras: centralizar API en src/index.ts y usar export type para tipos.
    • Validación: pruebas de tipos en CI con tsd y tests locales antes de publish.

    Introducción

    Saber cómo hacer una librería npm tipada con TypeScript significa entregar dos cosas a la vez: código que la máquina ejecuta y tipos que el desarrollador consume. Desde el primer archivo hasta el .d.ts final, la meta es que quien instale tu paquete tenga autocompletado y verificaciones de tipo sin tocar su configuración. Esta guía va directo al flujo de producción: configuración, estructura, packaging y validación local.

    Resumen rápido (lectores con prisa)

    Una librería npm tipada combina JavaScript/TypeScript compilado y declaraciones de tipo (.d.ts). Usa tsc con declaration: true para generar tipos; centraliza la API en src/index.ts; apunta types en package.json al .d.ts maestro; valida con npm pack y ejecuta tsc --noEmit en un proyecto consumidor.

    Cómo hacer una librería npm tipada con TypeScript — pasos y criterios

    1. Estructura mínima

    • src/ — código TypeScript.
    • dist/ — artefactos compilados (generado).
    • package.json, tsconfig.json, README.md.

    2. Instala y prepara TypeScript

    npm init -y
    npm install typescript --save-dev
    npx tsc --init

    3. tsconfig.json recomendable (base)

    {
      "compilerOptions": {
        "target": "ES2020",
        "module": "CommonJS",
        "moduleResolution": "node",
        "declaration": true,
        "declarationMap": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules", "**/*.test.ts"]
    }
    • declaration: true es obligatorio: genera .d.ts.
    • declarationMap: true mejora la experiencia “Go to definition” en IDEs.
    • outDir y rootDir mantienen el árbol limpio.

    Documentación oficial TypeScript: TypeScript declaration files publishing

    4. Código: exportaciones claras y barrel file

    Centraliza la API pública en src/index.ts. Exporta valores y tipos explícitamente:

    // src/types.ts
    export interface LibraryConfig { timeout: number; }
    
    // src/core.ts
    import type { LibraryConfig } from './types';
    export function init(cfg: LibraryConfig) { /* ... */ }
    
    // src/index.ts
    export { init } from './core';
    export type { LibraryConfig } from './types';
    

    Usa export type para separar contratos estáticos (no existen en runtime). Evita filtrar internals por accidente.

    5. package.json: el contrato que vincula .js y .d.ts

    {
      "name": "mi-libreria-tipada",
      "version": "1.0.0",
      "main": "dist/cjs/index.js",
      "module": "dist/esm/index.js",
      "types": "dist/index.d.ts",
      "files": ["dist"],
      "scripts": {
        "build": "npm run build:cjs && npm run build:esm",
        "build:cjs": "tsc --outDir dist/cjs --module commonjs",
        "build:esm": "tsc --outDir dist/esm --module ESNext",
        "prepublishOnly": "npm run build"
      },
      "exports": {
        ".": {
          "types": "./dist/index.d.ts",
          "require": "./dist/cjs/index.js",
          "import": "./dist/esm/index.js"
        }
      }
    }
    • types debe apuntar al .d.ts maestro.
    • files actúa como whitelist; evita subir src o configs.
    • exports (condicional) habilita resolución moderna y puede mapear CJS/ESM. Coloca types junto a la condición raíz para que TypeScript lo resuelva bien.

    Si publicas scoped package (p. ej. @scope/name), recuerda npm publish --access public para paquetes públicos.

    6. ¿Generar .d.ts con tsc o con bundler?

    • Para proyectos simples o librerías de funciones: usa tsc con declaration: true. Es robusto y sencillo.
    • Para bundles complejos (single-file output), usa un plugin para generar tipos como rollup-plugin-dts o la opción tsup --dts. Si eliges esta vía, verifica que el .d.ts final coincida con la estructura exportada.

    Herramientas útiles:

    7. Validación local antes de publicar

    1. npm pack — crea un .tgz. Ábrelo y verifica contenido: dist/, package.json, README.md.

      Docs: npm pack docs

    2. Instalación local en proyecto cliente:
      cd ../project-test
      npm install /ruta/a/mi-libreria-tipada-1.0.0.tgz

      Importa y ejecuta tsc --noEmit en el proyecto consumidor: si types está mal, fallará aquí.

    3. Tests de tipos automáticos: integra tsd en tu CI para asegurar que la API expuesta no cambia rompeints:

      tsd

    8. Dependencias y peerDependencies

    • Declara en peerDependencies librerías que el consumidor debe proveer (React, por ejemplo) para evitar duplicados.
    • Pon utilidades que la librería necesita en dependencies.

    9. Checklist rápido antes de publish

    • [ ] npm run build genera dist con .js y .d.ts.
    • [ ] package.json apunta a main, module (si aplica) y types.
    • [ ] files incluye solo dist, README.md, LICENSE.
    • [ ] npm pack inspeccionado.
    • [ ] Prueba de instalación local y tsc --noEmit en proyecto consumidor.
    • [ ] Tests de tipos (tsd) pasados en CI.

    10. Comandos finales para publicar

    npm login
    npm publish --access public

    (Usa --access public para paquetes scope públicos.)

    Conclusión

    Construir una librería tipada no es mágico: es disciplina. Configura tsc para emitir declaraciones, expone solo lo necesario y valida el paquete en un entorno consumidor antes de pulsar publish. El tiempo que inviertes en estos pasos se recupera con menos issues en integraciones y una mejor experiencia para quienes usan tu paquete.

    FAQ

    ¿Por qué es obligatorio generar .d.ts?

    Las declaraciones (.d.ts) proporcionan tipos a los consumidores de tu librería sin necesidad de que compilen tu código TypeScript. Sin ellas, los usuarios perderían autocompletado y las comprobaciones de tipo.

    ¿Puedo usar un bundler para generar tipos?

    Sí. Para bundles single-file es común usar plugins como rollup-plugin-dts o herramientas como tsup --dts. Verifica que el resultado refleje la API exportada.

    ¿Qué debe apuntar el campo types en package.json?

    types debe apuntar al .d.ts maestro que describe la API pública, por ejemplo dist/index.d.ts.

    ¿Cómo pruebo la experiencia del consumidor antes de publicar?

    Usa npm pack para generar un .tgz, instálalo en un proyecto de prueba con npm install /ruta/mi-libreria.tgz y ejecuta tsc --noEmit en el proyecto consumidor.

    ¿Debo usar peerDependencies para React?

    Sí. Declara frameworks como React en peerDependencies para evitar múltiples copias y problemas de compatibilidad en el proyecto consumidor.

    ¿Qué herramientas recomiendo para pruebas de tipos en CI?

    Integra tsd en tu CI para pruebas automáticas de tipos. Revisa tsd para más detalles.

    ¿Qué archivos incluir en el paquete publicado?

    Usa files en package.json para incluir solo dist, README.md y LICENSE. Evita subir src o configuraciones internas.

  • Cómo utilizar OpenSpec para documentar APIs de forma efectiva

    Cómo utilizar OpenSpec para documentar APIs de forma efectiva

    Qué es OpenSpec y como empezar a usarlo?

    Tiempo estimado de lectura: 4 min

    Ideas clave:

    • OpenSpec (típicamente OpenAPI) es un contrato interoperable para describir APIs RESTful en YAML o JSON.
    • Buenas especificaciones reducen errores, permiten generación de SDKs y mejoran el comportamiento de agentes IA.
    • Empieza con un openapi.yaml válido, centraliza esquemas en components/schemas y valida con linters en CI/CD.
    • Decide una única fuente de la verdad: la spec o el código; sincronízalos para evitar drift.
    • Usa herramientas como Spectral, openapi-generator, Orval y n8n para integrar la spec en tu stack.

    Tabla de contenidos

    ¿Quieres que tus APIs hablen claro con humanos, herramientas y agentes de IA? Entender qué es OpenSpec y como empezar a usarlo es el primer paso para dejar de parchear integraciones y empezar a diseñar sistemas que realmente escalan.

    Resumen rápido (lectores con prisa)

    OpenSpec (normalmente OpenAPI) es un contrato en YAML/JSON que describe endpoints, parámetros y respuestas. Usarlo permite generación de clientes, validación automática y que agentes IA descubran y llamen funciones. Empieza con un openapi.yaml, centraliza esquemas en components/schemas y valida con Spectral en CI/CD.

    Qué es OpenSpec (breve y sin rodeos)

    OpenSpec = OpenAPI (típicamente). Repositorio oficial: OpenAPI Spec repo.

    Es un estándar agnóstico al lenguaje para describir APIs RESTful en YAML o JSON. No es solo “documentación bonita”: es un artefacto interoperable que potencia generación de clientes, validación en CI/CD y, hoy en día, el comportamiento de agentes IA que realizan tool-calling.

    Si lo haces bien, reduces preguntas, errores y tiempo de debugging. Si lo haces mal, tus integraciones sufrirán en silencio.

    Por qué importa ahora (sí, de verdad)

    • Los modelos de lenguaje consumen especificaciones para descubrir capacidades y ejecutar llamadas: mejores especificaciones = agentes más útiles.

    • Herramientas como n8n pueden mapear rutas desde una OpenSpec y reducir el trabajo manual en flujos.

    • Generadores como openapi-generator o Orval crean SDKs y hooks tipados: menos errores y builds que fallan temprano.

    • Linters como Spectral te permiten bloquear cambios que rompan contratos en CI/CD.

    Si tu arquitectura usa automatizaciones, agentes o consumidores externos, OpenSpec deja de ser “opcional” y pasa a ser infraestructura.

    Cómo empezar a usar OpenSpec en 5 pasos prácticos

    1) Elige formato: YAML por defecto

    YAML es más legible en PRs, permite comentarios y evita ruido. JSON es válido, pero menos humano.

    2) Crea el archivo base openapi.yaml con metadatos

    Ejemplo mínimo:

    openapi: 3.1.0
    info:
      title: API de Gestión de Usuarios
      version: 1.0.0
    servers:
      - url: https://api.midominio.com/v1

    3) Modela endpoints y componentes reutilizables

    Centraliza esquemas en components/schemas. Usa operationId en cada operación: es la referencia estable que usan generadores y agentes.

    Ejemplo:

    paths:
      /usuarios:
        get:
          operationId: getUsuarios
          summary: Lista usuarios activos
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    type: array
                    items:
                      $ref: '#/components/schemas/Usuario'
    
    components:
      schemas:
        Usuario:
          type: object
          required: [id, nombre]
          properties:
            id:
              type: string
              format: uuid
            nombre:
              type: string

    4) Valida y lintea en CI/CD

    Instala Spectral y córrelo en tus pipelines:

    npm install -g @stoplight/spectral-cli
    spectral lint openapi.yaml

    Configura reglas propias para estilo y seguridad. Esto evita que la IA actúe sobre un contrato erróneo.

    5) Conecta la especificación a tu stack

    • n8n: importa la spec para mapear rutas en nodos HTTP.

    • Frontend: usa Orval para generar hooks React/Next o servicios Angular.

    • Backend: genera SDKs con openapi-generator.

    • Agentes IA: pasa el YAML como “capability file” al sistema prompt o úsalo con LangChain para que los agentes sepan qué funciones pueden invocar.

    Decisiones prácticas: cuándo autogenerar y cuándo escribir

    • Si tu API cambia rápido o es grande: genera la OpenSpec desde código fuente o desde un DSL (ej. TypeSpec de Microsoft TypeSpec). Mantener un YAML manual de miles de líneas es fuego lento.

    • Si tu API es estable y pequeña: editar manualmente puede ser más rápido y explícito.

    • Regla simple: la fuente de la verdad debe ser única. O el código genera la spec, o la spec manda al código. No ambos sin sincronización.

    Riesgos y límites a considerar

    • Verbosidad: OpenAPI 3.x puede crecer mucho. Divide specs por dominios si hace falta.

    • Ambigüedad semántica: un buen schema evita que agentes “adivinen” parámetros. Sé explícito con required, tipos y ejemplos.

    • Seguridad: nunca publiques specs con credenciales ni ejemplos sensibles.

    Herramientas clave (rápido)

    Si tu trabajo incluye automatización, agentes o workflows, puede interesarte explorar recursos adicionales en Dominicode Labs como una continuación práctica para integrar especificaciones en pipelines y agentes. La mención apunta a material y experimentos que complementan las prácticas aquí descritas.

    FAQ

    ¿Qué diferencia hay entre OpenSpec y OpenAPI?

    OpenSpec, en el uso común, suele referirse a la OpenAPI Specification (OAS). Es el estándar para describir APIs RESTful en YAML o JSON.

    ¿Debo usar YAML o JSON?

    YAML es preferible para edición humana y PRs por su legibilidad y soporte de comentarios. JSON es igualmente válido pero menos práctico para revisión manual.

    ¿Cómo integro Spectral en CI/CD?

    Instala la CLI (npm install -g @stoplight/spectral-cli) y añade un paso en tu pipeline que ejecute spectral lint openapi.yaml. Configura reglas para estilo y seguridad para bloquear merges que rompan contratos.

    ¿Cuándo genero la spec desde código?

    Cuando el API cambia rápido o es grande: generar la spec desde el código o desde un DSL evita drift y reduce la carga de mantenimiento manual.

    ¿Puedo usar la spec con agentes IA?

    Sí. Puedes pasar el YAML como “capability file” en prompts o usar frameworks como LangChain para que los agentes conozcan las funciones disponibles.

    ¿Qué evitar al publicar una spec?

    No publiques credenciales ni ejemplos con datos sensibles. Evita ambigüedades en tipos y required; sé explícito para que consumidores y agentes no adivinen.

  • Implementación de Agent Skills para proyectos Angular

    Implementación de Agent Skills para proyectos Angular

    Hacer post sobre los Agent Skills de angular: guía práctica para arquitectos y equipos

    Tiempo estimado de lectura: 3 min

    • Agent Skills son capacidades automatizadas para leer, analizar y proponer cambios en código Angular respetando arquitectura y límites.
    • Prioridad ahora: Standalone Components, Signals, inject() y Zoneless requieren que la IA entienda contexto antes de tocar código.
    • Flujo operativo: discovery read‑only → auditorías estáticas → propuestas mecánicas en rama con PR y revisión humana.
    • Governance: integrar skills en CI, playbooks y dashboards para trazabilidad y control de riesgos.

    Hacer post sobre los Agent Skills de angular no es levantar hype ni repetir buzzwords; es ordenar una pieza crítica del puzzle: cómo convertimos agentes de IA en revisores técnicos y refactorizadores seguros para proyectos Angular. Si tu equipo opera con monorepos, Signals y rutas lazy, necesitas que la IA entienda topología, prácticas y riesgo antes de tocar una línea de código.

    Resumen rápido (lectores con prisa)

    Agent Skills son capacidades programáticas para que un agente automatizado lea, analice y proponga cambios en un repo Angular respetando arquitectura. Úsalos para discovery read‑only, auditorías estáticas y para generar propuestas en ramas con revisión humana. Integrar skills en CI y playbooks es clave para trazabilidad y control de riesgo.

    Qué son los Agent Skills de Angular (y por qué importan)

    Los Agent Skills son capacidades programáticas que permiten a un agente automatizado leer, analizar y —con límites— transformar código Angular respetando la arquitectura del proyecto. No hablamos de completados contextuales: hablamos de habilidades concretas para mapear workspaces, auditar patrones reactivos, detectar fugas de memoria y proponer refactorizaciones alineadas con la versión del framework.

    Por qué ahora

    Angular cambió. Standalone Components, Signals, inject() y la tendencia Zoneless hacen que un snippet bien formado pueda ser, en contexto, una regresión. El objetivo de los Agent Skills es cerrar la brecha entre lo que el modelo “sabe” y lo que el repo “es”.

    Categorías de Agent Skills (práctico y accionable)

    1) Descubrimiento y topología (Read‑Only)

    Mapeo de workspace

    Mapeo de workspace: lee angular.json / project.json y detecta apps, librerías y fronteras de dominio. Referencia: Nx.

    Árbol de dependencias

    Árbol de dependencias: construye el grafo de importaciones para detectar fronteras violadas o riesgos de dependencia circular.

    Regla de oro: siempre en modo solo lectura. Ejecutar antes de cualquier propuesta.

    2) Auditoría y análisis estático (reportes)

    Detección de fugas

    Detección de fugas: localizar suscripciones sin cleanup (ngOnDestroy, takeUntilDestroyed()), event listeners no removidos o timers persistentes.

    Change detection check

    Change detection check: validar OnPush vs mutaciones por referencia que rompen el rendering.

    Compatibilidad Zoneless

    Compatibilidad Zoneless: identificar patrones que dependen de la detección implícita de zone.js. Referencia: zone.js.

    Resultado: un informe accionable con líneas de riesgo, prioridad y referencia documental.

    3) Modernización y refactorización (propuestas con revisión humana)

    Migración a Standalone

    Migración a Standalone: extraer un componente de un NgModule, aplicar standalone: true y actualizar rutas lazy.

    Refactor inject()

    Refactor inject(): reemplazar inyección por constructor cuando el patrón lo permita.

    RxJS → Signals

    RxJS → Signals: transformar estados locales sincronizados (BehaviorSubject) a signal()/computed() cuando no haya flujos asíncronos complejos.

    Nota: estas operaciones deben proponerse en PRs, nunca aplicarse directamente en main sin revisión.

    Ejemplo de flujo mínimo (adoptable hoy)

    1. PR abierto → webhook dispara workflow.
    2. Ejecutar skills Read‑Only: mapea workspace + análisis de dependencias.
    3. Ejecutar auditorías: fugas, OnPush, Zoneless. Adjuntar report con URLs de documentación (angular.dev).
    4. Si todo OK, ejecutar propuestas mecánicas (migración Standalone o inject()) en una rama de trabajo y abrir PR automático con checklist y pruebas.

    Este flujo es orquestable con n8n o runners CI que disparen agentes y persistan resultados para trazabilidad.

    Criterio técnico (lo que no debes automatizar sin control)

    • No otorgues permisos de escritura globales hasta que el agente haya corrido los skills de descubrimiento y get_best_practices.
    • Tratar la migración Zoneless como diagnóstico, no como parche automático: los efectos secundarios en tiempo de ejecución y librerías externas pueden romper producción.
    • No convertir RxJS → Signals de forma indiscriminada; define reglas: solo estado local sincronizable y sin operadores complejos.
    • Exige trazabilidad: cada recomendación viene con fragmento de doc y URL a la sección relevante de angular.dev.

    Integración con gobernanza y equipos

    Los Agent Skills dejan de ser experimentales cuando se integran en:

    • Pipelines de CI que registran outputs de los inspectores junto al PR.
    • Playbooks de revisión que establecen qué skills pueden autocorregir en ramas de feature y cuáles requieren aprobación de un Senior.
    • Dashboards de deuda técnica que priorizan hallazgos del agente (fugas, violaciones de fronteras, patrones Zoneless).

    Qué ganas — y cuánto riesgo evitas

    Implementar Agent Skills bien definidos reduce regresiones arquitectónicas, acelera migraciones repetitivas y convierte a la IA en un revisor que aporta contexto (no solo snippets). El retorno real es tiempo de arquitecto liberado para decisiones complejas y menos deuda introducida por cambios automáticos mal contextualizados.

    Cierre práctico

    1. Añadir un skill de mapeo de workspace en tu pipeline.
    2. Automatizar la auditoría de fugas por cada PR.
    3. Pilotar migraciones Standalone en ramas feature con revisión humana.

    Con eso pones la IA a trabajar para tu arquitectura, no contra ella.

    Si quieres integrar estos workflows en tus pipelines y experimentos, considera explorar recursos y experiments en Dominicode Labs. Es una continuación natural para llevar skills de inspección y orquestación a pipelines con trazabilidad y playbooks.

    FAQ

    ¿Qué son exactamente los Agent Skills en Angular?

    Son capacidades programáticas que permiten a un agente automatizado leer, analizar y, con límites, transformar código Angular respetando la arquitectura del proyecto. Incluyen discovery, auditoría estática y generación de propuestas para revisión humana.

    ¿Cuándo debo ejecutar los skills en un PR?

    El flujo recomendado es: al abrir un PR, disparar skills Read‑Only (mapeo de workspace y análisis de dependencias) y luego las auditorías (fugas, OnPush, Zoneless). Solo después de pasar estos pasos se generan propuestas mecánicas en una rama separada.

    ¿Puede el agente aplicar cambios directamente en main?

    No. Las operaciones de modificación deben proponerse en PRs y revisarse. No otorgues permisos de escritura globales hasta validar discovery y mejores prácticas. Las transformaciones automatizadas requieren trazabilidad y aprobación.

    ¿Cómo integro los reports en CI?

    Orquesta el flujo con webhooks o runners CI: al abrir PR disparas agentes que persisten los resultados junto al PR. Puedes usar herramientas como n8n para orquestación y almacenar informes para dashboards de deuda técnica.

    ¿Qué precauciones con Zoneless?

    Trata la migración Zoneless como diagnóstico, no como parche automático. Identifica patrones que dependen de zone.js y evalúa efectos secundarios en tiempo de ejecución y librerías externas antes de proponer cambios.

    ¿Cómo asegurar trazabilidad de recomendaciones?

    Cada recomendación debe incluir fragmentos de doc y un enlace a la sección relevante de angular.dev, junto con el contexto del repo (archivo, línea, grafo de dependencias) y el historial del agente que generó la sugerencia.

  • Cómo orquestar subagentes de IA para un desarrollo eficaz

    Cómo orquestar subagentes de IA para un desarrollo eficaz

    Subagentes como equipo de desarrollo: orquestación con Claude Code

    Tiempo estimado de lectura: 4 min

    Ideas clave

    • Plan, delega, commit, valida: estructura que convierte a un asistente en un equipo con coordinador y subagentes.
    • Riesgos mitigados: degradación de contexto, decisiones implícitas y falta de trazabilidad.
    • Regla de commit inquebrantable: cada subagente debe hacer un commit atómico antes de avanzar.
    • DAG y paralelismo: lanzar en paralelo solo nodos sin dependencias y revisar diffs antes de desbloquear dependientes.
    • Requisitos para producción: CLAUDE.md, pipelines rápidos, política de revisión y auditoría de commits.

    Introducción

    Subagentes como equipo de desarrollo: orquestación con Claude Code es el patrón que convierte a un asistente de IA en un equipo real: un coordinador que descompone trabajo y subagentes que ejecutan tareas atómicas, hacen commits y devuelven resultados auditables. Si vas a automatizar entregas complejas, empieza por esta estructura: plan, delega, commit, valida.

    Resumen rápido (lectores con prisa)

    Patrón que transforma un asistente en un equipo con un coordinador que define la spec y un conjunto de subagentes que implementan tareas atómicas. Útil cuando puedes separar trabajo por interfaces claras y hay necesidad de trazabilidad y rollback atómico. Requiere commits por subagente, pipelines rápidos y un CLAUDE.md como referencia.

    Subagentes como equipo de desarrollo: por qué importa y cómo cambia el riesgo

    La diferencia entre generar código rápido y entregar cambios sostenibles no está en la velocidad de la IA, sino en cómo gestionas el contexto y las decisiones. Un agente que trabaja solo acumula contexto y toma decisiones implícitas; eso produce deuda técnica que emerge en integración. Orquestar subagentes reduce tres riesgos claves:

    • Degradación de contexto: cada subagente opera con una ventana limitada y relevante.
    • Propagación de decisiones implícitas: el coordinador valida outputs antes de avanzar.
    • Falta de trazabilidad: cada subagente hace un commit atómico, facilitando revertir y revisar.

    Documentación útil: Claude Code overview y Claude (Anthropic)

    Cómo funciona el flujo: roles, primitives y regla del commit

    1. Agente principal (coordinador)

    – Define la spec global y el DAG de dependencias.
    – Descompone el trabajo en tareas atómicas.
    – Lanza subagentes con la primitiva task.

    2. Subagentes (desarrolladores)

    – Reciben una tarea acotada: archivos relevantes, firmas esperadas, criterios de aceptación.
    – Implementan cambios, añaden tests y hacen un commit.
    – Devuelven al coordinador el diff, logs de test y un resumen de riesgos pendientes.

    3. Regla inquebrantable: cada subagente hace un commit antes de que el coordinador asigne la siguiente tarea dependiente

    Beneficios: aislamiento de errores, validación incremental, trazabilidad en Git.

    Ejemplo de secuencia para migración

    • Task 1: migrar modelo de pagos → commit “payments: migrate model v2”
    • Task 2: actualizar servicio de facturación (depende de Task 1) → commit “billing: use payments v2”
    • Task 3: actualizar tests e2e (paralelo) → commit “tests: update e2e for payments v2”

    Reglas operativas: cómo escribir tareas para subagentes

    Una mala especificación produce malos resultados, aunque el subagente sea capaz. Sigue estas reglas:

    • Objetivo claro en 1–2 líneas.
    • Alcance: archivos y módulos permitidos.
    • Contratos: firmas, DTOs, errores esperados.
    • Criterios de aceptación automatizables (tests unitarios o comandos de CI).
    • Comando de commit esperado y mensaje sugerido.
    • Limitar tiempo/recursos si procede.

    Plantilla mínima para una tarea

    • Título: actualizar UserService para usar AuthV2
    • Archivos permitidos: src/services/userService.ts, src/types/auth.ts
    • Contrato: getUser(id): UserDto
    • Tests: añadir unit tests para getUser con mocks de AuthV2
    • Commit: “user: migrate to AuthV2 — tests added”

    Integración, paralelismo y control de dependencias

    – Construye un DAG (grafo acíclico) de tareas. Lanza en paralelo solo nodos sin dependencias entre sí.
    – Siempre inspecciona el diff tras cada commit. El coordinador puede ejecutar hooks o pipelines ligeros antes de desbloquear tareas dependientes.
    – Si una tarea paralela falla, su rollback es local: revertir su commit o patch específico, sin tocar el trabajo válido previo.

    Requisitos previos para producción

    • CLAUDE.md actualizado en la raíz: stack, patrones prohibidos, comandos CI. Los subagentes la leerán al iniciar. (Ver ejemplo de uso de CLAUDE.md en prácticas de equipo).
    • Pipelines de CI rápidos: que verifiquen commits intermedios (lint, tests unitarios).
    • Política de revisión: define qué commits requieren revisión humana inmediata (p. ej., cambios en auth, DB).
    • Mecanismo de auditoría: etiquetas de commit que identifiquen subagente y tarea.

    Cuándo aplicar este patrón (y cuándo no)

    Úsalo cuando

    • Puedes descomponer trabajo en módulos con interfaces claras.
    • Hay paralelismo real entre módulos.
    • Necesitas trazabilidad y rollback atómico.

    No lo uses cuando

    • La tarea es totalmente secuencial o indivisible.
    • Las interfaces son ambiguas o el proyecto carece de convenciones documentadas.
    • El overhead de coordinación supera el beneficio (scripts pequeños, fixes triviales).

    Métricas que importan para medir éxito

    • Tiempo medio desde task creada hasta merge sin rework.
    • Número de reverts por milestone.
    • % de tasks que pasan CI en primer commit.
    • Latencia de integración (tiempo entre commit de dependencia y comienzo de tareas dependientes).

    Un aumento en la proporción de merges sin rework y una caída en los reverts indican que la orquestación está funcionando.

    Limitaciones honestas

    El patrón amplifica capacidad, no sustituye criterio. Si el coordinador delega mal —tareas vagas, contratos inconsistentes— obtendrás implementaciones rápidas y equivocadas. La diferencia está en quién escribe las specs: la IA ejecuta, el humano decide.

    Dominicode Labs

    Para seguir explorando patrones de orquestación y automatización aplicados a equipos mixtos humano+IA, consulta Dominicode Labs. Es una continuación lógica para pruebas de concepto y plantillas de CLAUDE.md en equipos de ingeniería.

    FAQ

    Es una estructura donde un coordinador descompone trabajo en tareas atómicas y subagentes ejecutan esas tareas, hacen commits atómicos y devuelven diffs, logs y riesgos pendientes.

    Cuando puedes descomponer trabajo en módulos con interfaces claras, hay paralelismo real y necesitas trazabilidad y capacidad de rollback atómico.

    Objetivo en 1–2 líneas, alcance (archivos permitidos), contratos (firmas/DTOs), criterios de aceptación automatizables, comando de commit esperado y límites de tiempo/recursos si procede.

    Se recomiendan pipelines rápidos que verifiquen commits intermedios con lint y tests unitarios. No se prescribe una herramienta específica en este texto.

    El rollback es local: revertir el commit o aplicar un patch específico de la tarea fallida, sin tocar el trabajo válido previo.

    Debe incluir stack, patrones prohibidos y comandos CI. Los subagentes la leerán al iniciar y sirve como referencia de equipo.

    Aumentos en merges sin rework, caída en reverts, tiempo medio hasta merge menor, alto % de tasks que pasan CI en primer commit y baja latencia de integración.

  • Cómo usar Claude Code para mejorar la productividad en desarrollo

    Cómo usar Claude Code para mejorar la productividad en desarrollo

    Como usar Claude Code como un pro

    Tiempo estimado de lectura: 4 min

    • Sesiones enfocadas: abre Claude Code en la carpeta del módulo, no en la raíz del monorepo.
    • Delega ciclos completos: pide flujos (tests → ejecutar → corregir → commit), no snippets aislados.
    • Controla contexto: evita indexar logs, binarios y secretos; limpia filtros antes de iniciar.
    • Autonomía con control: mantén confirmaciones para comandos destructivos y usa entornos efímeros para ejecuciones automáticas.

    Si quieres transformar la terminal en un entorno de ingeniería productiva, necesitas saber como usar Claude Code como un pro desde el primer comando. Claude Code no es un complemento de autocompletado: es un agente que puede leer tu repositorio, ejecutar comandos, iterar sobre fallos y aplicar cambios. Usarlo bien implica diseño de prompts, control del contexto y reglas claras de seguridad.

    Resumen rápido (lectores con prisa)

    Qué es: Un agente que puede leer repositorios, ejecutar comandos y aplicar cambios.

    Cuándo usarlo: Para flujos completos como refactorizaciones, tests y generación de PRs en entornos controlados.

    Por qué importa: Acelera tareas completas y reduce iteraciones manuales cuando se usa con límites y control de contexto.

    Cómo funciona: Indexa el directorio de sesión, ejecuta comandos permitidos y puede iterar según la salida real del sistema.

    como usar Claude Code como un pro: principios prácticos

    1) Trabaja en sesiones enfocadas (aprovecha el prompt caching)

    Claude Code indexa el directorio en el que abres la sesión. Esa indexación se cachea para reducir latencia y coste. La regla: una sesión = un microservicio o un módulo. Cambiar de contexto dentro de la misma sesión invalida la caché y dispara costes y latencia.

    Práctica: abre la CLI dentro de services/payments/, resuelve la tarea y cierra la sesión. No abras Claude Code en la raíz de un monorepo a menos que realmente necesites ver todo.

    2) Delega ciclos completos, no micro-tareas

    Un uso amateur pide snippets. Un uso profesional delega un flujo entero:

    Prompt tipo pro:
    “Refactoriza src/billing para eliminar dependencias a legacy-lib.
    Crea tests Jest que cubran el 80% de las rutas críticas.
    Ejecuta npm run test y corrige fallos hasta que la suite pase.
    Genera un changelog corto y crea un commit.”

    Resultado: código probado, commit y artefactos (tests + changelog). No más “escribe la función X”.

    3) Controla el contexto y el ruido (asegura tu entrada)

    Si la sesión indexa logs, bases SQLite locales o binarios, el modelo desperdicia tokens. Dos acciones imprescindibles:

    • Ejecuta Claude Code desde la carpeta del módulo que interesa.
    • Mantén .gitignore y filtros locales limpios; mueve o excluye archivos pesados antes de indexar.

    No inventes exclusiones mágicas: la higiene del repositorio reduce errores y mejora precisión.

    4) Define expectativas y contratos en el prompt

    Un prompt efectivo contiene: objetivo, criterios de éxito, límites y comandos permitidos. Ejemplo breve:

    • Objetivo: “Internacionalizar mensajes de error en src/errors.”
    • Criterio de éxito: “Tests de integración deben pasar y la clave i18n existir en cada error.”
    • Límite: “No modificar build/ ni archivos en vendor/.”
    • Comandos permitidos: “npm test, git add, git commit.”

    Esto evita cambios sorpresivos y deja claro qué auditar.

    Integración segura: autonomía con control

    La gran pregunta es siempre autonomía vs control. Claude Code pide confirmación antes de comandos destructivos; esa barrera debe mantenerse por defecto. Habilitar ejecución totalmente autónoma solo tiene sentido en entornos efímeros: contenedores Docker desechables o runners de CI con permisos mínimos.

    Patrón recomendado:

    • Local: Human-in-the-loop. Aprobar cambios críticos manualmente.
    • CI/CD: Sesiones automáticas dentro de contenedores con snapshot y rollback.
    • Producción: Nunca sin procesos de revisión y herramientas de observabilidad.

    Ejemplo de entorno efímero:

    docker run --rm -v $(pwd):/work -w /work node:18 bash -c "claude-code session --authed"
    (Ejecutar la CLI dentro de un contenedor permite pruebas reproducibles y segura reversión).

    Casos de uso donde Claude Code rinde como un pro

    Ejemplos claros donde aporta valor:

    • Onboarding técnico: “Lee src/ y genera un diagrama Mermaid de la arquitectura.” Resultado: documentación inicial y mapa de dependencias.
    • Refactorización transversal: “Sustituye libX por libY y ejecuta linter + tests.” Resultado: cambios aplicados + report.
    • Auditoría rápida: “Revisa el módulo de auth contra OWASP Top 10 y documenta hallazgos.” Resultado: lista priorizada de riesgos.
    • PR autopiloto: “Analiza esta rama, aplica fixes mínimos, y crea PR con descripción técnica y checklist de QA.”

    Métricas para demostrar ROI

    No es magia; mide impacto con indicadores concretos:

    • Tiempo medio para cerrar una tarea compleja (antes / después).
    • % de PRs que pasan CI en primera corrida.
    • Tiempo de onboarding de nuevos devs (documentación generada).
    • Reducción de errores por regresiones introducidas manualmente.

    Riesgos y cómo mitigarlos

    • Fugado de secretos: asegúrate de que la CLI no indexe .env con credenciales; usar vaults y secrets managers.
    • Cambios no revisados: habilita hooks que obliguen revisión humana en cambios críticos.
    • Sobredependencia: Claude Code acelera, no sustituye juicio. Mantén reglas de propiedad de código.

    Resumen rápido y acción inmediata

    Para empezar ya: instala la CLI, abre una sesión en un módulo pequeño, prueba un prompt de TDD completo (escribir tests → ejecutar → corregir) y ejecuta todo dentro de un contenedor temporal. Documenta los resultados y ajusta prompts.

    Si aprendes como usar Claude Code como un pro tendrás menos código parcheado y más flujos reproducibles. La terminal deja de ser un editor y se convierte en un orquestador: potente, pero bajo tu criterio.

    Dominicode Labs

    Si trabajas con agentes, automatización o workflows, considera continuar explorando patrones y experimentos en Dominicode Labs. Está diseñado como una continuación práctica para pruebas controladas y prototipos de integración.

    FAQ

    Respuesta: ¿Qué es Claude Code y para qué sirve?

    Claude Code es un agente que puede leer tu repositorio, ejecutar comandos, iterar sobre fallos y aplicar cambios, usado para acelerar flujos completos como refactorizaciones, tests y creación de PRs.

    Respuesta: ¿Cuándo debo abrir una sesión en la carpeta del módulo versus en la raíz?

    Abre la sesión en la carpeta del módulo cuando trabajes en una unidad cohesionada (microservicio o paquete). Evita la raíz en monorepos grandes para no invalidar caché y aumentar coste y latencia.

    Respuesta: ¿Qué debe incluir un prompt profesional?

    Debe contener objetivo, criterios de éxito, límites y comandos permitidos. Ejemplo: objetivo claro, tests necesarios, carpetas prohibidas y lista de comandos autorizados.

    Respuesta: ¿Cómo mitigo el riesgo de fugado de secretos?

    No indexes .env ni archivos con credenciales; usa vaults y secrets managers; filtra o mueve archivos sensibles antes de iniciar la sesión.

    Respuesta: ¿Es seguro habilitar ejecución autónoma en producción?

    No. Habilita ejecución autónoma solo en entornos efímeros y controlados. En producción exige revisiones humanas y observabilidad.

    Respuesta: ¿Qué métricas son útiles para medir ROI?

    Tiempo medio para cerrar tareas complejas, porcentaje de PRs que pasan CI en la primera corrida, tiempo de onboarding y reducción de errores por regresiones manuales.

    Claude Code (documentación oficial: documentación oficial) se comporta como un colaborador técnico: puede generar código, ejecutar tests y corregir errores basándose en la salida real del sistema.

  • Fundamentos del Spec-First Development para desarrolladores

    Fundamentos del Spec-First Development para desarrolladores

    Deja de vibe-codear: Fundamentos del Spec-First Development

    Tiempo estimado de lectura: 6 min

    • Spec-First invierte minutos en especificar para evitar horas de corrección posterior.
    • Sin una spec, los agentes (p. ej. Claude Code) completan huecos con suposiciones que rompen invariantes.
    • Una spec efectiva contiene contexto, contrato, restricciones y casos de uso.
    • Usa vibe coding para prototipos; usa Spec-First para producción y sistemas compartidos.

    Deja de vibe-codear: Fundamentos del Spec-First Development. Deja de vibe-codear: Fundamentos del Spec-First Development. Si confías en prompting improvisado para todo, acabarás con un sistema que “funciona” y nadie entiende. Spec-First Development no es paperwork; es el antídoto práctico contra las suposiciones que los agentes —incluido Claude Code— introducen cuando no hay una especificación clara.

    Resumen rápido (lectores con prisa)

    Spec-First Development: escribir la especificación mínima (contexto, contrato, restricciones, ejemplos) antes de implementar. Útil para producción y sistemas compartidos. Evita suposiciones de agentes y pérdida de consistencia arquitectónica. Usa vibe coding solo para prototipos.

    Fundamentos del Spec-First Development: por qué importa antes de abrir Claude Code

    Vibe coding acelera prototipos. Funciona hasta que el prototipo debe vivir en producción. Los agentes como Claude Code operan dentro de ventanas de contexto finitas; cuando esa ventana se cierra, el agente no recuerda decisiones previas y completa lagunas con suposiciones. Resultado: fragmentos correctos en aislamiento que, juntos, rompen invariantes del sistema.

    Spec-First Development cambia el orden: primero especificas el sistema mínimo necesario (contexto, contrato, restricciones, ejemplos), y luego pides al agente que implemente. Así conviertes a Claude en un ejecutor alineado, no en un improvisador.

    Fuentes útiles:

    Qué falla con el vibe coding en sistemas reales

    • Pérdida de memoria arquitectónica: cada sesión es una pizarra limpia; las decisiones previas no viajan implícitas.
    • Suposiciones silenciosas: el agente rellena huecos según heurísticas, no según tus invariantes.
    • Deuda de coherencia: el conjunto pasa tests unitarios pero falla en invariantes transversales; refactorizarlo es costoso.

    No es que los agentes sean malos. Es que sin especificaciones les pides que inventen el contexto del proyecto en cada interacción.

    Qué debe contener una spec efectiva (los 4 pilares)

    1. Contexto del sistema

    – Stack, rutas, estructura modular, patrones de estado y librerías permitidas.

    – Ejemplo: “Next.js (App Router), Zustand para estado cliente, servicios de microservicios en /services, convención kebab-case para nombres de archivo.”

    2. Contrato de la interfaz

    – Inputs (tipos), outputs (tipos), efectos secundarios permitidos, invariantes.

    – Ejemplo: “Función getUser(id: string): Promise. No realizar llamadas externas salvo a auth-service; no mutar objetos globales.”

    3. Restricciones y criterios de aceptación

    – Requisitos no funcionales: latencia, límites de dependencias, compatibilidad con versiones, criterios de seguridad.

    – Ejemplo: “Respuesta en <200ms p95; no usar librerías con licencia X; cobertura mínima 80% en pruebas unitarias.”

    4. Casos de uso y ejemplos de I/O

    – Un caso nominal, al menos un caso borde y comportamiento ante error.

    – Ejemplo: entrada JSON, salida esperada, salida esperada cuando falta un campo.

    Estos cuatro pilares evitan que el agente “sea creativo” donde no debe.

    Cómo integrar specs en tu flujo con Claude Code (pasos prácticos)

    1. Escribe la spec antes de abrir la sesión del agente

    – No la guardes en Google Docs. Ponla en el repo: SPEC.md junto al test file o como comentario estructurado en tests.

    2. Incluye la spec textual como primer contexto en el prompt

    – No resumas: copia y pega. El agente necesita reglas explícitas, no interpretaciones.

    3. Pide la implementación y las pruebas asociadas

    – Solicita código + tests unitarios que verifiquen los criterios de aceptación.

    4. Valida resultado contra la spec antes de mergear

    – Verde en CI no equivale a alineación arquitectónica. Comprueba invariantes, latencias, dependencias y contratos.

    5. Versiona la spec junto al código

    – Si cambian los requisitos, actualiza SPEC.md; la spec es parte del contrato del repo.

    Ejemplo mínimo de SPEC.md (esquema)

    • – Contexto: [stack, rutas, convenciones]
    • – Contrato: [firma, tipos, efectos secundarios permitidos]
    • – Restricciones: [latencia, dependencias, seguridad]
    • – Casos: [input nominal → output; caso borde; error esperado]
    • – Criterios de aceptación: [tests, performance, compatibilidad]

    Guardarlo en el repo reduce el ciclo “pregunta-respuesta” y elimina ambigüedades en prompts posteriores.

    Cuándo usar vibe coding y cuándo spec-first

    Vibe coding: validación rápida de concepto, experimentación aislada, exploración de bibliotecas.

    Spec-First: producción, microservicios compartidos, sistemas con múltiples mantenedores, integraciones críticas.

    No es blanco o negro: usa vibe para idear, spec-first para construir. Esa transición mental es la diferencia entre velocidad aparente y velocidad sostenible.

    Cierre: el coste real de no especificar

    Un agente sin spec es un colaborador talentoso sin briefing: produce soluciones plausibles que resuelven problemas distintos al que tienes. Escribir specs no es burocracia; es invertir minutos que ahorran horas de corrección y semanas de deuda técnica. Antes de abrir Claude Code, escribe la spec. Tu base de código te lo agradecerá.

    Para equipos que trabajan con automatización, agentes y workflows, una práctica complementaria es centralizar plantillas y ejemplos en un laboratorio interno. Más recursos y experimentos aplicados están disponibles en Dominicode Labs.

    FAQ

    Respuesta:

    Spec-First Development es la práctica de definir la especificación mínima necesaria (contexto, contrato, restricciones, ejemplos) antes de implementar el sistema o función.

    Respuesta:

    Antes de comenzar una tarea que vaya a producción, que implique integración entre equipos o que afecte invariantes transversales. Para prototipos rápidos puedes saltarla.

    Respuesta:

    La spec es un contrato operativo y minimalista pensado para ejecución y validación (tests, CI), no un documento extenso de diseño. Está orientada a la implementabilidad.

    Respuesta:

    Sí. Los agentes consumen la spec como contexto explícito y la usan para reducir suposiciones. Es crucial pegar la spec textual en el prompt o ponerla en el repo accesible.

    Respuesta:

    Contexto del sistema, contrato de la interfaz, restricciones y criterios de aceptación, y casos de uso con ejemplos de I/O.

    Respuesta:

    Se pierde trazabilidad entre versiones del código y sus requisitos; provoca divergencias, errores en integración y mayor deuda técnica. Versionar la spec junto al código evita ambigüedades.

  • Cómo usar httpResource() en Angular para manejar peticiones HTTP

    Cómo usar httpResource() en Angular para manejar peticiones HTTP

    httpResource() — Resource API para HTTP: Peticiones Reactivas con Signals

    ¿Te imaginas definir una petición HTTP como si fuera una variable y olvidarte de orquestar cancelaciones, estados y subscriptions? Eso es lo que propone httpResource() — Resource API para HTTP: defines los parámetros como señales y el recurso se actualiza solo cuando cambian las señales dependientes.

    Tiempo estimado de lectura: 3 min

    • Convierte llamadas de red en estado reactivo accesible desde Signals.
    • Automatiza cancelaciones y recargas cuando las señales dependientes cambian.
    • Reduce boilerplate respecto al patrón Observable + suscripción para patrones “estado → petición”.
    • No reemplaza a RxJS para orquestaciones temporales o streams continuos.

    Introducción

    httpResource() convierte llamadas de red en estado reactivo. Si trabajas con Angular y Signals, esto no es una mejora cosmética: es una forma distinta de pensar la carga de datos. En lugar de construir Observables, suscribirte y gestionar cancelaciones manuales, consumes estado.

    Resumen rápido (lectores con prisa)

    Definición: httpResource() expone peticiones HTTP como señales reactivas que actualizan automáticamente su estado al cambiar dependencias.

    Cuándo usarlo: Para cargas impulsadas por estado (filtros, rutas, paginación).

    Por qué importa: Simplifica cancelaciones automáticas, estados y recargas sin boilerplate.

    Limitación clave: No sustituye a RxJS para orquestación temporal o streams continuos.

    httpResource() — Resource API para HTTP: qué es y por qué importa

    La Resource API expone operaciones asíncronas como señales. httpResource() es la implementación para peticiones HTTP: envuelve la llamada y te devuelve señales listas para consumir (value, loading, error, status y un método reload).

    Angular documenta el enfoque reactividad en su guía oficial (ver Reactive primitives). El HttpClient sigue siendo el motor de la petición; httpResource() es la interfaz que lo conecta con Signals.

    Actualmente la API está en Developer Preview en las versiones recientes, por lo que conviene probar con criterio en proyectos que puedan tolerar cambios en la firma antes de que se estabilice.

    Cómo funciona, sin demasiada magia

    Piensa en httpResource() como un computed() que hace fetch. Definís una función que lee señales. Angular registra dependencias. Cuando cualquiera cambia:

    • la petición anterior se aborta si aún está en curso,
    • se lanza una nueva petición con los parámetros actualizados,
    • las señales (value, isLoading, error, status) reflejan el ciclo de vida automáticamente.

    Ejemplo mínimo

    readonly userId = signal(1);
    
    readonly userResource = httpResource(() => ({
      url: `/api/users/${this.userId()}`,
      method: 'GET'
    }));

    Cambia userId() y el recurso hace el resto. No hay switchMap, no hay takeUntil, no hay memoria de suscripciones.

    Qué recibes “de serie” y por qué eso importa

    Al usar httpResource() obtienes señales listas para consumir en la plantilla o en lógica reactiva:

    • .value() — datos resueltos.
    • .isLoading() — booleano para spinners/skeletons.
    • .error() — información del fallo.
    • .status()idle | loading | resolved | error.
    • .reload() — fuerza una re-ejecución.

    Esto elimina mucho boilerplate: ya no necesitas declarar isLoading, data, error y actualizar cada uno en handlers separados.

    Race conditions: ya no es una preocupación recurrente

    Las condiciones de carrera eran el talón de Aquiles cuando el usuario cambiaba filtros rápido y una respuesta tardía pisaba datos nuevos. Con httpResource() la cancelación es automática: si la señal que define la petición cambia, las peticiones intermedias se abortan. Resultado: datos consistentes y menos código defensivo.

    Eso no significa que el problema desaparezca en todos los contextos, pero sí que desaparece en el patrón más común: “estado cambia → cargar datos para la vista”.

    Cuándo usarlo — y cuándo no

    Usos recomendados

    • La obtención de datos está impulsada por estado (filtros, ruta, paginación).
    • Quieres minimizar boilerplate y unificar el modelo mental del equipo en Signals.
    • Buscas evitar errores por manejo manual de cancelaciones.

    Cuándo no usarlo

    • Necesitas orquestación compleja de eventos (WebSockets, SSE, streams continuos).
    • Requieres transformaciones temporales avanzadas (debounceTime, windowing, combinaciones complejas).
    • Estás construyendo pipelines de datos que dependen del tiempo y eventos más que del estado.

    RxJS sigue siendo la herramienta correcta para flujos de eventos; httpResource() es la herramienta correcta para “estado → petición” limpio y declarativo.

    Impacto arquitectónico real

    El cambio más importante no es técnico: es mental. Cuando el equipo compra la narrativa Signals-first, los componentes y su testing se vuelven más simples. Menos suscripciones olvidadas. Menos efectos colaterales. Mejor onboarding para quien llega nuevo al repo.

    No es magia: es coherencia. httpResource() reduce superficie para errores y acelera decisiones arquitectónicas sobre dónde debe vivir la lógica de carga de datos.

    Recursos y siguientes pasos

    Angular Reactivity Guide

    HttpClient (persistencia del motor)

    Si estás en Angular 19+ y ya trabajas con Signals, empieza por prototipar una pantalla con httpResource() y compara la legibilidad, tests y bugs con la versión RxJS/HttpClient. No lo adoptes por moda: mídelo. Y si en el prototipo funciona, lo siguiente es reescribir un flujo real y ver cuántas líneas de código desaparecen.

    Esto no acaba aquí: hay decisiones de testing, caching y error handling que merecen otra pieza. Si querés, escribo la segunda parte con patrones de testing y estrategias de cache para httpResource().

    FAQ

    ¿Qué es exactamente httpResource()?

    Es una implementación de la Resource API para peticiones HTTP que expone el resultado y el estado de la petición como señales reactivas. Envuelve el mecanismo del HttpClient y ofrece .value(), .isLoading(), .error(), .status() y .reload().

    ¿Cómo se integra con HttpClient?

    El HttpClient sigue siendo el motor que efectúa las peticiones. httpResource() actúa como la interfaz declarativa que lee señales y delega la ejecución y cancelación al HttpClient.

    ¿Qué señales ofrece por defecto?

    Provee .value() (datos), .isLoading() (booleano), .error() (detalle del fallo), .status() (idle | loading | resolved | error) y .reload().

    ¿El API cancela peticiones automáticamente?

    Sí. Si la señal que define la petición cambia mientras una petición está en curso, la petición anterior se aborta automáticamente para evitar condiciones de carrera comunes.

    ¿Cuándo debo seguir usando RxJS?

    Cuando necesitas orquestación compleja de eventos, streams continuos (WebSockets, SSE) o transformaciones temporales avanzadas (debounceTime, windowing, combinaciones complejas), RxJS sigue siendo la herramienta adecuada.

    ¿Es estable la API en producción?

    La API estaba en Developer Preview en versiones recientes; conviene probar con criterio en proyectos que puedan tolerar cambios en la firma antes de adoptarla ampliamente en producción.

  • Aprende sobre el nuevo Authoring Format en Angular y Signals

    Aprende sobre el nuevo Authoring Format en Angular y Signals

    Nuevo Authoring Format (Signal Components): qué es y cómo prepararte

    Tiempo estimado de lectura: 3 min

    Ideas clave

    • Nuevo Authoring Format: replantea la autoría de componentes para hacer la reactividad nativa en la sintaxis.
    • Dos enfoques: funciones con Signals vs Single File Components (SFC).
    • Impactos: cambia Language Service, compilador, tooling, ciclos de vida y compatibilidad con Web Components.
    • Recomendación práctica: migrar a Standalone Components, adoptar Signals y desacoplar lógica.

    Introducción

    El término Nuevo Authoring Format (Signal Components) aparece cada vez más en las discusiones oficiales de Angular. En las primeras líneas: el Nuevo Authoring Format (Signal Components) propone cambiar cómo se escriben los componentes, pasando de clases y decoradores a formatos más funcionales—ya sea funciones con Signals o Single File Components tipo Vue/Svelte. Esto no es un capricho sintáctico: tiene consecuencias profundas en compilador, tooling y arquitectura de aplicaciones.

    Resumen rápido (lectores con prisa)

    Qué es: Un nuevo formato de autoría para componentes de Angular que prioriza la reactividad nativa (Signals) y alternativas sintácticas funcionales o SFC.

    Cuándo usarlo: Es una dirección en discusión; hoy, adopta Standalone Components y Signals para prepararte.

    Por qué importa: Afecta inferencia de tipos, tooling, rendimiento y compatibilidad con Web Components.

    Cómo funciona (alto nivel): Componentes como funciones crean Signals y devuelven representación; SFC agrupa lógica y template en un archivo.

    Nuevo Authoring Format (Signal Components): qué está en juego

    Angular lleva años evolucionando: Signals, Zoneless y Standalone Components han sido el preludio. El siguiente paso es replantear la autoría de componentes para que la reactividad sea nativa en la sintaxis, no un patrón insertado dentro de clases. El equipo mantiene discusiones públicas (ver RFCs y hilos) y la dirección es clara aunque no definitiva.

    ¿Por qué importa? Porque cambiar la forma de autoría impacta en:

    • Inferencia de tipos y Language Service.
    • Minificación, tree-shaking y rendimiento del bundle.
    • Ergonomía del desarrollador (DX) y curva de aprendizaje.
    • Compatibilidad con Web Components y herramientas del ecosistema.

    Referencias útiles:

    Dos enfoques en discusión

    El equipo de Angular evalúa principalmente dos caminos. Ninguno está finalizado; pueden mezclarse o descartar ambos.

    Funciones con Signals

    • El componente es una función que crea Signals y devuelve la representación (o templates vinculados).
    • Elimina el contexto this, favorece closures y composición.
    • Ejemplo conceptual:
    export const UserCard = component(() => {
      const name = signal('Ada');
      const greeting = computed(() => `Hola, ${name()}`);
      return html`<p>${greeting()}</p>`;
    });

    Beneficios: compresión mejor por bundlers, composición natural, menos errores por binding de contexto.

    Single File Components (SFC)

    • Un archivo con bloques <script> y <template> (estilo .vue/.svelte).
    • Separación visual lógica/template sin el decorador @Component.
    • Mejora la legibilidad y facilita la adopción por desarrolladores nuevos.
    • Ejemplo conceptual:
    <script>
      const count = signal(0);
      const double = computed(() => count() * 2);
    </script>
    
    <template>
      <button (click)="count.set(count() + 1)">{{ double() }}</button>
    </template>

    Beneficios: DX clara, flujo secuencial, herramientas de análisis más directas.

    Impactos técnicos que debes considerar

    Language Service y tooling

    • Sin decoradores opacos, el Language Service puede ofrecer autocompletado y detección de errores más precisos en templates.
    • Requiere cambios en el análisis estático y en la forma en que el compilador Ivy mapea lógica y vista.

    Ciclos de vida y hooks

    • ngOnInit y ngOnDestroy podrían migrar a hooks funcionales reutilizables (p. ej. onMount, onDestroy), que facilitan testing y composición.

    Compatibilidad con Web Components

    • Web Components exigen kebab-case en nombres (p. ej. my-element). Un formato sin selector obliga a definir reglas para exportación como custom elements o mecanismos automáticos de derivación de selectores.

    Retrocompatibilidad

    • Angular tenderá a mantener convivencia entre modelos: las clases seguirán funcionando durante varias versiones, y la migración se hará mediante schematics y herramientas.

    Qué hacer hoy (criterio práctico para Tech Leads)

    No bloquees desarrollo ni intentes replicar experimentalmente la sintaxis en producción. Haz esto en su lugar:

    1. Migra a Standalone Components. Es el requisito técnico más claro para cualquier nuevo formato.
    2. Adopta Signals en tus componentes actuales: signal, computed, effect. Esto reduce la brecha conceptual entre hoy y el nuevo formato.
    3. Extrae lógica de negocio a funciones puras o servicios. Mantén la presentación lo más delgada posible. Así la migración será principalmente sintáctica.
    4. Mejora la cobertura de tests (unit + E2E) en formularios y flujos críticos; los cambios en autoría pueden exponer fricciones en bindings y hooks.
    5. Establece convenciones de naming y scripts de codemods para renombrados masivos: facilita que un futuro schematic haga el trabajo fino.

    Conclusión

    El Nuevo Authoring Format (Signal Components) no es solo una reforma estética: es la pieza que permite a Angular consolidar Signals como primer ciudadano del framework. La opción final (funciones vs SFC) aún no está cerrada, pero la dirección estratégica es evidente: menos decoradores, mejor inferencia, DX más directa y componentes concebidos para la reactividad desde el primer token.

    Prepara tu base de código: adopta Standalone Components, usa Signals y desacopla la lógica. Cuando Angular estabilice la sintaxis, no querrás estar rehaciendo arquitectura; querrás ejecutar schematics y seguir adelante.

    FAQ

    ¿Qué es el Nuevo Authoring Format (Signal Components)?

    Es una propuesta para cambiar la forma de escribir componentes en Angular, priorizando la reactividad nativa mediante Signals y alternativas sintácticas como componentes basados en funciones o Single File Components.

    ¿Cuáles son los enfoques que se están evaluando?

    Principalmente dos: componentes como funciones que crean Signals y devuelven la representación, y Single File Components (SFC) con bloques separados de <script> y <template>, al estilo .vue/.svelte.

    ¿Por qué afecta al Language Service y al tooling?

    Porque remover decoradores opacos y usar patrones funcionales implica cambios en el análisis estático. El Language Service puede ofrecer autocompletado y detección de errores más precisos si el compilador y mapeos entre lógica y vista se ajustan a la nueva sintaxis.

    ¿Cómo debo preparar mi código hoy?

    Migra a Standalone Components, adopta Signals (signal, computed, effect), extrae lógica de negocio a funciones puras o servicios y mejora la cobertura de tests para formularios y flujos críticos.

    ¿Qué pasa con la compatibilidad con Web Components?

    Hay que definir reglas de exportación y naming: Web Components exigen kebab-case en nombres (p. ej. my-element), por lo que un formato sin selector debe ofrecer mecanismos automáticos o convenciones para generar selectores compatibles.

    ¿Se mantendrán las clases y decoradores actuales?

    Sí. Angular tenderá a mantener convivencia entre modelos durante varias versiones y facilitará migraciones mediante schematics y herramientas; las clases seguirán funcionando mientras se realiza la transición.

  • Mejora la inferencia de tipos en TypeScript con el operador satisfies

    Mejora la inferencia de tipos en TypeScript con el operador satisfies

    satisfies operator: el operador de TypeScript que no estás usando

    Tiempo estimado de lectura: 5 min

    • Valida sin perder inferencia: El operador satisfies valida que una expresión cumpla un tipo sin cambiar el tipo inferido del valor.
    • Mejora la DX en repos grandes: Conserva literales y autocompletado donde las anotaciones tradicionales causan upcast o las aserciones apagan la seguridad.
    • Casos de uso claros: Constants, routes, design tokens y mapeos estáticos se benefician más.
    • Complemento con as const: Úsalo junto a as const para validación más inmutabilidad absoluta.
    • No es para runtime: No reemplaza validación en tiempo de ejecución para datos dinámicos.

    Introducción

    satisfies operator: el operador de TypeScript que no estás usando es la frase que deberías leer en todos los PRs donde alguien fuerza tipos con as o sacrifica la inferencia de literales. Introducido en TypeScript 4.9, satisfies arregla —sin drama— un problema de tipado que hemos parcheado mal durante años.

    Resumen rápido (lectores con prisa)

    Qué es: Un operador de TypeScript que valida que un valor cumple un tipo sin cambiar la inferencia del valor.
    Cuándo usarlo: Para constantes exportadas, rutas, tokens de diseño y mapeos estáticos donde quieres conservar literales.
    Por qué importa: Mantiene autocompletado y seguridad de tipos en grandes bases de código.
    Cómo funciona: Verifica el contrato en tiempo de compilación y deja intacta la inferencia literal.

    ¿Qué hace exactamente el satisfies operator y por qué importa?

    El operador satisfies valida que una expresión cumpla con un tipo, pero no cambia el tipo inferido del valor. Es decir: verifica el contrato y deja intacta la inferencia literal del valor. Eso suena pequeño; en equipos con bases de código grandes es una diferencia estructural.

    Comparación con anotación y aserción

    • La anotación const x: T = ... valida pero hace upcast: pierde literales.
    • La aserción const x = ... as T fuerza sin validar: apaga la seguridad.
    • const x = ... satisfies T valida y conserva la inferencia.

    Ejemplo práctico: tema de colores que no deberías arruinar

    Sin satisfies, acabas escribiendo código que obliga al IDE a perder información útil:

    type Color = string | [number, number, number];
    
    const theme: Record = {
      primary: "blue",
      secondary: [255, 0, 0]
    };
    
    theme.primary.startsWith("b"); // Error: theme.primary es Color, no string literal
    

    Con satisfies:

    const theme = {
      primary: "blue",
      secondary: [255, 0, 0]
    } satisfies Record;
    
    theme.primary.startsWith("b"); // OK — TypeScript sabe que es string
    theme.secondary[0];            // OK — sabe que es number
    

    Validación sin amputación de tipos. Eso es todo.

    Casos de uso donde satisfies aporta valor real

    – Configuraciones públicas (constants.ts). Valores exportados y consumidos desde varios módulos se benefician de mantener literales.
    – Rutas y diccionarios (routing). keyof typeof ROUTES debe devolver claves concretas, no string.
    – Design tokens y paletas. Necesitas diferenciar hex strings de tuplas RGB sin perder método de string/array.
    – API clients estáticos o mapeos entre endpoints y tipos de respuesta.

    Ejemplo de rutas

    type RouteConfig = Record;
    
    const ROUTES = {
      home:      { path: "/",    protected: false },
      dashboard: { path: "/app", protected: true  },
    } satisfies RouteConfig;
    
    // keyof typeof ROUTES => "home" | "dashboard"
    

    Si hubieras usado : RouteConfig, perderías esas claves y con ellas, seguridad y autocompletado.

    Patrón avanzado: satisfies + as const

    Úsalos juntos cuando quieras validación + inmutabilidad absoluta:

    const ENDPOINTS = {
      users:   "/api/users",
      session: "/api/session",
    } satisfies Record as const;
    
    // ENDPOINTS.users es literal "/api/users" y readonly
    

    Esto es ideal para inyectar constantes que se comparten por toda la app sin riesgo de mutación accidental.

    Cuándo no usar satisfies

    No es una bala de plata. No lo apliques en cada tipo local ni donde el valor no necesite exportarse o conservar literales.

    • Definición y consumo inmediato en el mismo scope → anotación clásica puede ser más clara.
    • Datos dinámicos desde la red → valida en runtime (Zod, io-ts) y transforma antes de confiar en tipos.

    Recuerda: satisfies es para diseño estático y claridad, no para validar payloads de clientes externos en producción.

    Cómo adoptarlo en un repo sin romper nada

    1. Audit rápido: busca ficheros constants, theme, routes, tokens.
    2. Busca patrones problemáticos: as const seguido de as Type, o const X: Record<...> = {...}.
    3. Reemplaza por satisfies donde quieras conservar literales.
    4. Añade tests de tipo (tsd) para casos críticos y ejecuta tsc --noEmit en CI.
    5. Actualiza guía de estilo y explica el porqué en CONTRIBUTING.md.

    Comando grep útil:
    grep -R --line-number -E "as const.*as |: Record<|: {[A-Za-z0-9_]+: .*}" src/

    Impacto en equipo y mantenimiento

    Adoptar satisfies reduce errores sutiles: llamadas a funciones con claves incorrectas, fallos de autocompletado que llevan a as any, y la necesidad de escribir casts defensivos. A nivel de DX, mejora el autocompletado y la intención del código. A nivel de arquitectura, reduce deuda técnica silenciosa: menos as T, menos // @ts-ignore.

    Cierre práctico

    No es una moda; es una herramienta ergonométrica del tipo system. Si tu repo exporta constantes que consumen otros módulos, haz una pasada hoy mismo y reemplaza las anotaciones que hacen upcast por satisfies. Empieza por constants.ts, theme.ts, routes.ts. Verás menos PRs con as any y más código que documenta intención y comportamiento real.

    Implementarlo es simple. Ignorarlo es caro. Esto no acaba aquí: la próxima vez que veas un as en un PR, pregúntate si deberías usar satisfies en su lugar.

    Referencia oficial (anuncio): la sección relevante y la documentación general.

    FAQ

    ¿Qué hace exactamente satisfies?

    Valida que una expresión cumple con un tipo en tiempo de compilación sin cambiar la inferencia literal del valor.

    ¿Cuándo debo usarlo en lugar de una anotación de tipo?

    Cuando quieras validar un valor pero conservar literales y autocompletado, especialmente en constantes exportadas o mapeos estáticos.

    ¿Puede reemplazar validación en runtime?

    No. Para datos dinámicos desde la red sigue utilizando validadores en runtime (Zod, io-ts) y transforma antes de confiar en tipos estáticos.

    ¿Cómo se combina con as const?

    Úsalos juntos para validación estática más inmutabilidad absoluta; por ejemplo, ... satisfies Record<string,string> as const mantiene literales y readonly.

    ¿Afecta a keyof typeof?

    Sí. Usar satisfies en mapeos permite que keyof typeof infiera claves concretas en lugar de string.

    ¿Romperá código existente si lo introduzco en un repo grande?

    En general no; es una herramienta de seguridad estática. Haz una migración por áreas (constants, theme, routes) y añade pruebas de tipo en CI.

    ¿Dónde empiezo una auditoría?

    Busca ficheros constants, theme, routes, tokens y patrones como as const seguido de as Type o : Record.