Tag: Angular

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

  • Implementación del Model Context Protocol en Angular 21

    Implementación del Model Context Protocol en Angular 21

    Hacer post sobre los MCPs de angular 21

    Tiempo estimado de lectura: 6 min

    • MCP estandariza cómo los agentes de IA leen, razonan y proponen cambios en repositorios Angular 21.
    • Orden de inspectores (list_projects → get_best_practices → search_documentation → find_examples → onpush_zoneless_migration) minimiza cambios destructivos.
    • MCP exige trazabilidad: cada recomendación debe incluir referencia a la documentación oficial y reglas del proyecto.
    • Integración práctica: CI/pipelines y IDEs deben ejecutar inspectores antes de ofrecer o aplicar cambios.
    • Riesgos reales: reduce alucinaciones pero no elimina límites del análisis estático; requiere revisión humana.

    Introducción

    Hacer post sobre los MCPs de angular 21 empieza por entender que no hablamos de una feature menor: hablamos de cómo los agentes de IA leerán, razonan y propondrán cambios en tu código sin romper la arquitectura. En Angular 21, donde Signals, Standalone Components y la migración zoneless son la norma, el Model Context Protocol (MCP) pasa de ser un extra a una herramienta de gobernanza técnica indispensable.

    Resumen rápido (lectores con prisa)

    Qué es: Un protocolo que estandariza cómo los modelos de lenguaje interactúan con repositorios para descubrir topología, consultar documentación y aplicar reglas antes de sugerir cambios.

    Cuándo usarlo: En monorepos, pipelines automatizados y entornos donde agentes de IA pueden proponer cambios en código base (migraciones, refactors, PRs).

    Por qué importa: Evita propuestas que compilan pero introducen deuda técnica; garantiza trazabilidad y reglas específicas por versión.

    Cómo funciona (resumen): Ejecuta inspectores en orden (list_projects → get_best_practices → search_documentation → find_examples → onpush_zoneless_migration) y adjunta referencias oficiales a cada recomendación.

    Por qué importa ahora

    Angular 21 consolida patrones que rompen supuestos antiguos: la inyección por constructor deja paso a inject(), la reactividad local se orienta a Signals y zone.js tiende a desaparecer. Un LLM no contextualizado propondrá soluciones que compilan pero introducen deuda técnica. El MCP asegura que el agente primero lea la topología y las reglas del proyecto y luego proponga —no al revés.

    Componentes del flujo MCP en Angular 21

    • Descubrimiento de topología: el agente usa Nx para mapear aplicaciones, librerías y dependencias mediante list_projects.
    • Inyección de prácticas por versión: get_best_practices detecta la versión y aplica reglas (p. ej. evitar NgModules cuando no proceden).
    • Validación documental en tiempo real: search_documentation consulta angular.dev antes de afirmar APIs o signaturas.
    • Ejemplos concretos y actualizados: find_examples recupera implementaciones modernas (Signals, inject(), formularios reactivos).
    • Coaching contextual: ai_tutor ajusta el nivel técnico según el rol del usuario.
    • Auditoría zoneless: onpush_zoneless_migration analiza compatibilidad al eliminar zone.js.

    Ejemplo práctico (flujo mínimo aplicable)

    1) Ejecuta list_projects: el agente devuelve el mapa del monorepo con apps y librerías.

    2) Ejecuta get_best_practices: se cargan reglas específicas para Angular 21 (uso de Signals, restricciones sobre RxJS local).

    3) Pide search_documentation para la API concreta (p. ej. Signals API).

    4) Solicita find_examples para ver implementaciones validadas.

    5) Si el objetivo es migrar, corre onpush_zoneless_migration y compila la lista de refactorizaciones.

    Este orden evita que el agente proponga cambios destructivos en zonas equivocadas del repo.

    Casos de uso concretos y recomendaciones

    • Revisiones automáticas de PR: integra MCP en pipelines de CI para que el agente haga una pre-auditoría. Usa n8n o tu runner de CI para ejecutar los inspectores en cada PR.
    • Onboarding y documentación viva: ai_tutor puede generar guías de cambios y checklist de migración adaptados al repositorio. Útil para equipos nuevos que deben adoptar Signals y patrones zoneless.
    • Auditorías zoneless: no confíes en una única ejecución. Los detectores estáticos identifican patrones vulnerables (suscripciones sin limpieza, efectos colaterales) pero requieren revisión humana en casos límite.

    Criterio técnico que debes aplicar

    • Nunca concedas permisos de escritura antes de ejecutar list_projects y get_best_practices.
    • Exige trazabilidad: cada recomendación debe venir con la referencia a la documentación oficial (angular.dev).
    • Valida migraciones zoneless con pruebas E2E y monitoreo en staging; los cambios en detección de cambios pueden aparecer solo en escenarios complejos.
    • Mantén una “zona de seguridad” en la que la IA puede proponer cambios no destructivos (documentación, tests, refactorizaciones no críticas) y otra donde solo humanos aprueban (cambios en librerías compartidas, infraestructuras críticas).

    Integración con herramientas (práctico)

    • IDEs con IA: plug-ins que implementen MCP deben ejecutar inspectores antes de ofrecer snippets.
    • Automatización: orquesta inspectores con n8n para que cada PR dispare una auditoría RAG (Read-Only).
    • Registro y auditoría: guarda outputs de inspectores (mapa de topología, reglas aplicadas, fragmentos de doc) junto al PR para trazabilidad histórica.

    Riesgos y límites reales

    MCP reduce alucinaciones, no las elimina totalmente. Hay límites del análisis estático: efectos en tiempo de ejecución, race conditions y casos complejos de detección de cambios pueden escapar. Además, dar permisos de escritura sin límites en monorepos empresariales sigue siendo una superficie de riesgo alta.

    Conclusión (lo que ganas)

    Hacer post sobre los MCPs de angular 21 no es solo hablar de una integración técnica; es establecer un contrato de confianza entre IA y equipo. Con MCP, los agentes dejan de ser generadores indiscriminados y pasan a ser asistentes que conocen tu repo, tus reglas y tus límites. Implementados con disciplina (orden de inspectores, trazabilidad y revisión humana), los MCPs reducen deuda técnica, aceleran migraciones y convierten la IA en parte fiable del flujo de desarrollo.

    Dominicode Labs

    Para equipos que orquestan agentes y pipelines, una referencia práctica de investigación y experimentación es Dominicode Labs. Integrar MCP con flujos de trabajo y pruebas reproducibles ayuda a mantener trazabilidad y mejorar la adopción de prácticas zoneless.

    FAQ

    ¿Qué es exactamente un MCP?

    Un Model Context Protocol (MCP) es un conjunto de inspectores y flujos estandarizados que permiten a modelos de lenguaje interactuar con un repositorio de forma gobernada: descubrir topología, cargar reglas de práctica por versión y validar documentación antes de generar cambios.

    ¿Cuándo debo ejecutar inspectores en mi flujo?

    Siempre antes de conceder permisos de escritura a un agente: al menos ejecutar list_projects y get_best_practices. Para migraciones, añadir search_documentation, find_examples y onpush_zoneless_migration.

    ¿Cómo garantiza el MCP que no se rompa la arquitectura?

    No lo garantiza por completo, pero reduce riesgos al exigir que el agente conozca la topología y las reglas específicas del proyecto antes de proponer cambios. Además obliga a adjuntar referencias y un plan de refactorización verificable.

    ¿Qué referencias documentales se deben adjuntar a las recomendaciones?

    Las referencias deben ser enlaces a la documentación oficial pertinente en angular.dev (por ejemplo la Signals API) y, cuando corresponda, recursos técnicos como repositorios oficiales (p. ej. zone.js).

    ¿Puede un MCP eliminar la necesidad de revisión humana?

    No. MCP reduce alucinaciones y añade trazabilidad, pero las decisiones críticas (cambios en librerías compartidas, infraestructuras) deben seguir pasando por revisión humana.

    ¿Cómo integrar MCP en CI con n8n?

    Orquesta los inspectores como pasos en la pipeline: cada PR dispara un flujo de RAG (Read-Only) en el que list_projects y get_best_practices se ejecutan primero, seguidos por validaciones documentales y generación de un reporte adjunto al PR. Una opción práctica es usar n8n para encadenar esos inspectores.

    ¿Qué precauciones tomar en migraciones zoneless?

    Validar compatibilidad con onpush_zoneless_migration, ejecutar pruebas E2E en staging y monitorizar cambios en detección de cambios. No confiar exclusivamente en análisis estático: casos de race conditions y efectos en tiempo de ejecución requieren supervisión humana.

  • Cómo diagnosticar errores CORS en Angular con responseType

    Cómo diagnosticar errores CORS en Angular con responseType

    responseType en HttpResponse: diagnóstico programático de CORS y Fetch API en Angular

    Tiempo estimado de lectura: 3 min

    • Diagnóstico preciso: responseType expone la clasificación de Fetch (basic, cors, opaque, error, opaqueredirect) para convertir status: 0 en información accionable.
    • Observabilidad: Usar interceptores para registrar fetchType permite distinguir fallos de red, bloqueos CORS y respuestas opacas.
    • Estrategia de reintentos: No aplicar reintentos genéricos; combinar responseType con límites y circuit breakers.
    • Compatibilidad: Habilitar Fetch con provideHttpClient(withFetch()) para acceder a esta propiedad; XHR no la expone.

    Introducción

    responseType en HttpResponse es la nueva propiedad que Angular expone en HttpResponse y HttpErrorResponse para reflejar el tipo de respuesta de la Fetch API (‘basic’, ‘cors’, ‘opaque’, ‘error’, ‘opaqueredirect’). En las primeras líneas: esto permite diagnosticar programáticamente fallos de red y bloqueos de CORS sin depender de DevTools ni de mensajes genéricos con status: 0.

    Resumen rápido (lectores con prisa)

    Qué es: una propiedad en HttpResponse/HttpErrorResponse que refleja Response.type de la Fetch API.

    Cuándo usarlo: cuando uses Fetch como motor en Angular (provideHttpClient(withFetch())).

    Por qué importa: convierte status: 0 en señales accionables (p. ej. distinguir ‘opaque’ de ‘error’).

    Cómo funciona: el navegador categoriza la respuesta (basic/cors/opaque/error/opaqueredirect) y Angular la expone en HttpResponse/HttpErrorResponse.

    responseType en HttpResponse: qué es y por qué importa

    La propiedad es la representación de Response.type de la Fetch API (documentada en MDN). Angular la hace accesible cuando el HttpClient usa Fetch como motor (activar con provideHttpClient(withFetch())). Si tu app sigue con XHR, no tendrás esta clasificación semántica porque XHR no expone esos metadatos.

    No confundas esto con la opción de petición responseType que define ‘json’|’blob’|’text’, ni con HttpEventType. Aquí hablamos de cómo el navegador trató la respuesta desde la perspectiva de seguridad/origen.

    Fuentes:

    Valores y significado práctico

    basic

    ‘basic’ — Same-origin. No hay CORS. Acceso completo a cabeceras y cuerpo.

    cors

    ‘cors’ — Cross-origin aprobado; el servidor ha respondido con las cabeceras CORS correctas.

    opaque

    ‘opaque’ — Petición cross-origin en modo no-cors: el navegador ejecuta la petición pero oculta cuerpo y cabeceras (respuesta inaccesible).

    error

    ‘error’ — Falla de red total: DNS, conexión rechazada o bloqueo de CORS severo que impidió la entrega.

    opaqueredirect

    ‘opaqueredirect’ — Redirección cross-origin en modo manual.

    Interpretar correctamente evita reintentos inútiles y mejora observabilidad: un ‘opaque’ no se arregla reintentando; es probable que la petición haya sido enviada en modo incorrecto o que falten cabeceras CORS.

    Implementación práctica: interceptor de diagnóstico

    Un interceptor global es el lugar natural para enriquecer logs y tomar decisiones automáticas:

    import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
    import { catchError, throwError } from 'rxjs';
    
    export const corsDiagnosticInterceptor: HttpInterceptorFn = (req, next) => {
      return next(req).pipe(
        catchError((error: HttpErrorResponse) => {
          if (error.status === 0) {
            // En versiones antiguas puede requerirse "as any" si no está tipado aún
            const fetchType = (error as any).responseType as string | undefined;
    
            if (fetchType === 'opaque' || fetchType === 'error') {
              // Log estructurado para observabilidad
              const payload = {
                tag: 'network:cors',
                fetchType,
                url: req.url,
                method: req.method,
                timestamp: new Date().toISOString()
              };
              // Ej: enviar a Sentry/Datadog/YourLogger
              console.error('[Network/CORS]', payload);
            }
          }
          return throwError(() => error);
        })
      );
    };

    Detalles prácticos: añade contexto (userAgent, entorno, versiones) y evita logs verbosos en clientes con alto tráfico. Etiqueta adecuadamente para filtrar incidentes de red en tu SIEM.

    Decisiones automáticas y recomendaciones

    Cómo actuar según responseType:

    • ‘error’ + status 0: marcar como fallo de infraestructura; no reintentar automáticamente; alertar al equipo infra.
    • ‘opaque’: auditar la configuración de la petición (¿se envía mode: ‘no-cors’ accidentalmente?), revisar proxies y CDN que puedan transformar la petición.
    • ‘cors’ con payload inválido: tratar como error de backend normal y aplicar reintentos controlados si procede.
    • ‘basic’: investigar conectividad local u origen compartido.

    Evita políticas de reintento genérico: una estrategia inteligente combina responseType con límites de reintentos y circuit breakers.

    Requisitos, tipado y compatibilidad

    • Habilita Fetch: provideHttpClient(withFetch()). (Ver: Angular HttpClient)
    • Tipado: en versiones intermedias de Angular puede ser necesario castear (error as any). Revisa las notas de versión si la propiedad aún no aparece en .d.ts.
    • Testing: en E2E/Browsers reales verifica valores de Response.type. En unit tests, simula HttpErrorResponse con responseType para validar rutas de logging.

    Observabilidad y tiempo de resolución

    La granularidad que aporta responseType reduce el time-to-detect y time-to-resolve. En entornos donde integras APIs de terceros, proxies corporativos o autenticación centralizada (OAuth), distinguir entre ‘opaque’ y ‘error’ te ahorra reproducir errores en local y te deja respuestas accionables en dashboards.

    Conclusión

    responseType en HttpResponse no es solo un campo más; es una palanca para convertir errores opacos en señales operativas. Si manejas integraciones cross-origin en producción, habilitar Fetch y aprovechar esta propiedad en interceptores y pipelines de observabilidad es una mejora tangible en resiliencia y diagnóstico.

    FAQ

    ¿Qué es exactamente responseType en HttpResponse?

    Es la representación de Response.type de la Fetch API expuesta por Angular en HttpResponse y HttpErrorResponse cuando HttpClient usa Fetch como motor.

    ¿Necesito cambiar a Fetch para verlo?

    Sí. Habilita Fetch con provideHttpClient(withFetch()). Si tu app sigue con XHR, esa clasificación no estará disponible.

    ¿Cómo distingue ‘opaque’ de ‘error’?

    ‘opaque’ indica una petición cross-origin en modo no-cors con cuerpo y cabeceras ocultas; ‘error’ indica una falla de red total (DNS, conexión rechazada o bloqueo severo de CORS).

    ¿Debo reintentar cuando veo status 0?

    No automáticamente. Usa responseType para decidir: por ejemplo, no reintentar en ‘error’ y auditar la configuración en ‘opaque’.

    ¿Dónde conviene registrar fetchType?

    En un interceptor global, con logs estructurados que incluyan contexto (userAgent, entorno, versiones) y etiquetas para filtrar en tu SIEM.

    ¿Cómo pruebo esto en tests?

    En E2E y navegadores reales verifica Response.type. En unit tests simula HttpErrorResponse con responseType para validar rutas de logging y comportamiento del interceptor.

  • Cómo Vitest se convierte en el test runner por defecto en Angular 21

    Cómo Vitest se convierte en el test runner por defecto en Angular 21

    Vitest como test runner por defecto en Angular 21

    Tiempo estimado de lectura: 3 min

    • Ideas clave:
    • Angular 21 define a Vitest como runner de tests por defecto, unificando pipeline con Vite/esbuild.
    • Ventajas técnicas: pipeline unificado, ESM-first, feedback más rápido en –watch, menos dependencias puente.
    • Riesgos a validar: compatibilidad con TestBed, zone.js, elección de entorno DOM y migración incremental en monorepos.
    • Estrategia recomendada: probar en branch, alinear vite.config.ts, migrar por iteraciones y mantener Karma/Jest hasta completar la transición.

    Vitest como test runner por defecto cambia algo más que un paquete en package.json: alinea el pipeline de pruebas con el mismo motor que compila y sirve tu aplicación (Vite/esbuild). En las primeras líneas: esto reduce discrepancias entre entorno de desarrollo y entorno de tests, acelera el feedback en modo watch y simplifica la gestión de módulos ESM. Angular 21 fija Vitest por defecto; Karma y Jest siguen soportados, pero la dirección es clara.

    Resumen rápido (lectores con prisa)

    Vitest es un runner sobre Vite optimizado para ESM y esbuild.

    Significa un pipeline unificado entre dev y tests, menos transformaciones duplicadas y feedback más rápido en –watch.

    Valida TestBed, zone.js y entornos DOM (jsdom vs happy-dom) antes de migrar.

    Migración recomendada por fases; mantener Karma/Jest hasta completar la cobertura en Vitest.

    Vitest como test runner por defecto: qué significa para tu arquitectura

    Vitest es un runner construido sobre Vite y optimizado para flujos ESM y esbuild. Al adoptar Vitest por defecto, Angular deja de tener dos pipelines distintos (uno para build/serve y otro para tests), lo que elimina una fuente frecuente de errores: transforms distintos, alias no replicados o compatibilidades ESM que “se rompen” solo en CI.

    Ventajas técnicas (no marketing)

    Pipeline unificado

    Same toolchain para dev y tests. Menos config duplicada = menos fallos de integración.

    ESM-first

    Vitest trata paquetes ESM nativos sin hacks; adiós a transformIgnorePatterns interminables.

    Feedback más rápido en –watch

    Vite conoce el grafo de módulos y re-ejecuta solo lo necesario, reduciendo tiempos de feedback en desarrollo.

    Menos “glue” y dependencias puente

    Menos presets y adaptadores como los que Jest requería en proyectos Angular.

    Cuando la herramienta de testing comparte transformaciones exactas con tu dev server, tus tests reflejan la realidad del runtime.

    Ejemplo mínimo de integración y scripts

    Un ejemplo práctico de lo que cambias en un proyecto Angular:

    package.json (scripts)

    {
      "scripts": {
        "test": "vitest",
        "test:watch": "vitest --watch",
        "test:ci": "vitest --run"
      }
    }

    vite.config.ts (fragmento)

    import { defineConfig } from 'vite';
    import { angular } from '@analogjs/vite-plugin-angular'; // ejemplo de integración si aplica
    
    export default defineConfig({
      test: {
        environment: 'happy-dom', // o 'jsdom' según tus necesidades
        globals: true
      }
    });

    Estos archivos muestran la convergencia: tu vite.config.ts puede centralizar transformaciones y alias que antes había que duplicar para Jest o Karma.

    Riesgos y puntos a validar antes de migrar

    • TestBed y APIs específicas de Angular: sigue funcionando, pero revisa utilidades que dependan de polyfills o zone.js. Algunas configuraciones de test legacy pueden requerir ajustes.
    • Entornos DOM: Vitest soporta jsdom y happy-dom; valida cuál reproduce mejor tu suite (render tests, componentes con animaciones, etc.).
    • Monorepos y grandes bases de tests: migrar toda la suite puede ser costoso de golpe; planifica migraciones por paquetes o por dominios funcionales.
    • Cold starts en CI: aunque Vitest suele ser rápido, en suites masivas el rendimiento en cold start depende mucho de la configuración y del entorno del runner (compara con tus números reales).

    Estrategia práctica de migración (tech-lead friendly)

    1. Probar en un branch: instala Vitest y configura un ejemplo mínimo (un módulo o paquete).
    2. Alinear vite.config.ts: centraliza las transformaciones que usas en dev (alias, plugins).
    3. Comparar modos: ejecutar la suite en modo watch y en CI (run) para comparar tiempos y fallos.
    4. Migración por iteraciones: migrar grupos de tests por feature o carpeta, ajustando mocks y environment según sea necesario.
    5. Mantener Karma/Jest activos: mantenerlos durante la transición; eliminar cuando el 100% de tests pasen en Vitest y la monitorización confirme estabilidad.

    Si quieres una herramienta rápida para estimar el impacto, mide:

    • Tiempo total de test en CI antes/después.
    • Tasa de falsos negativos/positivos detectados por cambio de runner.
    • Tiempo medio de feedback en dev (–watch).

    CI/CD: ganancias reales

    En CI obtendrás beneficios prácticos:

    • Menor complejidad en runners (menos paquetes, menos instalación).
    • Startup más rápido y menor consumo de memoria en contenedores.
    • Pipeline más predecible al usar el mismo empaquetador que en dev.

    Ejemplo breve (GitHub Actions step):

    - name: Run tests
      run: npm ci && npm run test:ci

    Sin Webpack en la ecuación, la instalación y ejecución suelen ser más ágiles.

    Cuándo no migrar aún

    • Si tienes una suite de tests con integración muy fina en Jest (configuraciones Nx o monorepos con dependencias internas) y el coste de migración supera el beneficio inmediato.
    • Si dependes de plugins de Jest que no tienen equivalentes en Vitest y que son críticos para tu flujo.

    En esos casos, planifica la migración como iniciativa de medio plazo: arranca con nuevos proyectos y módulos para ganar experiencia.

    Conclusión

    Vitest como test runner por defecto en Angular 21 no es solo una nueva opción: es la consolidación de una decisión de coherencia técnica. Para proyectos nuevos, adopta Vitest. Para proyectos con Karma, planifica la migración cuanto antes. Para entornos Jest maduros, valora ROI y migra por fases. La apuesta central aquí es menos complejidad operativa, menos errores por discrepancias de herramientas y tests que realmente reflejan el comportamiento en producción.

    Lecturas y recursos:

    FAQ

    Respuesta: Vitest es un test runner construido sobre Vite, optimizado para flujos ESM y esbuild. Comparte transformaciones con el dev server de Vite, lo que reduce discrepancias entre desarrollo y tests.

    Respuesta: Angular 21 fija Vitest por defecto para alinear pipelines, reducir duplicación de configuración y mejorar la coherencia entre runtime de desarrollo y entorno de tests.

    Respuesta: Validar compatibilidad de TestBed y APIs específicas de Angular, dependencia en zone.js o polyfills, y elegir el entorno DOM adecuado (jsdom vs happy-dom).

    Respuesta: Depende de tu suite. jsdom puede ser más completo en APIs DOM; happy-dom suele ser más ligero. Probar ambos según componentes y animaciones es la vía correcta.

    Respuesta: Migrar por paquetes o dominios funcionales, no todo de golpe. Medir impacto en cada iteración y mantener herramientas legacy activas hasta validar cobertura completa en Vitest.

    Respuesta: Sí. Mantener Jest o Karma durante la transición es recomendable. Eliminar solo cuando todos los tests pasen en Vitest y la monitorización confirme estabilidad.

    Respuesta: Medir tiempo total de test en CI antes/después, tasa de falsos negativos/positivos por cambio de runner y tiempo medio de feedback en dev (–watch).

  • Migración a Signal Forms en Angular 22: Mejorando formularios reactivos

    Migración a Signal Forms en Angular 22: Mejorando formularios reactivos

    Signal Forms estable: el nuevo estándar de formularios en Angular 22

    Tiempo estimado de lectura: 4 min

    • Signal Forms reemplaza Observables por Signals nativos para exponer valor, validez y estados de control.
    • Mejora ergonomía y rendimiento al evitar suscripciones manuales y emitir reactividad síncrona y dirigida.
    • Encaja con una arquitectura Zoneless para re-rendering quirúrgico y menor sobrecarga en formularios complejos.
    • Migración pragmática: usar toSignal() y desacoplar validadores reduce el coste de adopción.

    Introducción

    Signal Forms estable aparece como la evolución natural que consolida el manejo de formularios basado en Signals en Angular 22. Signal Forms estable reemplaza la dependencia de RxJS en la capa de interfaz por un modelo de reactividad síncrono y explícito, alineado con la arquitectura Zoneless y Signals centrales del framework.

    La propuesta no es solo sintaxis; es un cambio de ergonomía y rendimiento. Aquí explico qué cambia, por qué importa y cómo preparar una migración pragmática en proyectos reales.

    Resumen rápido (lectores con prisa)

    Signal Forms expone estado de formularios como Signals nativos en lugar de Observables. Use Signals para leer estado y computed() para derivar valores síncronos. Migración pragmática: convertir valueChanges a Signals con toSignal() y desacoplar validadores.

    ¿Qué es Signal Forms estable y qué problema resuelve?

    Signal Forms estable expone el estado del formulario —valor, validez, touched/dirty— como Signals nativos en lugar de streams Observables. Los problemas que resuelve de forma directa:

    • Evita gestión manual de suscripciones (memory leaks).
    • Elimina desfases causados por emisiones asíncronas en validaciones cruzadas.
    • Encaja de forma nativa con una arquitectura Zoneless, donde Signals notifican de forma quirúrgica qué partes del DOM actualizar.

    Fuente y discusión activa sobre el diseño: discusiones en GitHub. Para contexto sobre Signals y reactividad en Angular: guía de Signals en Angular.

    Cambio conceptual: de observar eventos a leer estado derivado

    Con ReactiveFormsModule hoy suelen usarse propiedades como valueChanges o statusChanges (Observables). Signal Forms cambia el patrón: en lugar de suscribirte, lees un Signal o creas computed() que derive estados complejos.

    Comparativa rápida:

    // ReactiveForms (actual)
    readonly isValid$ = this.form.statusChanges.pipe(
      map(s => s === 'VALID'),
      distinctUntilChanged()
    );
    
    // Signal Forms (conceptual)
    readonly isValid = computed(() => this.form.status() === 'VALID');
    

    computed() es síncrono, no requiere teardown manual y se integra con el grafo de dependencias para reevaluar solo cuando los valores relevantes cambian.

    Validaciones cruzadas y tracking de dependencias

    Las validaciones cruzadas son donde RxJS más fricción introduce: operadores para evitar bucles, distinct checks, y micro-delays. Con Signals, el runtime realiza dependency tracking: si una validación depende de A y B, se reevaluará solo cuando A o B cambien, sin operadores adicionales ni emisiones redundantes.

    Esto reduce tanto complejidad como carga en el hilo principal en formularios con muchos campos interrelacionados (CRMs, ERPs).

    Integración con Zoneless y pipeline de rendimiento

    La llegada de Signal Forms completa el modelo Zoneless (ver: guía Zoneless). En una app Zoneless:

    • Cada actualización de control escribe en un Signal.
    • Angular identifica qué templates dependen de ese Signal.
    • Solo esos nodos se re-renderizan.

    Resultado: menos trabajo innecesario en eventos de alta frecuencia y trazas de depuración limpias (sin contaminación por Zone.js). Para contexto sobre migraciones Zoneless y Signals, consulta: guía de Signals en Angular.

    Ejemplo práctico de interoperabilidad temporal

    Antes de la estabilización completa, la forma pragmática de probar el patrón es convertir valueChanges a Signal con toSignal (rxjs-interop).

    import { toSignal } from '@angular/core/rxjs-interop';
    
    this.formValue = toSignal(this.form.valueChanges, { initialValue: this.form.value });
    
    computed(() => {
      const value = this.formValue();
      // derivaciones y validaciones sincronas aquí
    });
    

    Esto ofrece un puente entre ReactiveForms y lo que Signal Forms hará nativo.

    Guía de rxjs-interop: guía de rxjs-interop.

    Estado de la API y compatibilidad futura

    La API está en RFC y discusión, y lo más probable es que conviva con ReactiveFormsModule durante varias versiones para minimizar rupturas. El equipo de Angular apunta a compatibilidad con validadores existentes y a proporcionar herramientas de migración (schematics) en el momento del lanzamiento estable.

    Sigue las discusiones oficiales: discusiones en GitHub.

    Estrategia práctica para Tech Leads y equipos

    No reescribas todo hoy. Sí aplica estas medidas para reducir el coste de migración:

    • Desacopla la lógica de validación y transformación del FormGroup. Mantén funciones puras o servicios para reglas de negocio.
    • Introduce toSignal() donde tenga sentido para que los templates y la lógica consuman estado de forma síncrona.
    • Establece ChangeDetectionStrategy.OnPush en componentes nuevos para asegurar un modelo de render predecible.
    • Automatiza pruebas E2E que cubran flujos de validación cruzada y efectos secundarios.
    • Reserva una fase de migración por componentes: empezar por formularios sencillos y luego los complejos.

    Checklist mínimo antes de activar Signal Forms en producción

    • Lógica desacoplada (validadores fuera del FormGroup).
    • Cobertura de pruebas para validación y envíos.
    • Observabilidad en producción para comparar métricas (TTI, LCP).

    Conclusión: por qué importa para tu arquitectura

    Signal Forms estable no es solo una API nueva: es la culminación de la transición de Angular hacia reactividad explícita y rendimiento previsiblemente escalable. Para proyectos a largo plazo, representa menos boilerplate, menor riesgo de fugas de memoria y una integración natural con la estrategia Zoneless.

    Prepara tu código hoy —desacopla, prueba y adopta interoperabilidad con toSignal— y tu equipo tendrá una migración suave cuando Angular 22 estabilice Signal Forms. La mejora no será estética: será tangible en rendimiento y mantenibilidad.

    Fuentes y recursos

    FAQ

    Respuesta: ¿Qué es Signal Forms?

    Signal Forms expone el estado del formulario —valor, validez, touched/dirty— como Signals nativos en lugar de Observables, permitiendo lecturas síncronas y derivaciones con computed().

    Respuesta: ¿Cuándo debería considerar migrar a Signal Forms?

    Considéralo cuando busques reducir suscripciones manuales, eliminar desfases en validaciones cruzadas, o al adoptar una arquitectura Zoneless para mejoras de rendimiento predecible.

    Respuesta: ¿Cómo afecta Signal Forms a las validaciones cruzadas?

    El runtime de Signals realiza dependency tracking, por lo que las validaciones que dependen de múltiples campos solo se reevaluarán cuando cambien esas dependencias, evitando emisiones redundantes y operadores adicionales de RxJS.

    Respuesta: ¿Puedo mezclar ReactiveForms y Signal Forms?

    Sí. Antes de la estabilización completa, una estrategia pragmática es convertir valueChanges a Signal con toSignal() (rxjs-interop) para interoperabilidad temporal.

    Respuesta: ¿Qué beneficios de rendimiento puedo esperar?

    Menos re-renderings innecesarios, menos trabajo en eventos de alta frecuencia y menor riesgo de fugas por manejo de suscripciones. La integración Zoneless permite re-rendering quirúrgico de los nodos que dependen de un Signal.

    Respuesta: ¿Qué precauciones antes de activar en producción?

    Asegura lógica desacoplada (validadores fuera del FormGroup), cobertura de pruebas para validación y envíos, y observabilidad en producción para comparar métricas (TTI, LCP).

  • Cómo Angular 21 Optimiza la Asincronía Sin Zone.js

    Cómo Angular 21 Optimiza la Asincronía Sin Zone.js

    Sin Zone.js — Native Async/Await: Angular 21 y la arquitectura Zoneless

    Sin Zone.js — Native Async/Await es el cambio arquitectónico que Angular 21 trae para cerrar años de parches y trampas alrededor de la asincronía. En lugar de confiar en interceptores globales, el framework apuesta por Signals y async/await nativo, lo que redefine cómo se detectan cambios, cómo se escribe lógica asíncrona y cómo se depura una aplicación Angular a escala.

    Resumen rápido (lectores con prisa)

    Angular 21 elimina Zone.js y usa Signals como fuente de verdad. Usa async/await nativo sin envoltorios. Resultado: detección de cambios localizada, trazas de error más limpias y menos bundle inicial.

    Cuándo: migraciones planificadas por fases. Cómo: mover estado a signals y usar OnPush; auditar subscribes y callbacks externos.

    Tiempo estimado de lectura

    Tiempo estimado de lectura: 5 min

    Ideas clave

    • Signals reemplazan a Zone.js: reactividad explícita y renderizado localizado.
    • Async/await nativo: sin transpile ni parches que oculten trazas y comportamiento.
    • Mejoras medibles: bundle inicial más pequeño, trazas limpias y menos comprobaciones globales.
    • Migración requiere auditoría: refactor de suscripciones y adopción de OnPush y signals.

    Sin Zone.js — Native Async/Await: qué cambia y por qué importa

    Zone.js surgió como una solución pragmática: interceptar (monkey‑patch) APIs del navegador —setTimeout, fetch, Promises, addEventListener— para saber cuándo lanzar la detección de cambios. Funcionó, pero con costes claros: comprobaciones globales innecesarias, trazas de pila contaminadas y fricción con APIs modernas. Más aún, Zone.js no intercepta await a nivel de motor V8, lo que obligó a técnicas de transpile para mantener el comportamiento esperado.

    Angular 21 elimina esa capa. En su lugar, Signals actúan como la fuente de verdad: cuando un signal cambia, Angular calcula qué partes de la vista dependen de él y actualiza solo esas piezas. El async/await se usa tal cual lo diseñó ECMAScript —sin envoltorios ni polyfills— y el navegador ejecuta la asincronía de manera óptima.

    Documentación clave:

    Cómo funciona el modelo Zoneless en práctica

    La filosofía es simple: reactividad explícita y renderizado localizado. Un servicio realiza una llamada asíncrona con async/await, actualiza signals y la vista reacciona únicamente a esos cambios.

    Ejemplo: servicio con signals

    @Injectable({ providedIn: 'root' })
    export class UserService {
      readonly users = signal([]);
      readonly loading = signal(false);
    
      async loadUsers(): Promise {
        this.loading.set(true);
        try {
          const response = await fetch('/api/users');
          this.users.set(await response.json());
        } finally {
          this.loading.set(false);
        }
      }
    }

    Ejemplo: template con signals

    En el template, consumir signals es directo y elimina muchos patrones previos (pipes async, subscribes que mutan propiedades de clase):

    <if (loading()) {>
      <app-skeleton />
    <} @else {>
      @for (user of users()) {
        <app-user-card [user]="user" />
      }
    }

    Angular gestiona internamente las dependencias entre signals y templates; no necesitas Zone.js para “avivar” la vista.

    Beneficios técnicos medibles

    • Reducción del bundle: eliminar Zone.js suele suponer ~100 KB minificados menos en el bundle inicial, con impacto directo en LCP.
    • Trazas de error limpias: las stack traces muestran tu código, no los callbacks internos de Zone.js, lo que reduce tiempo de diagnóstico.
    • Rendimiento en runtime: eventos de alta frecuencia (scroll, mousemove) dejan de disparar comprobaciones globales; solo los cambios efectivos actualizan la UI.
    • Compatibilidad natural con APIs modernas: fetch, Web Streams, WebSockets y librerías modernas funcionan sin trampas de detección.

    Riesgos y puntos a auditar antes de migrar

    Eliminar Zone.js no es solo quitar un import. Patrones comunes que romperán incluyen:

    • Mutar propiedades de clase dentro de subscribe() esperando que la vista se refresque automáticamente.
    • Dependencia implícita en NgZone.run() para actualizaciones desde callbacks externos.
    • Componentes sin ChangeDetectionStrategy.OnPush que confían en comprobaciones globales.

    Identifica y refactoriza estos puntos antes de desactivar Zone.js.

    Estrategia práctica de migración

    Pasos prácticos recomendados para una migración segura y por fases.

    Checklist rápida

    1. Establece ChangeDetectionStrategy.OnPush en toda la base de componentes.
    2. Migrar estado de componentes a signal() y computed(). Empieza por componentes hoja.
    3. Reemplaza suscripciones RxJS que mutan estado por toSignal() (@angular/core/rxjs-interop) o por flujos que actualicen signals explícitamente.
    4. Ejecuta pruebas E2E y de accesibilidad; valida rendimiento en escenarios reales.
    5. Activa el modo zoneless (p. ej. provideExperimentalZonelessChangeDetection() o su equivalente estable) cuando el 100% del estado dependiente de la vista esté en signals.

    Checklist rápida para auditoría antes de activar zoneless:

    • Todos los componentes críticos usan OnPush.
    • No existen mutaciones de estado implícitas en subscribes.
    • CI ejecuta pruebas de integración que cubren flujos asíncronos.
    • Observabilidad en producción: trazas y métricas para comparar comportamiento pre/post migración.

    Conclusión: menos magia, más control

    Sin Zone.js — Native Async/Await no es una moda; es la materialización de un principio: reactividad explícita y alineada con las plataformas. Para equipos que priorizan rendimiento, claridad y una depuración más rápida, Angular 21 ofrece un modelo más predecible y eficiente.

    Empieza la migración por componentes críticos, automatiza las pruebas de integración y planifica la adopción en fases: la reducción de complejidad será visible y medible. En la próxima entrega publicaremos una checklist automatizable y scripts de migración para acelerar este proceso en proyectos reales.

    FAQ

    ¿Qué es exactamente lo que reemplaza a Zone.js en Angular 21?

    Signals y el uso de async/await nativo. Signals actúan como la fuente de verdad para dependencias y actualizaciones de vista.

    ¿Por qué las trazas de error mejoran sin Zone.js?

    Porque se elimina el monkey‑patching y las trampas que insertan callbacks intermedios en las stack traces; las trazas muestran código de la aplicación y no callbacks internos.

    ¿Qué patrones rompen al desactivar Zone.js?

    Patrones que mutan estado dentro de subscribe(), dependencia en NgZone.run() y componentes que esperan comprobaciones globales en lugar de OnPush y signals.

    ¿Cuánto se reduce el bundle al quitar Zone.js?

    Eliminar Zone.js suele suponer aproximadamente 100 KB minificados menos en el bundle inicial, según la observación mencionada en el artículo.

    ¿Cuál es el primer paso práctico para migrar?

    Establecer ChangeDetectionStrategy.OnPush en los componentes y mover estado a signal() y computed(), empezando por componentes hoja.

    ¿Debo activar el modo zoneless inmediatamente en producción?

    No. Activarlo cuando el 100% del estado dependiente de la vista esté en signals y después de haber ejecutado pruebas E2E y de integración que validen flujos asíncronos.
  • Cómo evitar memory leaks en aplicaciones Angular

    Cómo evitar memory leaks en aplicaciones Angular

    Frontend Memory Leaks y como evitarlo en Angular

    Tiempo estimado de lectura: 3 min

    • Identifica suscripciones, listeners y timers que sobreviven a componentes.
    • Prefiere async pipe y herramientas integradas (takeUntilDestroyed) para evitar fugas.
    • Diagnostica con Chrome DevTools: heap snapshots y comparación de instancias.
    • Disciplina de equipo: code review, linters y políticas de invalidación en singletons.

    Introducción

    ¿Tu SPA se vuelve lenta sin error en consola? Frontend Memory Leaks y como evitarlo en Angular debería ser parte de tu checklist antes de enviar a producción. Si no controlas referencias, suscripciones y listeners, la app “come” memoria hasta que la pestaña muere.

    Resumen rápido (lectores con prisa)

    Qué es: Objetos no referenciados siguen vivos y el Garbage Collector no los elimina.

    Cuándo usar: Siempre que tu SPA cree y destruya componentes, gestiona suscripciones, listeners y timers.

    Por qué importa: Fugas causan degradación silenciosa de rendimiento en sesiones largas y móviles.

    Cómo funciona: Evita referencias persistentes: async pipe, takeUntilDestroyed, Renderer2 y limpieza en ngOnDestroy.

    Frontend Memory Leaks y como evitarlo en Angular: qué pasa y por qué importa

    Un memory leak ocurre cuando objetos que ya no necesitas siguen referenciados y el Garbage Collector no los elimina. En Angular esto duele más: la app vive sin recargas, los componentes se crean y destruyen, y cualquier referencia residual se acumula en el heap.

    No es magia ni culpa del navegador: es disciplina de código. Y sí, pasa en producción, en móviles y en equipos con pestañas cientos abiertas.

    Fuentes útiles:

    Vectores comunes y cómo cerrarlos

    Suscripciones RxJS sin gestionar

    • Qué pasa: .subscribe() vive más que el componente. El callback mantiene la instancia viva.
    • Soluciones: Async pipe (templates), takeUntilDestroyed() en Angular 16+, o takeUntil con un destroy$ en versiones anteriores.
    • Docs: RxJS takeUntil ; Angular rxjs-interop

    Event listeners globales

    Qué pasa: window.addEventListener('resize', fn) sin removeEventListener mantiene la referencia.

    Solución: usar Renderer2.listen() (retorna un unlisten) o guardar la referencia y llamarla en ngOnDestroy().

    Timers (setInterval, setTimeout)

    Qué pasa: un timer que cierra sobre this impide la recolección.

    Solución: almacenar el id y clearInterval/clearTimeout en ngOnDestroy().

    Servicios singleton con datos retenidos

    Qué pasa: providedIn: 'root' vive toda la sesión; si almacenas grandes estructuras sin limpieza, la memoria crece.

    Solución: políticas explícitas de invalidación / límites / weak references lógicas.

    Patrones prácticos (código)

    Async pipe — la opción más simple

    <!-- template -->
    <div *ngIf="data$ | async as data">{{ data.title }}</div>
    

    El async se suscribe y se desuscribe automáticamente cuando el componente muere.

    takeUntilDestroyed() — Angular 16+ (recomendado para TS)

    import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
    
    this.service.stream()
      .pipe(takeUntilDestroyed())
      .subscribe(x => this.handle(x));
    

    Patrón clásico (Angular <16)

    private destroy$ = new Subject<void>();
    
    ngOnInit() {
      this.service.stream()
        .pipe(takeUntil(this.destroy$))
        .subscribe(...);
    }
    
    ngOnDestroy() {
      this.destroy$.next();
      this.destroy$.complete();
    }
    

    Listeners con Renderer2

    private unlisten: () => void;
    
    ngOnInit() {
      this.unlisten = this.renderer.listen('window', 'scroll', this.onScroll.bind(this));
    }
    
    ngOnDestroy() {
      this.unlisten?.();
    }
    

    Timers

    private intervalId?: ReturnType<typeof setInterval>;
    
    ngOnInit() {
      this.intervalId = setInterval(() => this.poll(), 5000);
    }
    
    ngOnDestroy() {
      clearInterval(this.intervalId);
    }
    

    Cómo diagnosticar fugas — proceso práctico

    1. Abre Chrome DevTools → Memory (Chrome DevTools Memory).
    2. Toma Heap Snapshot.
    3. Navega al componente sospechoso, repite la interacción que crees filtra memoria.
    4. Regresa y toma otro snapshot.
    5. Compara: si ves instancias de tu componente o Detached DOM nodes, hay fuga.

    Allocation timeline te muestra crecimiento en tiempo real: una línea que solo sube y no baja es una mala señal.

    Política de equipo: lo que realmente previene fugas

    Esto no es sólo técnica, es disciplina:

    • Regla: preferir Async Pipe cuando sea posible.
    • Regla: toda suscripción en clase debe documentarse: ¿se cancela? ¿por quién?
    • Code reviews: busca .subscribe( sin takeUntil / takeUntilDestroyed o sin async.
    • Linters y tareas CI: detecta patrones peligrosos (por ejemplo .subscribe( sin unsubscribe en componentes).
    • Tests de rendimiento en CI: integra un paso de e2e que simule navegación repetida y monitorice memoria.

    Cierre: no cures síntomas, cambia hábitos

    Un memory leak no es un bug aislado; es deuda técnica que crece silenciosamente. Angular te da herramientas —async pipe, takeUntilDestroyed, Renderer2— pero depende del equipo usarlas consistentemente.

    Si quieres, en Dominicode podemos publicar una checklist de code-review y un snippet de ESLint que detecte suscripciones peligrosas. No te prometo magia: te prometo menos pestañas explotando. Apúntate y seguimos.

    FAQ

    ¿Qué es un memory leak?

    Un memory leak ocurre cuando objetos que ya no necesitas siguen referenciados y el Garbage Collector no los elimina. En una SPA esto provoca acumulación de memoria en el heap mientras la sesión continúa.

    ¿Cómo detectarlo en Angular?

    Usa Chrome DevTools → Memory: toma heap snapshots antes y después de interacciones repetidas. Busca instancias de componentes o Detached DOM nodes que no desaparecen.

    ¿Cómo manejar suscripciones RxJS?

    Prefiere async en templates, o en código use takeUntilDestroyed() (Angular 16+) o takeUntil con un destroy$ y limpieza en ngOnDestroy().

    ¿Y los listeners globales?

    No uses addEventListener sin remover. Usa Renderer2.listen() y llama al “unlisten” en ngOnDestroy().

    ¿Qué hacer con timers e intervalos?

    Guarda los ids retornados por setInterval/setTimeout y llama a clearInterval/clearTimeout en ngOnDestroy().

    ¿Los servicios singleton pueden filtrar memoria?

    Sí. Los servicios providedIn: 'root' viven toda la sesión; si almacenan grandes estructuras sin limpieza la memoria crece. Define políticas de invalidación y límites.

  • Cómo proteger adecuadamente prompts y API Keys en Angular

    Cómo proteger adecuadamente prompts y API Keys en Angular

    ¿Quieres que tu app con IA no termine viralizando tus secretos y vaciando la cuenta bancaria en una semana?

    Tiempo estimado de lectura: 6 min

    • Arquitectura segura: nunca llames al LLM desde el frontend; usa un BFF que gestione prompts, moderación y keys.
    • Secrets y governance: guarda API keys y prompts sensibles en un Secret Manager y versiona prompts como código.
    • Protecciones técnicas: short-lived tokens, rate limiting, moderación previa y sanitización post-respuesta.
    • Operaciones y observabilidad: logs con hashes, traceId, métricas de coste y playbook de emergencia (kill switch, rotación).
    • Defensa contra Prompt Injection: whitelist schemas, bloquear patrones instructivos y anclar reglas en servidor.

    Poca gente lo dice tan claro: esconder prompts y llaves en el frontend no es un fallo “feo”. Es criminal desde el punto de vista técnico y ético. Si llegas a producción así, te van a robar prompts, te van a reventar la factura y vas a explicar a Compliance por qué alguien generó 10k imágenes con tu API key.

    Voy a darte el patrón completo para proteger prompts en Angular. Código útil. Amenazas reales. Y la lista exacta de cosas que debes tener antes de pulsar “deploy”. Sin postureo. Directo al grano.

    Resumen rápido (lectores con prisa)

    Arquitectura: el frontend solo recoge input y JWT; el BFF guarda prompts, valida, modera y llama al LLM desde un entorno seguro. No keys en cliente. Usa Secret Manager, tokens cortos, rate limits y logging con hashes.

    Arquitectura correcta (la única que deberías usar)

    1) Cliente Angular

    Recoge únicamente el input del usuario y el JWT de sesión. Nunca contiene secrets ni prompts.

    2) BFF (Backend For Frontend)

    Recibe input, aplica prompt engineering y reglas de negocio, valida, hace moderación, llama al LLM y devuelve JSON seguro.

    3) Secret Manager

    Almacena API keys y prompts sensibles. (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault).

    4) Observability y Auditing

    Logs anónimos, métricas, traceId por petición, control de costes.

    5) Governance

    Políticas de privacidad, retención y consentimiento.

    No dejes la caja fuerte abierta en la vitrina: el BFF es la caja fuerte. El cliente es la ventana de la tienda.

    Cómo funciona el flow seguro (resumido)

    • Angular → BFF: sólo userInput + JWT.
    • BFF valida, aplica promptVersion, moderación y rate-limit.
    • BFF llama al LLM con la API Key desde su entorno seguro.
    • BFF sanitiza la respuesta y devuelve JSON acotado a Angular.
    • BFF guarda metadatos: promptId, promptVersion, hash(userInput), traceId.

    Código: ejemplo mínimo (BFF Node/Express)

    No hay keys aquí. Sólo patrón.

    // bff/routes/ai.js
    const express = require('express');
    const router = express.Router();
    const { validateJwt, rateLimiter } = require('../middleware');
    const { getPromptById } = require('../prompts');
    const { callLLM, moderate } = require('../llm-client');
    
    router.post('/generate', validateJwt, rateLimiter, async (req, res) => {
      const { promptId, userInput } = req.body;
      if (!promptId || !userInput) return res.status(400).send({ error: 'invalid' });
    
      // 1) Moderación preliminar
      const isSafe = await moderate(userInput);
      if (!isSafe) return res.status(403).send({ error: 'input-blocked' });
    
      // 2) Recupera prompt seguro desde secret store / filesystem controlado
      const promptTemplate = await getPromptById(promptId); // server-side only
    
      // 3) Ensamblar prompt en servidor
      const finalPrompt = promptTemplate.replace('{{user}}', userInput);
    
      // 4) Llamada segura al LLM (keys en server env o secret manager)
      const llmResp = await callLLM(finalPrompt);
    
      // 5) Sanitizar/filtrar salida antes de devolverla
      const safeOutput = sanitize(llmResp);
    
      // 6) Log mínimo: promptId + hash(input) + traceId
      logAudit({ userId: req.user.id, promptId, inputHash: hash(userInput), traceId: req.headers['x-trace-id'] });
    
      res.json({ result: safeOutput });
    });
    
    module.exports = router;
    

    Angular: lo básico (solo enviar input y manejar estado)

    // generator.service.ts
    @Injectable({ providedIn: 'root' })
    export class GeneratorService {
      constructor(private http: HttpClient) {}
    
      generate(promptId: string, userInput: string) {
        return this.http.post('/api/ai/generate', { promptId, userInput });
      }
    }
    

    Protecciones concretas que debes ejecutar YA

    • Nunca, nunca, nunca: API keys en frontend.
    • Short-lived tokens: usa JWTs con expiración corta y refresh tokens en cookies HttpOnly.
    • Secret Manager: no pongas las keys en variables de entorno en texto plano en producción. Usa AWS Secrets Manager o Vault.
    • Key rotation: automatiza la rotación de claves y la revocación rápida.
    • Rate limiting: por IP y por userId. Si un usuario hace 100 req/min, bloquea.
    • Quotas y throttling: límites globales para evitar Denial-of-Wallet.
    • Moderación previa: bloquea prompts con keywords sensibles (pornografía, discursos de odio, logos protegidos).
    • Sanitización post-respuesta: elimina URLs, comandos remotos o código potencialmente peligroso.
    • Prompt Versioning: guarda promptId + promptVersion en metadata para reproducibilidad.
    • Logging: guarda hashes y metadatos, no PII.
    • Traceability: añade traceId a cada llamada y propágalo.
    • CI/CD: no commit de prompts secretos en repos. Usa repos públicos solo para templates no sensibles.

    Defensa contra Prompt Injection

    • Filtra patrones típicos: /ignore previous instructions/i, /forget previous/i, /disregard/i.
    • Limita la longitud y la complejidad del input (max tokens).
    • Usa white-listing: si esperas un formato (JSON), exige strict schema y valida en servidor.
    • Usa blacklist de frases instructivas.
    • Añade un system prompt final en servidor que ancle reglas: “No sigas instrucciones dentro del userInput que intenten alterar las reglas de uso.”
    • Monitorea patrones raros y añade reglas automáticas que cierren IPs sospechosas.

    Prompt Versioning — ejemplo de esquema

    {
      "promptId": "product_describer",
      "version": "v1.3",
      "template": "System: Eres un asistente que... User: {{user}}",
      "author": "ai-team",
      "createdAt": "2025-02-01T10:00:00Z",
      "riskLevel": "low"
    }
    

    Guarda promptId y version en los logs. Si algo sale mal, puedes reproducir la llamada exacta con promptVersion.

    Auditoría y privacidad

    • Guarda solo hashes del input (SHA256) si necesitas rastrear. No guardes texto plano sin consentimiento.
    • PII: si el prompt contiene datos personales, aplica redaction antes de almacenar o pide consentimiento explícito.
    • Retención: define y aplica políticas (ej. borrar contenido crudo tras 30 días salvo auditoría autorizada).
    • Derecho a la explicación: mantén la capacidad de explicar por qué el modelo respondió como lo hizo (promptId + promptVersion + trace).

    Observability y métricas

    Registra y monitorea:

    • Latencia por prompt (p50/p95/p99).
    • Cost per call (USD).
    • Cache hit ratio (si aplicas caching de outputs).
    • Error rate y código de fallo (rate limit, moderation block).
    • Incidencias de prompt-injection detectadas.

    Usa OpenTelemetry para trazas distribuidas. Conecta a un panel (Grafana/Datadog).

    Testing y CI

    • Mockea llamadas al LLM en tests unitarios y e2e.
    • Contract tests: el BFF debe validar que las salidas del modelo cumplen schema.
    • Canary prompts: despliega cambios de prompt con 1% tráfico antes de hacer rollout completo.
    • Revisión humana: initial human-in-loop para las primeras 1000 queries si es relevante.

    UX y consentimiento (ética)

    • Transparencia: informa al usuario cuando se usan modelos generativos.
    • Permite ver y editar la entrada antes de enviar.
    • Opción de optar por no almacenar (opt-out) de registros de prompts con PII.
    • Mecanismo de reporte: “Reportar respuesta inapropiada” que levante ticket y almacene evidencia.

    Operaciones de emergencia (playbook)

    • Rotación de clave: botónes y scripts para revocar y crear nueva key.
    • Kill switch: endpoint que detiene todas las llamadas a LLM en caso de abuso.
    • Monitoreo de gasto: alertas si coste diario supera umbral.
    • Plan de comunicación: plantilla para informar a usuarios si hay filtración de prompts (qué se filtró, impacto, pasos).

    Checklist de seguridad mínimo (imprime y cumple)

    • [ ] BFF en producción que haga todas las llamadas a LLM.
    • [ ] API keys en Secret Manager, no en repo.
    • [ ] Rate limiting y quotas por usuario.
    • [ ] Moderación automática en el BFF.
    • [ ] Prompt versioning e identificación en metadatos.
    • [ ] Logging con hash, no PII.
    • [ ] TraceId y observabilidad (OpenTelemetry).
    • [ ] CI para cambios de prompt y canary rollout.
    • [ ] Política de retención y consentimiento del usuario.
    • [ ] Kill switch y rotación de claves automatizada.

    Cierre (qué hacer ahora)

    Si tu app ya está en producción y llamas al LLM desde Angular: deténlo ahora. Sí, ahora. Migrar a BFF te cuesta tiempo, pero recuperar la reputación y la factura te costará mucho más.

    Si no has llegado a producción: implementa el BFF como primera tarea del sprint. Versiona prompts. Habilita moderación. Añade observabilidad.

    ¿Quieres el kit listo para pegar en tu repo? Tengo:

    • BFF Node/Nest con example routes, secret manager integration, rate limiting y moderation.
    • Angular client mínimo seguro (JWT + promptId).
    • Scripts de CI para canary prompt releases.

    Responde “QUIERO EL KIT” y te lo mando con tests e2e y documentación. No lo digo por decir: esto no acaba aquí. Si lo haces bien, reduces riesgos y proteges a tus usuarios. Si lo haces mal, tu factura y tu nombre pagarán el precio. ¿Empezamos?

    Dominicode Labs

    Si buscas ejemplos, plantillas y kits para desplegar arquitecturas seguras con IA y workflows reproducibles, revisa Dominicode Labs como continuación lógica a lo expuesto en este artículo. Encontrarás recursos orientados a BFFs, secret managers y pruebas canary de prompts.

    FAQ

    ¿Por qué no debo poner la API key en el frontend?

    El navegador es público: todo lo que pones en bundle.js se puede leer. Si la API key está en el frontend, cualquiera puede usarla para consumir la API y generar gasto o filtrar prompts.

    ¿Qué hace exactamente el BFF?

    El BFF valida JWT, aplica prompt engineering, modera el input, recupera prompts desde un secret store, llama al LLM con las keys desde entorno seguro, sanitiza la respuesta y registra metadatos (promptId, promptVersion, hashes, traceId).

    ¿Cómo evito prompt injection?

    Valida esquema (whitelisting), bloquea patrones instructivos (p. ej. “ignore previous instructions”), limita longitud, modera antes de enviar al LLM y ancla reglas en un system prompt final en servidor.

    ¿Qué es prompt versioning y por qué importan las versiones?

    Versionar prompts como código (id, version, author, changelog, riskLevel) permite reproducibilidad y auditoría: con promptId+promptVersion puedes reproducir la llamada exacta si hay un incidente.

    ¿Qué registros debo almacenar para auditoría?

    Guarda hashes (SHA256) del input, promptId, promptVersion, traceId y metadatos. No almacenes texto plano con PII sin consentimiento y aplica políticas de retención (ej. borrar crudos tras 30 días).

    ¿Qué hacer si detecto uso abusivo o un pico de coste?

    Activa el kill switch para detener llamadas al LLM, rota y revoca keys, bloquea IPs/usuarios con rate limiting y ejecuta el playbook de comunicación y mitigación.

  • Optimiza tu flujo de trabajo en Angular con Copilot y Cursor

    Optimiza tu flujo de trabajo en Angular con Copilot y Cursor

    ¿Quieres que tu equipo deje de pelear con boilerplate y empiece a diseñar producto? Bien. Esto va de eso: Copilot para Angular —pero con criterio— usando Cursor o v0 para acelerar sin autodestruir la arquitectura.

    Tiempo estimado de lectura: 6 min

    Ideas clave

    • IA acelera pero no sustituye la decisión técnica: reduce boilerplate, pero aumenta la necesidad de revisar arquitectura.
    • Combina herramientas: v0 para UI, Cursor para integración contextual y Copilot para completados rápidos.
    • Implanta reglas y prompts: un archivo .cursorrules obliga convenciones modernas (Angular 17+, Standalone, Signals).
    • Proceso humano obligatorio: PRs generados por IA necesitan revisión, tests adversarios y auditoría.

    Poca gente lo dice así: las herramientas de IA no vienen a reemplazar programadores. Vienen a cambiar qué tipo de trabajo hacemos. Antes te peleabas con imports y TestBed; ahora peleas por decisiones arquitectónicas. Eso no es menos trabajo. Es trabajo de más valor. Y si no lo gestionas, la IA te regala código rápido… y deuda técnica a la misma velocidad.

    Resumen rápido (lectores con prisa)

    IA para desarrollo: genera boilerplate y UI rápido. Úsala para ahorrar tiempo en markup y tests repetitivos, pero exige reglas de proyecto (Angular 17+, Standalone, Signals), revisión humana y prompts versionados. Combina v0 para UI, Cursor para integración contextual y Copilot para completados.

    Primero: la promesa y el peligro, en dos líneas

    • Promesa: generar componentes, servicios y tests en segundos. Menos tiempo en boilerplate, más tiempo en lógica.
    • Peligro: aceptar el output sin auditarlo. La IA copia patrones viejos. Te regala “Angular 12” en 2026 si no le pones reglas.

    Por qué Angular es el mejor campo de juego para Copilot/Cursor

    Angular es verboso. Tiene DI, módulos, lifecycles y muchas opciones correctas. Esa verbosidad es precisamente la palanca: una IA bien dirigida elimina ruido repetitivo. Pero ojo: la IA no entiende tu contrato de negocio. Tú sí. Tu trabajo pasa de “escribir” a “auditar y decidir”.

    Herramientas y cuándo usarlas (sin romantizar)

    v0 (Vercel)

    genial para prototipos visuales. Saca HTML y clases Tailwind rápido. Úsalo para wireframes y para acelerar markup. No le pidas estado ni efectos.

    Cursor

    el copiloto contextual. Lee tu repo, entiende servicios existentes y genera pruebas y refactors con sentido. Aquí es donde pides migraciones a Signals o refactors RxJS → Signals.

    GitHub Copilot

    útil para completar bloques. No te fíes para refactors completos.

    Pauta: combina

    v0 para UI, Cursor para integrar y Copilot para atajos. Uno más: exige al modelo un estilo de proyecto (Angular 17+, Standalone, Signals). Si no lo pides, no lo obtendrás.

    Patrón de trabajo ideal (workflow)

    1. Idea rápida: pide a v0 un HTML/Tailwind para el UI.
    2. Traducción: pega ese HTML en Cursor y pide “Crea componente Standalone de Angular, usa Signals, inject()”.
    3. Integración: pide a Cursor que conecte el componente al servicio existente (o que genere el servicio tipado).
    4. Testing: pide a Cursor que cree TestBed y mocks. Revisa assertions.
    5. Revisión humana: valida arquitectura, performance y seguridad. Siempre humano.

    Prompting: si no controlas, te entregan legado

    La calidad del resultado depende del prompt. No es magia: las reglas importan. Debes forzar APIs modernas y convenciones de equipo. Crea un archivo de reglas para tu IDE, que el modelo verá como “estándar del proyecto”.

    Plantilla mínima de reglas (.cursorrules)

    No es opcional. Es tu contrato con la IA.

    // .cursorrules
    AngularVersion: 17+
    Components: Standalone true
    State: Prefer Signals (signal, computed, effect)
    DI: Use inject() where possible
    Templates: Use @if/@for syntax for control flow
    Testing: Generate Jest or Karma with explicit mocks, no shallow copy
    RxJS: Use only when necessary; prefer Signals for UI derivations
    Style: Strict typing, avoid any, include interfaces for API responses

    Usos concretos y prompts que funcionan (ejemplos prácticos)

    Convertir BehaviorSubject a Signals

    Prompt: “Refactoriza este servicio que usa BehaviorSubject para usar signal() y computed(). Mantén la API pública idéntica y añade tests que verifiquen que las suscripciones actúan igual.”

    Crear TestBed con spies

    Prompt: “Genera un TestBed para MyComponent. Mockea ApiService y AuthService con jasmine.createSpyObj. Crea tests para: carga inicial, error de API, y validación de formulario.”

    Generar componente desde HTML (v0 → Cursor)

    Flujo: Pide a v0 el HTML. Luego: “Convierte el HTML en MyWidgetComponent standalone con Inputs: title:string, data: any[]; usa Tailwind classes y Signals para estado local.”

    Testing: la IA acelera, pero no delegues la verificación

    Esto es serio: la IA crea tests de forma automática. Eso es bueno. Pero hay trampa: la IA escribe assertions que validan lo que ella generó. Si ella asumió un comportamiento erróneo, tus tests pasarán en verde. Por eso:

    • Revisa cada expect: ¿está validando la regla de negocio o solo el output esperado?
    • Genera tests adversarios: pídele a la IA que escriba “tests rotos” que simulen edge cases.
    • Introduce revisión humana obligatoria en PRs para tests generados por IA.

    Refactorizaciones a gran escala: estrategia segura

    Si vas a migrar una base entera a Signals o a Standalone components, hazlo por fases:

    1. Identifica módulos críticos.
    2. Refactoriza servicios primero (asegura contratos).
    3. Refactoriza componentes sendero a sendero (uno o dos features por PR).
    4. Usa tests generados por IA + validación manual.
    5. Monitor de runtime: telemetría para detectar regresiones.

    Ejemplo de prompt para migración a Signals (listo para pegar)

    “Refactoriza el archivo user.service.ts para reemplazar BehaviorSubject con signal(). Mantén la API externa (getUser, updateUser) sin cambios. Añade computed properties para isLoggedIn y profileSummary. Escribe tests unitarios que verifiquen comportamiento de suscripciones y actualización de estado.”

    Código ejemplo: TestBed generado por IA (y qué revisar)

    La IA suele producir este bloque. Úsalo pero revisa nombres y assertions.

    beforeEach(async () => {
      const apiSpy = jasmine.createSpyObj('ApiService', ['getData', 'update']);
      await TestBed.configureTestingModule({
        imports: [MyStandaloneComponent],
        providers: [{ provide: ApiService, useValue: apiSpy }]
      }).compileComponents();
    });

    Revisar:

    • ¿Se mockea todo lo necesario? (Router, HttpClient, etc.)
    • ¿Los spies devuelven observables cuando se espera observables?
    • ¿Los tests usan fakeAsync/tick cuando interactúan con timers o zonas?

    Costes, velocidad y gobernanza

    • Rentabilidad: la IA reduce horas humanas pero aumenta la necesidad de revisiones. Calcula ROI: horas ahorradas vs tiempo de code review extra.
    • Versionado: trata los prompts como código. Guarda versiones de prompts y .cursorrules.
    • Auditoría: obliga a generar un changelog de cada PR producido con IA: qué partes generó la IA y qué partes editó un humano.

    Antipatrones que veo (y que debes bloquear)

    • Merge automático de PRs con tests generados por IA sin revisión.
    • Usar Copilot para “arreglar” errores de producción; terminarás con soluciones temporales eternas.
    • Pedir a la IA que “optimice rendimiento” sin métricas. La IA sugiere microoptimizations; pide pruebas de mejora antes de aceptar.

    Estrategias para equipos (roles y responsabilidades)

    • Senior devs: definir .cursorrules, revisar PRs críticos, decidir migraciones a Signals.
    • Mid devs: usar tools para producir código, ejecutar tests, proponer mejoras.
    • Juniors: enfocarse en pruebas, documentación y tareas guiadas. La IA es su acelerador, no su profesor único.

    Métrica que importa realmente

    No midas líneas generadas. Mide:

    • Tiempo de entrega de features reales.
    • Reversiones/regresiones post-PR IA.
    • Cobertura de código útil (tests que validan la lógica).
    • Deuda técnica introducida por PRs IA (contada en horas de revisión).

    Cultura: sin ella, la IA es peligro

    La IA potencia malos hábitos si tu equipo no cambia procesos. Establece:

    • PR review obligatorio para código generado por IA.
    • Etiquetas en PRs: “generated-by-IA”.
    • Sesiones regulares para ajustar prompts y .cursorrules.

    Ejemplo de checklist mínimo para PR con IA

    • [ ] ¿Se aplicaron rules (Angular 17, Standalone, Signals)?
    • [ ] ¿Se generaron tests? ¿Validan reglas de negocio?
    • [ ] ¿Hay mocks correctos y casos edge?
    • [ ] ¿Se ejecutó E2E en staging?
    • [ ] ¿Se documentaron cambios en prompts?

    Cierre: lo que debes hacer hoy mismo

    No esperes a que el resto del equipo “se adapte”. Empieza con estas tres acciones:

    1. Crea .cursorrules en la raíz del repo. Hazla obligatoria.
    2. Configura un endpoint interno para almacenar prompts aprobados y su versionado.
    3. En la próxima PR que use IA, reserva 30 minutos para auditar tests y arquitectura. Si tu equipo lo hace una vez, lo hará siempre.

    ¿Quieres que te haga las reglas para tu repo (Angular 17, Signals, Jest, Tailwind)? Respóndeme “Quiero las reglas” y te devuelvo un .cursorrules y 10 prompts listos para usar: componentes, servicios, migraciones y tests. No es magia. Es disciplina con atajos inteligentes. ¿Lo quieres ahora?

    Dominicode Labs

    Si tu flujo incluye automatización, agentes o workflows relacionados con IA, considera documentar y versionar prompts y reglas como parte de la gobernanza. Para más recursos y ejemplos prácticos sobre process-driven prompts y gobernanza de IA técnica, visita Dominicode Labs.

    FAQ

    ¿La IA reemplazará a los desarrolladores?

    No. La IA cambia el tipo de trabajo: menos esfuerzo repetitivo y más decisiones arquitectónicas y de negocio. Necesita supervisión humana constante.

    ¿Cuál es la combinación ideal de herramientas?

    v0 para UI y prototipos, Cursor para refactors e integración contextual, y Copilot para completados. Usa cada herramienta para su fortaleza y exige reglas de proyecto.

    ¿Qué debe contener .cursorrules?

    Reglas de versión de Angular, convención de componentes (Standalone), preferencia por Signals, DI con inject(), control de templates, testing estricto y estilo tipado. El archivo de ejemplo está en el artículo.

    ¿Cómo evito que la IA introduzca deuda técnica?

    Imponiendo reglas, revisiones humanas, versionado de prompts, PRs por fases y telemetría en runtime para detectar regresiones.

    ¿Los tests generados por IA son fiables?

    Pueden ser útiles, pero la IA a menudo valida su propio output. Revisa asserts, añade casos adversarios y obliga revisión humana.

    ¿Qué proceso de revisión recomiendas para PRs generados por IA?

    Revisión obligatoria por un senior, checklist que verifique reglas del proyecto, ejecución de E2E en staging y documentación de qué generó la IA y qué editó un humano.

  • Cómo usar IA en Angular correctamente y evitar la deuda técnica

    Cómo usar IA en Angular correctamente y evitar la deuda técnica

    ¿Sabes qué es peor que no usar IA en Angular? Usarla mal y creer que todo está bien.

    Tiempo estimado de lectura: 6 min

    • La IA no es neutral: los modelos tienden a reproducir código antiguo y antipatrón, lo que genera deuda técnica.
    • Exige reglas y prompts versionados: pide arquitectura y restricciones, no solo “haz un componente”.
    • Revisa PRs IA-assisted rigurosamente: checklist, métricas y explicación humana obligatoria.

    Introducción

    Poca gente lo dice en voz alta: la IA no es neutral. Los modelos devuelven lo más probable según lo que han visto… y lo que han visto es código antiguo, tutoriales desactualizados y montones de antipatrón. Resultado: miles de PRs que “funcionan” pero son bombas de deuda técnica con temporizador. Y tú, con suerte, te enteras cuando el bundle explota en producción.

    Esto no es una bronca moral. Es una advertencia práctica.

    La mayoría de desarrolladores Angular usan mal la IA (y ni siquiera lo saben). Lo hacen porque la IA les da lo que piden y ellos no piden con criterio. Piden “haz un componente” y reciben una reliquia: NgModules, BehaviorSubjects por todos lados, .subscribe() sin limpieza y cero Signals. Compilan. Rompen en escala.

    Si eres Tech Lead, Senior o simplemente alguien que no quiere que su app sea un Frankenstein, esto es para ti. Voy a darte lo que nadie te dice en conferencias polidas: cómo usar la IA con Angular sin meter la pata, prompts que funcionan y políticas que evitan que el repo se convierta en un cementerio de buenas intenciones.

    Resumen rápido (lectores con prisa)

    La IA no es neutral: los modelos devuelven las soluciones más probables según su entrenamiento.

    Mucho del código base de entrenamiento corresponde a Angular antiguas (8–12), antes de Standalone Components y Signals.

    Pide arquitectura y restricciones (prompts versionados); exige explicación humana y tests para cualquier PR asistido por IA.

    Mide PRs IA-assisted por rework, MTTR y memory leaks; si suben, la IA está degradando tu producto.

    Primero: lo que realmente está pasando

    La IA aprende de repositorios públicos. Mucho del código que alimenta estos modelos fue escrito para Angular 8–12.

    Angular cambió. Standalone Components, Signals, Control Flow y takeUntilDestroyed son la nueva guardia.

    Los LLMs siguen escribiendo código vintage. Te lo sirven envuelto y listo para mergear.

    No es culpa del modelo. Es culpa de quien no lo contextualiza.

    La diferencia entre “funciona” y “es mantenible”

    La diferencia entre “funciona” y “es mantenible” son decisiones que la IA no puede tomar por sí sola. Tú sí puedes.

    Tres antipatrones que detectas rápido (y que matan tu app lentamente)

    1) Reactividad híbrida

    Un PR mezcla BehaviorSubjects con Signals y subscribes por doquier. Resultado: re-renders inesperados, condiciones de carrera y debugging por mensajes de stack largos. Si lo ves, pide explicaciones claras: ¿por qué RxJS aquí y no Signals?

    2) Suscripciones huérfanas

    Código que usa .subscribe() sin takeUntilDestroyed o sin unsubscribe. Esto es memoria que se queda. A escala, tu cliente nota latencia y tú notas llamadas perdidas.

    3) Módulos por costumbre

    Componente “autónomo” que igual requiere NgModule. El bundle crece. El tree-shaking muere. La app se pone lenta en móviles. Y nadie recuerda por qué se introdujo ese módulo en primer lugar.

    Cómo arreglarlo sin convertirte en un nazi del código

    No se trata de prohibir la IA. Se trata de exigirle reglas de juego. Y versionarlas como si fueran tests.

    Regla 1: No pidas código. Pide arquitectura.

    Los prompts vagos generan código vagabundo. Los prompts específicos crean artefactos fiables.

    Prompt base (cópialo y adáptalo):

    “Eres un Staff Engineer especialista en Angular 17+. Refactoriza/crea [componente|servicio|PR] con estas restricciones: 1) Usa Standalone Components. 2) Aplica Signals para el estado local (signal/computed). 3) Limita RxJS exclusivamente a llamadas HTTP con operadores modernos. 4) Usa inject() en lugar de constructor(). 5) Incluye takeUntilDestroyed donde haya subscripciones. Devuélveme: 1) código, 2) tests que cubran casos límite, 3) checklist de riesgos y métricas a vigilar tras deploy.”

    ¿Ves la diferencia? No “haz un componente”, sino “hazlo según las reglas modernas y explícitas”.

    Regla 2: La IA es tu linter arquitectónico, no tu desarrollador

    No le pegues un ticket grande a la IA y mergees. Haz que la IA haga refactors y análisis. Pega un componente legacy y pídele migrarlo a Signals y Standalone. Pídele también un diff de riesgos.

    Prompt para migración:

    “Refactoriza este componente legacy (pego archivo). Reemplaza Observables derivados por computed() Signals. Asegura el mismo comportamiento. Indica: 1) supuestos que cambian, 2) pruebas adicionales necesarias, 3) riesgos operativos.”

    Regla 3: Versiona prompts y publícalos en la wiki del equipo

    Trátalos como infra. Cambia versión cuando el prompt mejore. Si algo rompe, tendrás historial para auditar: qué prompt produjo qué cambio.

    Regla 4: Exige una “explicación humana” en cada PR generado o asistido por IA

    Si tu app se rompe, quieres saber quién decidió. No aceptes merges sin:

    • Un párrafo (2–4 líneas) que explique por qué la elección es correcta;
    • Una lista de escenarios que prueban la decisión;
    • Al menos 1 prueba que falla si la suposición es falsa.

    Checklist rápido para revisar PRs (lo lees en 60 segundos)

    • ¿Usa Standalone Components? Si no, ¿por qué?
    • ¿State local con Signals o BehaviorSubjects? Preferir Signals salvo justificación.
    • ¿Suscripciones con takeUntilDestroyed o patrón seguro?
    • ¿Se agregaron tests para edge cases concurrentes?
    • ¿Dependencias nuevas justificadas por RFC?
    • ¿Quién será responsable del mantenimiento?

    Prompts listos para usar (copiar y pegar)

    1. Auditor de PR

      System: “Actúa como Senior Angular Architect.”
      User: “Analiza este diff y responde: 1) tres supuestos operativos que hace, 2) tres formas en que falla a escala y cómo detectarlo, 3) recomendación (aceptar/modificar/rechazar) con pasos concretos.”

    2. Generador de Kata (para mentoring)

      System: “Actúa como instructor.”
      User: “Crea un kata en Angular con Signals que contenga un bug sutil de reactividad. Incluye 1) código base, 2) 3 pistas, 3) tests que inicialmente fallan.”

    3. Migrador a Signals

      System: “Actúa como Staff Engineer pragmático.”
      User: “Refactoriza este componente a Signals. Mantén el comportamiento funcional. Proporciona diff, pruebas y checklist de rollback.”

    Métricas que importan (no las obvias)

    Olvida commits por día. Mide lo que realmente previene incendios.

    • % de PRs etiquetados “IA-assisted” que requieren rework por problemas arquitectónicos.
    • Tiempo medio hasta rollback (MTTR) tras deploys con cambios IA-assisted.
    • Número de memory leaks detectados por semana en staging.
    • Tamaño del bundle inicial tras merges (trend line).

    Si esas métricas suben, tu IA está haciendo algo peor que inútil: está degradando tu producto.

    Cultura y políticas (si no las pones, nadie las seguirá)

    • Política “IA-assisted” obligatoria: todo PR generado/ayudado por IA debe llevar etiqueta y la explicación humana.
    • Prompt Registry: versionado de prompts y ejemplos aprobados por el equipo.
    • Pistas obligatorias: Katas que sirvan para evaluar si alguien sabe explicar lo que la IA generó. Si no puede explicarlo, vuelta atrás.
    • Guardrails de dependencia: librerías nuevas requieren aprobación de Tech Lead y Security.

    Errores reales que verás (y cómo atajarlos)

    Error: “Funciona en local, perfecto”.

    Realidad: pasó tests, pero explotó en mirror de tráfico. Solución: siempre despliegue con feature flags y monitorización explícita (p95, error budget).

    Error: “El junior lo puso en prod con Copilot”.

    Realidad: nadie entendía la implementación. Solución: defensa técnica obligatoria. Si quien mergea no lo puede explicar, revert.

    Error: “Prompts mágicos de StackOverflow”.

    Realidad: la IA copia snippets inseguros. Solución: validación de seguridad automática y revisión manual.

    Metáfora que te pega: la excavadora y el plano

    Piensa en la IA como una excavadora gigante. Mueve toneladas en minutos. Sin plano, destruye cimientos. El Tech Lead es el que marca dónde cavar, qué pilares dejar intactos y cómo rellenar después. Usar la excavadora sin plano es rapidez sin criterio. Y eso se paga caro.

    Último truco: la regla de las tres preguntas

    Antes de aceptar un PR asistido por IA, pregúntate:

    • ¿Qué suposición técnica hace esta implementación?
    • ¿Qué falla si esa suposición es falsa?
    • ¿Cómo lo detecto y qué hago en 15 minutos si falla?

    Si no puedes responder las tres, no lo merges.

    Cierre con acción (porque esto no acaba aquí)

    Si quieres dejar de encontrar sorpresas a medianoche, no hay excusas: necesitas prompts, plantillas y un kit de auditoría que funcione desde hoy.

    Di “QUIERO EL KIT” y te mando:

    • 10 prompts versionados (arquitectura, PR audit, migración a Signals, Katas).
    • Plantilla PR con checklist obligatorio.
    • Mini-RFC para tu política “IA-assisted”.
    • 5 tests de estrés para staging que puedes pegar en CI.

    Y si no quieres el kit, al menos copia esto en tu wiki y pásaselo al equipo. Pero no te quedes callado cuando el AI-generated pain llegue: será tarde.

    Esto no acaba aquí. Tu repo habla. ¿Vas a escucharlo o seguirás fingiendo que todo está bien?

    Dominicode Labs

    Si gestionas automatización, IA aplicada, agentes o workflows como parte de tu stack, puede interesarte explorar recursos y experimentos prácticos. Sigue esta continuación lógica: Dominicode Labs.

    FAQ

    ¿Por qué se dice que la IA no es neutral?

    Porque los modelos devuelven lo más probable según los datos con los que fueron entrenados. Si ese entrenamiento contiene código antiguo o antipatrón, los resultados reflejarán esas prácticas.

    ¿Qué son Standalone Components y por qué importan?

    Standalone Components son una característica moderna de Angular que permite declarar componentes sin NgModules, reduciendo el bundle y mejorando el tree-shaking. Importan porque evitan módulos innecesarios que aumentan el tamaño y la complejidad.

    ¿Cuándo debo preferir Signals sobre RxJS?

    Preferir Signals para estado local y recalculación reactiva simple; usar RxJS cuando se trabaja con flujos complejos o streams combinados, idealmente limitándolo a llamadas HTTP o casos donde sus operadores sean necesarios.

    ¿Qué debe incluir la explicación humana en un PR IA-assisted?

    Un párrafo de 2–4 líneas justificando la elección técnica, una lista de escenarios que prueban la decisión y al menos una prueba que falle si la suposición es falsa.

    ¿Qué métricas concretas debo añadir al dashboard?

    Por ejemplo: % de PRs IA-assisted que requieren rework por arquitectura, MTTR tras deploys IA-assisted, número de memory leaks detectados en staging y tamaño del bundle inicial por tendencias.

    ¿Cómo versiono prompts de forma práctica?

    Guarda cada prompt en un repositorio o wiki con una versión semántica, ejemplos de entrada/salida y notas de cambios. Trata los prompts como infraestructura: cambia la versión cuando el prompt cambie y registra qué cambios produjo.