Ir al contenido

Guía de entrevista · Edición 2026

Angular
Senior

HTML, CSS, JavaScript, TypeScript, Angular moderno, RxJS, browser, arquitectura, performance, testing, seguridad, system design y liderazgo técnico.

Módulos
30
Conceptos
282
Preguntas
350

Bloque 01

Fundamentos web

HTML, CSS, JavaScript y TypeScript, desde la base hasta preguntas avanzadas.

01

HTML completo: semántica, formularios, medios y SEO

HTML define significado, navegación por teclado, formularios y la base que consumen buscadores y tecnologías asistivas.

Teoría

Documento y semántica
  • head contiene metadata, title, links, preload y scripts. body contiene el documento visible. Un title y description claros mejoran navegación y presentación en resultados.
  • header, nav, main, article, section, aside y footer describen la función de cada región. Navegadores y tecnologías asistivas usan esa estructura para crear landmarks. div y span agrupan contenido sin añadir significado.
  • Block e inline describen comportamiento de formatting context, que CSS puede cambiar. La semántica del elemento no cambia al modificar display.
  • a navega y necesita href; button ejecuta una acción. target=_blank requiere una política de rel apropiada para reducir acceso a opener.
Formularios y contenido
  • Imágenes necesitan alt según función. picture, srcset y sizes permiten formatos y resoluciones. Width y height reservan espacio y reducen CLS.
  • Video y audio admiten múltiples source, track para subtítulos y controles. Un iframe crea otro contexto; restringilo con sandbox, permisos y origen confiable.
  • Form asocia label con control, usa name para submission y aprovecha tipos nativos. GET codifica en URL; POST envía body. El servidor valida todos los campos.
  • Un button dentro de un formulario tiene tipo submit por defecto. type=button representa una acción auxiliar y evita envíos accidentales. La semántica de submit también permite enviar con Enter y ejecutar la validación nativa.
Carga, SEO y accesibilidad
  • Una tabla de datos se compone con caption, thead, tbody, celdas th y relaciones scope. Esa estructura permite asociar cada dato con sus encabezados. Las tablas usadas para layout comunican relaciones inexistentes y dificultan el responsive design.
  • br introduce un salto dentro del mismo contenido, como una dirección o un poema. hr marca un cambio temático entre bloques. El espacio visual entre elementos pertenece a margin, padding o gap en CSS.
  • Scripts con defer descargan en paralelo y ejecutan tras parsear, en orden. async ejecuta cuando descarga y no conserva orden. Modules difieren y usan defer por defecto.
  • SEO técnico incluye HTML rastreable, canonical, robots, structured data, status correctos, sitemap y rendering compatible con el contenido.

Ejemplo

<form (ngSubmit)="save()" [formGroup]="profileForm">
  <label for="email">Correo</label>
  <input id="email" type="email" autocomplete="email"
         formControlName="email" aria-describedby="email-error">
  <p id="email-error" role="alert">Ingresá un correo válido.</p>
  <button type="submit">Guardar</button>
</form>

Fuentes del tema

Preguntas y respuestas

¿Etiqueta y atributo?Ver respuesta

La etiqueta define el elemento; el atributo configura información o comportamiento en su start tag. Una property DOM representa el estado vivo y puede diferir del atributo inicial.

¿id o class?Ver respuesta

id identifica un elemento dentro del documento y sirve para relaciones, fragmentos y labels. class agrupa elementos para estilos o comportamiento.

¿Cómo crear un formulario accesible?Ver respuesta

Asocio labels, agrupo opciones con fieldset/legend, uso tipos y autocomplete, explico errores y muevo foco cuando el flujo lo requiere.

¿ol o ul?Ver respuesta

ol comunica que el orden modifica el significado; ul agrupa elementos sin secuencia semántica.

¿Cuándo usás un enlace y cuándo un botón?Ver respuesta

Un enlace con href cambia ubicación y conserva acciones nativas como abrir en otra pestaña. Un botón ejecuta una acción en la interfaz. Elegir el elemento correcto aporta teclado, rol y expectativas sin recrearlos con JavaScript.

¿Qué aporta la validación nativa de formularios?Ver respuesta

Atributos como required, type, min, max y pattern expresan restricciones y permiten feedback del navegador. La aplicación puede personalizar mensajes, pero el servidor debe repetir la validación porque el cliente se puede modificar.

¿Cómo te sentís con este tema?

02

CSS completo: cascade, layout, responsive y rendimiento

CSS resuelve una cascada antes de calcular layout y paint. Las preguntas clásicas empiezan con selectores; las Senior llegan a stacking contexts, containment y estabilidad visual.

Teoría

Cascada y box model
  • La cascada considera origen, importancia, layers, specificity, scope y orden. !important altera el orden dentro del origen y crea costo de mantenimiento.
  • Specificity cuenta IDs, clases/atributos/pseudo-clases y tipos/pseudo-elementos. :where() aporta especificidad cero; :is() y :not() toman la del argumento más específico.
  • Box model suma content, padding, border y margin. box-sizing: border-box incluye padding y border dentro del tamaño declarado.
  • Margin separa cajas; padding amplía el interior y el área de fondo. Márgenes verticales pueden colapsar en block formatting context.
  • display: none quita la caja y el árbol de accesibilidad; visibility: hidden conserva espacio y oculta; opacity: 0 conserva layout y puede conservar interacción si no la controlás.
Layout y responsive
  • Position static sigue flujo; relative conserva espacio y crea referencia; absolute sale del flujo y usa containing block; fixed se relaciona con viewport salvo transform ancestors; sticky cambia según scroll container.
  • Flexbox organiza una dimensión y distribuye espacio; Grid controla filas y columnas. min-width: 0 suele resolver overflow de hijos flex.
  • Responsive design combina tamaños fluidos, media queries, container queries, imágenes adaptativas y límites de ancho. Los breakpoints basados en el punto donde el contenido deja de funcionar resisten mejor cambios de dispositivos y layout.
  • Overflow puede clippear, scrollear o crear formatting context. text-overflow: ellipsis necesita restricciones de overflow y white-space.
  • z-index solo compara dentro del mismo stacking context. Transform, opacity, positioned elements y isolation pueden crear contextos nuevos.
Composición y rendimiento
  • Una transition interpola el cambio entre dos estados; una animation recorre keyframes aunque no cambie una propiedad por interacción. transform y opacity suelen ejecutarse en composición y evitan layout, mientras prefers-reduced-motion permite reducir movimiento no esencial.
  • BEM nombra Block, Element y Modifier; CSS Modules, Shadow DOM y Angular encapsulation resuelven scopes con modelos distintos.
  • Preprocesadores agregan sintaxis en build; frameworks entregan utilidades o componentes. Ninguno reemplaza cascade, layout ni accesibilidad.
  • contain limita qué partes del árbol pueden afectar layout, paint o style fuera de un elemento. content-visibility: auto permite omitir el render de contenido fuera del viewport. Ambas herramientas reducen trabajo, pero cambian mediciones, foco y accesibilidad si se aplican sin comprobar el resultado.

Ejemplo

@layer reset, base, components, utilities;

@layer components {
  .card { container-type: inline-size; }
  @container (min-width: 36rem) {
    .card__body { display: grid; grid-template-columns: 2fr 1fr; }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; }
}

Fuentes del tema

Preguntas y respuestas

¿Flexbox o Grid?Ver respuesta

Flexbox distribuye elementos a lo largo de un eje y permite wrapping. Grid define una estructura bidimensional. Una interfaz puede usar ambos en niveles distintos.

¿Por qué z-index: 9999 no funciona?Ver respuesta

El elemento puede vivir dentro de un stacking context que queda debajo de otro. Comparo contextos ancestros antes de subir el número.

¿display:none o visibility:hidden?Ver respuesta

display:none elimina la caja; visibility:hidden conserva su espacio. Si necesitás ocultar solo visualmente y mantener lectura, uso un patrón visually-hidden probado.

¿Cómo evitás CSS frágil?Ver respuesta

Reduzco especificidad, defino tokens y layers, limito alcance, documento variantes y pruebo estados, tamaños, temas y contenido real.

¿Cómo diagnosticás un problema de z-index?Ver respuesta

Identifico los stacking contexts de ambos elementos y comparo sus ancestros, no sólo sus números. transform, opacity, isolation y ciertos elementos posicionados crean contextos que limitan dónde compite un descendiente.

¿Media query o container query?Ver respuesta

Una media query responde al viewport o a preferencias del usuario. Una container query responde al espacio disponible para el componente. La segunda permite reutilizar la misma pieza en layouts distintos sin conocer la página que la contiene.

¿Cómo te sentís con este tema?

03

JavaScript: tipos, coerción, scope y funciones

Estas preguntas aparecen en entrevistas frontend de cualquier nivel. Una respuesta Senior explica la regla del lenguaje, muestra un caso que falla y propone una forma de escribir código predecible.

Teoría

Tipos y conversiones
  • JavaScript tiene tipos primitivos undefined, null, boolean, number, bigint, string y symbol. Los objetos se comparan por referencia. typeof null devuelve object por una decisión histórica.
  • var posee function scope, permite redeclaración y su declaración se eleva. let y const poseen block scope y permanecen en temporal dead zone hasta la inicialización. const fija la referencia, no vuelve inmutable el objeto.
  • La coerción es la conversión de un valor de un tipo a otro. Es explícita cuando el código llama a Number(value), String(value) o Boolean(value), e implícita cuando el lenguaje convierte porque un operador o contexto necesita otro tipo. Formularios, query params, atributos DOM y storage entregan strings aunque representen números o booleanos; convertir y validar en esa frontera evita que la coerción se propague al dominio.
  • Cuando un operador necesita convertir un objeto a primitivo, JavaScript ejecuta la operación abstracta ToPrimitive. Primero respeta Symbol.toPrimitive y, según el hint, consulta valueOf y toString hasta obtener un primitivo. Por eso [] se convierte en '', [1, 2] en '1,2' y un objeto común suele producir '[object Object]'; después el operador continúa con la conversión numérica o textual que corresponda.
  • El operador + es especial: después de convertir objetos a primitivos, concatena si alguno de los operandos es string; si no, realiza suma numérica. 1 + '2' produce '12', mientras '5' - 2, '5' * 2 y '5' / 2 convierten a número. Los template literals fuerzan string y los contextos de if, !, && y || usan conversión booleana.
  • Las conversiones tienen bordes que conviene conocer: Number('') y Number(null) producen 0, Number(undefined) produce NaN, y Boolean('false') es true porque cualquier string no vacío es truthy. Number exige que toda la cadena represente un número; parseInt('10px', 10) acepta el prefijo numérico. Ninguna de las dos reemplaza validar rango, formato y finitud con Number.isFinite.
Scope, hoisting y closures
  • === compara tipo y valor sin coerción. Object.is difiere en NaN y -0. == tiene casos útiles, como value == null, pero exige conocer su tabla de coerción.
  • Falsy incluye false, 0, -0, 0n, cadena vacía, null, undefined y NaN. Un array u objeto vacío es truthy.
  • Una declaración de función se eleva con su cuerpo. Una function expression sigue las reglas de su variable. Las arrow functions capturan this, arguments y super del entorno; no sirven como constructor.
  • this depende de cómo se invoca una función: method call, call/apply/bind, constructor con new o binding léxico de arrow. Extraer un método puede perder el receiver.
  • Un closure es la combinación de una función con el entorno léxico donde fue creada. La función puede ejecutarse después de que terminó la llamada exterior y seguir resolviendo parámetros y variables de ese entorno. makeCounter puede declarar let count = 0 y devolver una función que incrementa count; cada llamada a makeCounter() crea un binding privado e independiente.
Funciones, this y decisiones
  • El closure conserva bindings, no una fotografía de sus valores. Si el binding cambia, las funciones que lo cerraron observan el valor actual. Esto permite estado privado y callbacks coordinados, pero también explica bugs cuando varias funciones comparten accidentalmente una misma variable mutable.
  • En un loop, var crea un único binding con scope de función, por lo que callbacks diferidos suelen leer el valor final. let crea un binding nuevo por iteración. Antes de let, una IIFE o una factory recibía el valor de cada vuelta y creaba un entorno distinto.
  • Closures sostienen factories, currying, memoization, event handlers y callbacks asíncronos. El entorno permanece vivo mientras una función alcanzable lo necesite: no es una fuga por sí mismo, pero puede retener DOM, caches o respuestas grandes. El cleanup debe remover listeners, cancelar timers o suscripciones y evitar capturar objetos completos cuando alcanza con un identificador o un dato pequeño.
  • El spread copia un nivel y enumera propiedades. structuredClone cubre muchos valores y ciclos, pero no funciones ni todos los objetos host. Un JSON round-trip pierde fechas, undefined, BigInt y prototipos.
  • Destructuring extrae valores y admite defaults. El default corre solo para undefined, no para null. Rest agrupa el remanente y debe ocupar la última posición.

Ejemplo

function makeCounter() {
  let count = 0;
  return () => ++count;
}

const first = makeCounter();
const second = makeCounter();
console.log(first(), first(), second()); // 1, 2, 1

console.log(1 + '2');       // '12'
console.log('5' - 2);       // 3
console.log(Number('42'));  // 42
console.log(Boolean(''));   // false
console.log([] == false);   // true
console.log([] === false);  // false

Fuentes del tema

Preguntas y respuestas

¿Cuál es la diferencia entre var, let y const?Ver respuesta

var usa scope de función y permite redeclaración. let y const usan scope de bloque y temporal dead zone. const impide reasignar la variable, pero el valor referenciado puede mutar.

¿Por qué [] == false da true?Ver respuesta

La igualdad abstracta no compara directamente array y boolean. Primero convierte false a número: 0. Después aplica ToPrimitive al array: [].toString() produce ''. Como ahora compara string con number, convierte '' a 0; el resultado final es 0 == 0, que es true. En cambio, [] === false es false porque los tipos son distintos y no existe coerción. No memorizaría solamente este resultado: seguir los pasos boolean → number, object → primitive y string → number permite explicar también casos como [0] == false. En código de producto uso === y conversiones explícitas para que esa secuencia no quede escondida.

¿Arrow function o función normal?Ver respuesta

Uso arrow para callbacks que necesitan el this exterior. Uso función normal para métodos dinámicos, constructores o APIs que asignan receiver.

¿Shallow copy o deep copy?Ver respuesta

Una shallow copy crea un objeto o array nuevo, pero copia por referencia los valores anidados. Por ejemplo, con const original = { user: { name: 'Ana' } }; const copy = { ...original };, se cumple copy !== original, pero copy.user === original.user; por eso copy.user.name = 'Luis' también modifica original.user.name. Spread, Object.assign, Array.from y slice hacen copias superficiales. Una deep copy duplica recursivamente la estructura para que los objetos anidados no compartan identidad. structuredClone(original) sirve para muchos datos nativos y ciclos, pero no clona funciones, elementos DOM ni conserva el comportamiento de todas las instancias de clases. No hago una copia profunda por defecto: cuesta CPU y memoria, y puede romper identidades que la aplicación necesita. Para actualizar estado prefiero copiar sólo el camino modificado, por ejemplo { ...state, user: { ...state.user, name: 'Luis' } }; así mantengo inmutabilidad y structural sharing sin duplicar todo el grafo.

¿Qué es un closure y cuándo se crea?Ver respuesta

Un closure es una función junto con las referencias a los bindings de su entorno léxico. Se determina cuando la función se crea, no cuando se invoca. Por ejemplo, function makeCounter() { let count = 0; return () => ++count; } devuelve una función que sigue accediendo a count después de que makeCounter terminó. const a = makeCounter(); const b = makeCounter(); crea dos entornos: a() devuelve 1, luego 2, mientras b() comienza en 1. El runtime conserva sólo los entornos que todavía son alcanzables; por eso un closure permite estado privado sin convertir count en una variable global.

¿Un closure captura el valor o el binding?Ver respuesta

Captura el binding, es decir, la celda donde vive el valor, no una fotografía inmutable. Con let rate = 1; const price = value => value * rate; rate = 2;, price(10) devuelve 20 porque lee el valor actual de rate. Varias funciones pueden compartir el mismo binding y observar sus cambios. Si necesito congelar el valor de un momento, creo otro binding pasando el dato a una factory: const withRate = rate => value => value * rate. Cada llamada recibe su propio parámetro rate.

¿Por qué un loop con var y callbacks suele imprimir el valor final?Ver respuesta

var tiene scope de función, así que todas las callbacks cierran sobre un único binding i. Cuando ejecuta el timer, el loop ya terminó y ese binding vale 3: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); imprime 3, 3, 3. Con let, la especificación crea un binding nuevo en cada iteración y el resultado es 0, 1, 2. Otra solución es una factory o IIFE que reciba i y genere un parámetro distinto por vuelta. El punto importante no es el timer: es cuántos bindings existen y cuál captura cada función.

¿Cómo puede un closure retener memoria innecesariamente?Ver respuesta

Mientras una función sea alcanzable, también permanecen alcanzables los valores de su entorno que necesita. Un listener global que captura el componente, un timer que captura una respuesta grande o una cache sin límite pueden mantener vivo ese grafo después de retirar la vista. No todo closure es un leak: se vuelve problema cuando la vida de la referencia supera la vida útil del dato. Remuevo listeners, limpio timers y suscripciones, limito caches y capturo sólo el identificador o valor pequeño necesario. En Angular asocio el cleanup a DestroyRef o takeUntilDestroyed cuando corresponde.

¿Qué diferencia hay entre coerción implícita y conversión explícita?Ver respuesta

En una conversión explícita el código declara la intención: Number(input.value), String(id) o Boolean(flag). La coerción implícita ocurre dentro de un operador o contexto: '5' - 1 produce 4, 1 + '2' produce '12' y if ('false') entra porque el string no está vacío. La coerción no es automáticamente un error; templates, comparaciones y operadores dependen de ella. El riesgo aparece cuando oculta un contrato. En fronteras externas convierto, valido y conservo desde allí un tipo estable.

¿Qué es ToPrimitive y por qué importa?Ver respuesta

ToPrimitive es la operación abstracta que convierte un objeto en un valor primitivo antes de que otro algoritmo continúe. Si existe, llama a Symbol.toPrimitive; en caso contrario prueba valueOf y toString en un orden que depende del hint. Deben devolver un primitivo o la conversión falla con TypeError. Por eso [] + 1 produce '1': el array se vuelve '' y + concatena. Un objeto puede personalizar el resultado con [Symbol.toPrimitive](hint), pero hacerlo de forma sorprendente vuelve los operadores difíciles de razonar; normalmente prefiero métodos explícitos de dominio.

¿Por qué existe la Temporal Dead Zone?Ver respuesta

Al entrar en un bloque, JavaScript crea los bindings de let, const y class, pero los deja sin inicializar hasta ejecutar su declaración. Ese intervalo es la Temporal Dead Zone. Leer el binding durante ese tramo lanza ReferenceError: console.log(total); let total = 1;. Incluso typeof total falla si total está en la TDZ, a diferencia de una variable que no existe. Con var, en cambio, el binding se inicializa con undefined, por lo que el acceso prematuro no falla y puede ocultar un error de orden. La TDZ existe para que una variable con scope de bloque no se use antes de tener el valor que su declaración promete. No significa que let y const no tengan hoisting: sus bindings se crean al entrar al scope, pero todavía no son accesibles.

¿Usarías alguna vez ==?Ver respuesta

Sí, pero sólo usaría deliberadamente value == null cuando quiero aceptar exactamente null o undefined. La comparación es verdadera para esos dos valores y falsa para 0, false, '' y NaN; por ejemplo, if (response.middleName == null) detecta que el campo opcional no llegó sin rechazar una cadena vacía válida. Es una excepción conocida de Abstract Equality y conviene permitirla explícitamente con una regla como eqeqeq: ['error', 'always', { null: 'ignore' }]. En el resto del código uso === y !==, porque == aplica coerciones difíciles de leer: '' == 0, '0' == false y [] == false son verdaderas. Si el equipo prioriza máxima explicitud, escribo value === null || value === undefined; comunica el mismo contrato sin depender de conocer la excepción.

¿Cómo te sentís con este tema?

04

JavaScript: objetos, prototipos, arrays y programación funcional

JavaScript usa delegación prototípica. Las clases ofrecen sintaxis, pero los objetos todavía resuelven propiedades a través de una cadena de prototipos.

Teoría

Fundamentos
  • Object.create(proto) fija el prototipo. new C() crea un objeto, enlaza C.prototype, ejecuta C con ese this y devuelve el objeto salvo retorno explícito de otro objeto.
  • Una propiedad puede ser own o heredada. Object.hasOwn comprueba ownership; in recorre la cadena. Object.keys devuelve claves enumerables propias.
  • Los property descriptors controlan writable, enumerable y configurable; getters y setters forman accessors. Cambiar descriptores afecta serialización y copia.
  • Arrays son objetos con índices y length. for...of recorre valores de un iterable; for...in recorre claves enumerables y no conviene para arrays.
Mecanismo y aplicación
  • map crea una colección transformada, filter conserva elementos, reduce acumula, find devuelve la primera coincidencia y some o every evalúan predicados. Cada método comunica una intención distinta y evita acumular efectos dentro de un loop genérico.
  • sort muta y convierte a string sin comparator. toSorted, toReversed, toSpliced y with devuelven copias en runtimes modernos.
  • Una pure function depende de sus argumentos y no produce efectos observables. La pureza mejora tests y composición, pero una aplicación necesita efectos en fronteras controladas.
Decisiones y límites
  • Currying transforma una función de varios argumentos en una secuencia de funciones. Partial application fija algunos argumentos; no son conceptos idénticos.
  • Memoization guarda resultados asociados a sus argumentos. La estrategia necesita una regla de igualdad, un límite de tamaño y una política de invalidación; sin esos límites, la caché puede devolver datos obsoletos o retener memoria sin control.
  • Big O describe crecimiento. Acceso por índice de array suele ser O(1); búsqueda lineal O(n); sort comparativo O(n log n); acceso promedio a Map O(1). Las constantes todavía afectan al usuario.

Preguntas y respuestas

¿Clase o prototipo?Ver respuesta

class organiza herencia y métodos con sintaxis más clara; el runtime resuelve métodos mediante prototipos. Conocer el modelo explica instanceof, shadowing y métodos compartidos.

¿map o forEach?Ver respuesta

map crea una colección transformada y exige usar el retorno. forEach expresa un efecto por elemento y devuelve undefined.

¿Map o objeto?Ver respuesta

Map acepta cualquier clave, preserva orden de inserción y ofrece size e iteración directa. Un objeto encaja en records con claves string/symbol y serialización JSON.

¿Qué muta un array?Ver respuesta

push, pop, shift, unshift, splice, sort, reverse, fill y copyWithin. map, filter, slice, concat y los métodos to* crean otro array.

¿Qué pierde una copia con spread?Ver respuesta

Spread copia propiedades enumerables propias del primer nivel. Las referencias anidadas siguen compartidas y la copia no conserva descriptores completos ni comportamiento interno de todos los objetos. Primero defino qué parte del modelo necesita una nueva identidad.

¿Cuándo evitás map, filter y reduce encadenados?Ver respuesta

Cada operador puede recorrer y asignar otra colección. En una ruta caliente o una lista grande, un solo loop puede reducir memoria y trabajo. Mantengo la cadena cuando su claridad pesa más que ese costo medido.

¿Cómo te sentís con este tema?

05

JavaScript asíncrono: event loop, Promises y errores

JavaScript ejecuta código en un solo call stack y delega timers, red y eventos al entorno. Promises, async/await y el event loop permiten coordinar cuándo continúa cada operación sin bloquear la interfaz.

Teoría

Modelo de ejecución
  • El código síncrono termina una instrucción antes de comenzar la siguiente. JavaScript usa un solo call stack para ejecutar ese código en el main thread del navegador. Una función lenta ocupa el stack y retrasa clicks, input, layout y paint.
  • Una operación asíncrona inicia un trabajo cuyo resultado llegará después. El navegador puede encargarse de un timer, una petición de red o un evento mientras el stack continúa con otras instrucciones. Asincronía describe coordinación en el tiempo; no significa que dos fragmentos de JavaScript se ejecuten al mismo tiempo en el mismo thread.
  • El event loop coordina el call stack con el entorno del navegador y sus colas. Toma una task, ejecuta su callback hasta vaciar el stack, drena todas las microtasks pendientes, permite que el navegador renderice y después avanza a otra task. setTimeout, eventos y mensajes generan tasks; continuaciones de Promises y queueMicrotask generan microtasks.
  • Una Promise es un objeto que representa el resultado futuro de una sola operación. Nace en estado pending y termina como fulfilled con un valor o rejected con una razón. fulfilled y rejected forman el estado settled. Una Promise settled no puede cambiar de estado ni volver a emitir otro resultado.
  • El constructor new Promise(executor) ejecuta el executor de inmediato y de forma síncrona. Las funciones resolve y reject fijan el resultado eventual; no vuelven asíncrono el trabajo que se ejecuta dentro del executor. La asincronía proviene de la API usada, como fetch, un timer o IndexedDB. Si una API ya devuelve una Promise, envolverla en otra suele agregar código y errores sin aportar control.
Promise y async/await
  • then registra el camino de éxito, catch registra el de rechazo y finally ejecuta cleanup sin recibir ni reemplazar el resultado salvo que lance un error. Cada método devuelve una Promise nueva. Por eso una cadena no modifica la Promise anterior: cada eslabón describe cómo obtener el resultado siguiente.
  • El valor que retorna un callback decide el siguiente eslabón. Un valor común cumple la Promise siguiente con ese valor; una Promise o thenable hace que la siguiente adopte su estado; un throw la rechaza. Omitir return entrega undefined y deja fuera de la cadena cualquier operación iniciada dentro del callback.
  • Los handlers de then, catch y finally no corren durante el stack actual, aunque la Promise ya esté settled. JavaScript los encola como microtasks. El navegador drena esa cola antes de tomar otra task, por eso una cadena que crea microtasks sin terminar puede retrasar timers, eventos y render.
  • Una función declarada con async devuelve una Promise. Un return value produce una Promise fulfilled con value; un throw error produce una Promise rejected. await promise pausa sólo la ejecución de esa función, libera el stack y reanuda su continuación como microtask cuando la Promise termina. await no bloquea el thread ni mueve trabajo de CPU a otro thread.
  • Dos await consecutivos suelen ejecutar operaciones en secuencia cuando la segunda comienza después de resolver la primera. Si ambas son independientes, iniciarlas antes y esperar Promise.all reduce el tiempo total. La concurrencia empieza al crear o invocar las operaciones, no al escribir Promise.all.
Observable y streams
  • Los combinadores expresan políticas distintas. Promise.all cumple cuando todas cumplen, conserva el orden de entrada y rechaza ante el primer rechazo observado. Promise.allSettled espera todos los resultados. Promise.race adopta el primer settlement. Promise.any toma el primer fulfillment y, si todos rechazan, devuelve un AggregateError.
  • Un Observable representa una fuente que puede enviar cero, uno o varios valores a lo largo del tiempo. Una suscripción conecta un observer con esa fuente. El observer puede recibir notificaciones next, una única notificación terminal error o una única notificación terminal complete. Después de error o complete no llegan más valores.
  • La mayoría de los Observables de RxJS son lazy: el producer comienza para cada subscribe. Un Observable cold crea una ejecución independiente por suscriptor, como una request HTTP. Un Observable hot comparte una fuente que ya produce, como eventos del usuario o un Subject. Operadores como map, filter, switchMap y catchError crean Observables nuevos y describen el flujo sin mutar la fuente.
  • unsubscribe ejecuta el teardown registrado por el Observable y deja de entregar notificaciones a ese suscriptor. Detener el trabajo subyacente depende de que el producer implemente ese teardown. Angular HttpClient aborta la request al desuscribirse; un Observable propio que inicia un timer debe cancelarlo en su función de cleanup. Desuscribirse no deshace efectos que ya ocurrieron.
  • Promise y Observable modelan contratos distintos. Una Promise comparte un único resultado settled y se consume con then o await. Un Observable modela una secuencia, puede ser lazy, permite composición temporal y ofrece teardown por suscripción. Convertir entre ambos puede perder información: firstValueFrom toma el primer valor y necesita que la fuente emita o termine; convertir una Promise a Observable no vuelve cancelable la operación original.
Cancelación, errores y rendimiento
  • try/catch captura errores síncronos del bloque y rechazos que atraviesan un await. No captura un error lanzado más tarde por un callback desconectado, como un setTimeout. Ese callback necesita su propio manejo o debe formar parte de una Promise que el flujo retorne y espere.
  • Una Promise no define cancelación. AbortController permite pedirle a fetch y a otras APIs compatibles que detengan su trabajo mediante una signal. Cancelar el cliente evita procesar una respuesta innecesaria, aunque el servidor puede continuar si ya recibió y empezó la operación.
  • Una race condition aparece cuando varias operaciones compiten por actualizar el mismo estado y terminan en otro orden. Un buscador puede mostrar una respuesta vieja si la primera petición tarda más que la última. Abortá la anterior, asigná una versión a cada solicitud o aceptá el resultado sólo si todavía corresponde a la consulta vigente.
  • Debounce espera un período sin eventos antes de ejecutar; sirve para búsquedas mientras el usuario escribe. Throttle impone una frecuencia máxima; sirve para scroll o resize. Ambos necesitan cleanup para cancelar timers o trabajo pendiente cuando se destruye el consumidor.
  • async/await organiza espera de I/O, pero no reduce el costo del código síncrono. Dividí CPU intenso en tareas pequeñas cuando necesitás devolver control al navegador. Usá un Web Worker cuando el cálculo merece otro thread y el costo de copiar datos y enviar mensajes resulta aceptable.

Ejemplo

function delay(ms, value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), ms);
  });
}

async function loadDashboard() {
  const userRequest = delay(300, { id: 7 });
  const settingsRequest = delay(200, { theme: 'dark' });

  try {
    const [user, settings] = await Promise.all([
      userRequest,
      settingsRequest,
    ]);
    return { user, settings };
  } catch (error) {
    throw new Error('No se pudo cargar el dashboard', { cause: error });
  }
}

console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
queueMicrotask(() => console.log('D'));
console.log('E');

// A, E, C, D, B
loadDashboard().then(console.log).catch(console.error);

Preguntas y respuestas

¿Qué es una Promise?Ver respuesta

Una Promise representa un único resultado que todavía puede no estar disponible. Empieza pending y termina fulfilled con un valor o rejected con un error. Por ejemplo, fetch('/users') devuelve de inmediato una Promise; el objeto permite registrar qué hacer cuando lleguen la respuesta o el fallo. La Promise no contiene un thread ni ejecuta dos resultados: modela la finalización de una operación.

¿El executor de new Promise es asíncrono?Ver respuesta

No. new Promise((resolve) => { console.log('executor'); resolve(1); }) imprime executor durante el stack actual. Lo que corre después como microtask es el callback registrado con then. Poner un loop pesado dentro del executor bloquea la interfaz igual que cualquier otro código síncrono.

¿Qué devuelve then?Ver respuesta

then devuelve una Promise nueva. Si el callback retorna 42, la nueva Promise cumple con 42; si retorna fetch(...), adopta el estado de esa Promise; si lanza un error, queda rechazada. Este contrato permite encadenar transformaciones y propagar errores hasta un catch.

¿Qué pasa si olvidás return dentro de un then?Ver respuesta

El callback retorna undefined, así que el siguiente then continúa sin esperar la operación interna. En loadUser().then(user => { saveUser(user); }).then(showSuccess), showSuccess puede ejecutarse antes de que termine saveUser. La corrección es return saveUser(user) o usar await saveUser(user) dentro de una función async.

¿En qué orden imprime el ejemplo?Ver respuesta

Primero corre el stack síncrono: A y E. Después se procesan las microtasks en el orden en que fueron encoladas: C y D. Al final corre la task del timer y aparece B. El resultado es A, E, C, D, B. Un timer con demora cero indica una demora mínima; no salta por delante del stack ni de las microtasks.

¿await bloquea JavaScript?Ver respuesta

await pausa la función async que lo contiene y devuelve el control al caller. El main thread puede procesar otras tareas. Cuando la Promise termina, JavaScript encola la continuación de la función como microtask. Un cálculo pesado antes o después del await sigue bloqueando porque await no crea otro thread.

¿Cuándo usar ejecución secuencial y cuándo concurrente?Ver respuesta

Usá secuencia cuando una operación depende del resultado anterior, como cargar un usuario y después consultar sus permisos. Para trabajos independientes, iniciá ambos antes: const userRequest = loadUser(); const settingsRequest = loadSettings(); const [user, settings] = await Promise.all([userRequest, settingsRequest]);. Así el tiempo total se aproxima a la operación más lenta en lugar de sumar ambas esperas.

¿Promise u Observable?Ver respuesta

Una Promise entrega un único resultado y no incorpora cancelación. Empieza cuando la operación que la creó ya fue iniciada. Un Observable puede emitir cero, uno o varios valores; suele comenzar al suscribirse, permite unsubscribe y compone tiempo, cancelación y concurrencia mediante operadores. Para una request HTTP aislada una Promise puede alcanzar; para eventos, streams o flujos cancelables, un Observable expresa mejor el contrato.

¿Qué es un Observable y qué hace subscribe?Ver respuesta

Un Observable describe cómo producir y entregar una secuencia de notificaciones. subscribe inicia o conecta esa producción y devuelve una Subscription. El observer recibe next(value) mientras hay datos y después puede recibir complete() o error(reason) como final excluyente. Ejemplo: interval(1000) emite valores hasta que el consumidor se desuscribe; http.get() suele emitir una respuesta y completar.

¿Observable cold u hot?Ver respuesta

Un cold Observable crea una ejecución por suscriptor. Dos suscripciones a http.get('/users') suelen crear dos requests. Un hot Observable comparte una fuente externa o una ejecución entre suscriptores, como clicks o un Subject. share y shareReplay pueden compartir una fuente cold, pero requieren definir replay, refCount y reset para no conservar datos o conexiones más tiempo del previsto.

¿unsubscribe siempre cancela el trabajo?Ver respuesta

unsubscribe deja de entregar valores y ejecuta el teardown del producer. Cancela el trabajo sólo si ese teardown sabe detenerlo. HttpClient puede abortar la request; un Observable creado con new Observable debe retornar cleanup, por ejemplo return () => clearInterval(id). Una Promise convertida con from(promise) seguirá resolviéndose porque la Promise original no conoce la suscripción.

¿Cuándo convertir una Promise a Observable o un Observable a Promise?Ver respuesta

from(promise) integra un resultado único en un pipeline RxJS, pero no agrega cancelación a la Promise. firstValueFrom(source$) resuelve con la primera emisión y se desuscribe; lastValueFrom(source$) espera que la fuente complete y usa la última. Si la fuente no emite o no completa, la Promise puede rechazar o quedar pendiente, por lo que la conversión necesita un contrato de finalización claro.

¿Cómo cancelás fetch?Ver respuesta

Creo const controller = new AbortController(), paso controller.signal a fetch y llamo controller.abort() cuando la respuesta deja de ser útil. El rechazo resultante representa cancelación y no un error funcional. También limpio el controller al destruir el componente o al iniciar una solicitud que reemplaza la anterior.

¿Por qué try/catch no captura un error de setTimeout?Ver respuesta

El callback del timer corre en otra task, después de que el bloque try terminó. try { setTimeout(() => { throw new Error('boom'); }); } catch {} no lo captura. El callback necesita manejar su error o la API debe envolver el resultado en una Promise que el caller pueda retornar y esperar.

¿Cómo evitás que una respuesta vieja reemplace una nueva?Ver respuesta

Guardo la identidad de la solicitud vigente, cancelo la anterior o comparo una versión antes de escribir estado. En un buscador, la consulta ang puede responder después de angular; sin esa protección, la UI muestra resultados que ya no coinciden con el input. En RxJS, switchMap expresa la política de conservar sólo la operación más reciente.

¿Cómo puede una cadena de microtasks bloquear la interfaz?Ver respuesta

El navegador vacía la cola de microtasks antes de avanzar al siguiente task y al render. Una cadena que agenda otra microtask puede retrasar input y pintura. Divido el trabajo y cedo al scheduler cuando necesito que el navegador procese otra tarea.

¿Qué ocurre si falla una promesa dentro de Promise.all?Ver respuesta

Promise.all rechaza al recibir el primer rechazo observable, pero las operaciones restantes continúan salvo que su API admita cancelación. Uso allSettled cuando necesito el resultado de cada operación y AbortController cuando debo detener I/O compatible.

¿Cómo te sentís con este tema?

06

TypeScript avanzado

Angular amplifica TypeScript. Una base débil en el lenguaje produce templates inseguros, estado mutable y RxJS difícil de mantener.

Teoría

Sistema de tipos
  • TypeScript agrega un sistema de tipos estático sobre JavaScript. El compilador comprueba el programa y elimina los tipos al emitir JavaScript; por eso un tipo no valida datos que llegan en runtime.
  • La inferencia deduce tipos a partir de valores y contexto. El tipado estructural considera compatibles dos valores cuando su forma satisface el contrato, aunque no compartan una clase o declaración nominal.
  • Una unión expresa alternativas y el narrowing descarta posibilidades mediante typeof, in, instanceof, discriminantes o type guards. Un switch que entrega el caso restante a never detecta variantes nuevas durante la compilación.
  • interface describe contratos extensibles y admite declaration merging. type también representa unions, tuplas, primitivas y tipos calculados. La capacidad que necesita el modelo decide la elección.
  • TypeScript extiende JavaScript con un sistema de tipos estático. El compilador comprueba el programa y elimina los tipos al emitir JavaScript; por eso una anotación no valida por sí sola los datos que llegan en runtime.
Narrowing y modelado
  • La inferencia obtiene un tipo desde el valor y su contexto. Una anotación fija el contrato de forma explícita. as const conserva literales y vuelve readonly la estructura inferida, mientras una anotación amplia puede convertir un literal como 'open' en string.
  • TypeScript usa tipado estructural: dos valores son compatibles cuando su forma cumple las propiedades requeridas, aunque sus clases o nombres sean distintos. El exceso de propiedades se comprueba con más rigor en object literals que en variables intermedias.
  • Una interface describe contratos de objetos y admite declaration merging. Un type también puede representar unions, intersections, primitivas, tuplas y transformaciones calculadas. Ambos pueden expresar muchos contratos de objetos.
  • Una union A | B acepta cualquiera de sus miembros y sólo permite operaciones comunes hasta estrechar el tipo. Una intersection A & B exige que el valor cumpla ambos contratos al mismo tiempo.
  • Las firmas de funciones tipan parámetros y retorno. Los overloads publican varias formas válidas de llamada sobre una implementación, mientras los parámetros opcionales, rest y valores por defecto modelan variaciones dentro de una misma firma.
Tipos calculados y generics
  • any desactiva la comprobación para el valor y permite que el hueco de tipos se propague. unknown acepta cualquier valor, pero exige comprobar su tipo antes de operar con él.
  • never representa un valor que no puede existir. Aparece en funciones que no retornan y en ramas exhaustivas de una unión, donde permite detectar variantes sin manejar durante la compilación.
  • Un generic introduce parámetros de tipo. La relación entre entrada y salida se conserva sin reemplazarla por any; por ejemplo, una función identity<T>(value: T): T devuelve el mismo tipo que recibió.
  • Una discriminated union reúne variantes que comparten una propiedad literal, como kind. Al comprobar esa propiedad, TypeScript estrecha el tipo y habilita únicamente los campos de la variante activa. Un caso default asignado a never detecta estados nuevos que todavía no tienen manejo.
  • El operador satisfies comprueba que una expresión cumple un tipo sin reemplazar el tipo inferido de la expresión. Una anotación puede ensanchar el valor y un type assertion sólo le pide al compilador que confíe en el programador.
Runtime y configuración
  • Los utility types transforman tipos existentes. Partial vuelve opcionales sus propiedades, Required hace lo contrario, Pick y Omit seleccionan claves, y Record modela un mapa de claves a valores.
  • Un type guard estrecha un tipo dentro de una rama. typeof, instanceof, el operador in, predicados value is T y funciones de assertion permiten demostrarle al compilador qué variante existe en runtime.
  • Optional chaining (?.) corta una cadena sólo ante null o undefined. Nullish coalescing (??) usa el valor alternativo únicamente para esos dos casos, mientras que || también reemplaza 0, false y la cadena vacía.
  • Los decorators reciben metadata sobre clases o miembros y pueden reemplazar o complementar su definición según la propuesta y configuración usada. Angular los emplea para registrar componentes, directivas, pipes e inyectables.
  • La configuración strict activa un conjunto de comprobaciones, entre ellas nullability, parámetros de funciones y propiedades inicializadas. El compilador encuentra estados inválidos antes de que lleguen al template o al runtime.

Ejemplo

type LoadState<T> =
  | { kind: 'idle' }
  | { kind: 'loading' }
  | { kind: 'success'; data: T }
  | { kind: 'error'; error: Error };

function assertNever(value: never): never {
  throw new Error(`Unhandled state: ${JSON.stringify(value)}`);
}

Fuentes del tema

Preguntas y respuestas

¿Por qué unknown supera a any?Ver respuesta

unknown obliga a validar o estrechar el tipo antes de usarlo. any permite operaciones inválidas y propaga huecos por toda la aplicación.

¿interface o type?Ver respuesta

Ambos describen formas de objetos. interface admite declaration merging y extensión orientada a contratos; type también representa unions, intersections, tuplas y tipos calculados. La consistencia del código y la capacidad necesaria deciden la elección.

¿Qué diferencia existe entre satisfies y as?Ver respuesta

satisfies comprueba que el valor cumple un contrato y conserva su inferencia. as fuerza una interpretación del tipo y puede ocultar una incompatibilidad. Uso assertions sólo cuando el runtime aporta una garantía que el compilador no puede demostrar.

¿Cómo diseñás un generic útil?Ver respuesta

El generic debe conservar una relación entre valores, por ejemplo entre entrada y salida o entre una key y su propiedad. Si el parámetro de tipo aparece una sola vez, quizá una unión o un tipo concreto comunique mejor el contrato.

¿TypeScript valida una respuesta HTTP?Ver respuesta

No. Los tipos desaparecen al compilar y una assertion sólo cambia lo que cree el compilador. Valido el JSON con un schema o type guard en la frontera y recién entonces lo convierto al modelo interno.

¿Qué significa que TypeScript sea estructural?Ver respuesta

La compatibilidad depende de la forma del valor. Si un objeto posee las propiedades requeridas con tipos compatibles, puede satisfacer el contrato aunque provenga de otra declaración. Esto facilita composición, pero exige cuidado con exceso de propiedades y tipos demasiado amplios.

¿Cómo funciona un conditional type con infer?Ver respuesta

Un conditional type elige un resultado según una relación T extends U. infer declara una variable de tipo dentro del patrón: type Result<T> = T extends Promise<infer R> ? R : T extrae el valor resuelto de una Promise.

¿Cuándo usarías un mapped type?Ver respuesta

Cuando un contrato deriva de otro de forma mecánica. type Flags<T> = { [K in keyof T]: boolean } conserva las keys y cambia sus valores. Esto evita duplicar modelos que luego divergen.

¿Cómo te sentís con este tema?

Bloque 02

Angular moderno

Componentes, reactividad, DI, RxJS, routing, forms y HTTP.

07

Angular: fundamentos, renderizado y versiones

Angular organiza la aplicación como un árbol de componentes, compila templates, inyecta dependencias y actualiza el DOM mediante change detection. Desde esa base se entienden standalone, Signals, zoneless y las migraciones entre versiones.

Teoría

Modelo de Angular
  • Angular es un framework para construir aplicaciones web a partir de un árbol de componentes. Cada componente une una clase TypeScript, una plantilla, estilos y un host element. El router, la inyección de dependencias, forms y HttpClient completan la plataforma.
  • bootstrapApplication crea el environment injector, instancia el componente raíz y conecta su host view al DOM. Desde esa raíz Angular recorre views, evalúa bindings y actualiza sólo las propiedades del DOM cuyo valor cambió.
  • Una plantilla combina HTML con expresiones y bindings. {{ value }} interpola texto, [property] escribe una propiedad, [attr.name] escribe un atributo, (event) escucha un evento y [(value)] combina entrada y salida bajo un contrato de two-way binding.
Templates y actualización del DOM
  • Angular compila las plantillas y conoce de antemano qué nodos y bindings debe crear. Change detection vuelve a evaluar esos bindings cuando una notificación marca una vista para comprobar; Signals permiten registrar dependencias reactivas precisas.
  • Angular publica las versiones mayores de core y CLI de forma alineada. Cada versión admite rangos concretos de Node.js, TypeScript y RxJS; ng version, la tabla de compatibilidad y el Update Guide permiten comprobarlos antes de una migración.
  • Las aplicaciones nuevas usan componentes standalone. NgModules siguen siendo relevantes en bases antiguas y bibliotecas, pero ya no deben dirigir un diseño nuevo sin motivo.
Angular moderno
  • Angular 21+ usa change detection zoneless por defecto. El código debe notificar cambios mediante signals, listeners, AsyncPipe, setInput o markForCheck.
  • El control flow moderno usa @if, @for, @switch y @empty. track necesita una identidad estable; usar el índice en listas mutables crea errores visuales y trabajo DOM.
Versiones y migraciones
  • @defer separa las dependencias de una vista en otro chunk y las carga mediante triggers como viewport, idle o interaction. LCP y CLS muestran si diferir contenido visible empeora la carga principal o provoca saltos de layout.
  • La adopción de una API nueva depende de su estabilidad, soporte, capacidad del equipo y costo de fallback. APIs como resource, httpResource o Signal Forms requieren revisar su estado antes de incorporarlas a una base de producción.

Ejemplo

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes), provideHttpClient()],
});

@Component({
  selector: 'app-root',
  template: `
    <button [disabled]="saving()" (click)="save()">
      {{ saving() ? 'Guardando…' : 'Guardar' }}
    </button>
  `,
})
export class AppComponent {
  saving = signal(false);
}

Preguntas y respuestas

¿Migrarías todo a la última versión?Ver respuesta

Migraría por incrementos soportados, con tests, presupuestos de bundle y observabilidad. Priorizo seguridad, compatibilidad y APIs deprecadas; después adopto sintaxis nueva.

¿Standalone elimina los módulos?Ver respuesta

Elimina la necesidad de NgModules para declarar componentes. Los módulos todavía pueden agrupar APIs heredadas o librerías. Standalone simplifica dependencias y lazy loading.

¿Qué revisarías antes de activar zoneless?Ver respuesta

Busco mutaciones que dependen de ZoneJS, librerías que actualizan campos sin notificar y usos directos de APIs externas. Migro el estado visible a signals, AsyncPipe o marcas explícitas y comparo tests y métricas antes de retirar ZoneJS.

¿Cómo decidís qué contenido cargar con @defer?Ver respuesta

Difiero contenido costoso que no participa del primer objetivo visual. Elijo trigger y prefetch según la probabilidad de uso, reservo espacio para evitar CLS y mido LCP, transferencia e interacción en una build de producción.

¿Qué ocurre desde bootstrapApplication hasta ver el primer componente?Ver respuesta

Angular crea el environment injector con los providers de la aplicación, instancia el componente raíz, crea su host view y ejecuta el primer render. La plantilla compilada crea nodos, evalúa bindings y conecta listeners antes de que los cambios posteriores entren en change detection.

¿Interpolación, property binding o attribute binding?Ver respuesta

Interpolación produce texto. Property binding escribe una propiedad runtime del elemento o componente. Attribute binding escribe el atributo, por ejemplo ARIA o SVG. Elijo según el destino real del valor, no según una preferencia de sintaxis.

¿Cómo actualiza Angular el DOM?Ver respuesta

La plantilla compilada contiene instrucciones para cada binding. Durante change detection Angular evalúa la expresión, compara el resultado con el valor anterior y escribe sólo el destino que cambió. No vuelve a construir todo el HTML del componente.

¿Qué adoptarías primero al modernizar una aplicación antigua?Ver respuesta

Actualizo majors soportadas y estabilizo tests. Después reduzco NgModules con standalone, migro control flow y recién introduzco Signals o zoneless donde el modelo de estado lo justifique. Cada etapa conserva una forma de medir y revertir.

¿Cómo te sentís con este tema?

08

Componentes, templates y composición

Un componente Senior mantiene una API pequeña, estado local explícito y un template legible. La composición supera a la herencia para reutilizar UI.

Teoría

Contrato del componente
  • Cada componente renderiza dentro de un host element. La propiedad host de la metadata declara clases, atributos, propiedades y listeners del host en un solo lugar; una binding del consumidor puede colisionar con una binding del componente y Angular resuelve la prioridad según cuál sea estática o dinámica.
  • @let declara un valor local que Angular mantiene actualizado. Una template reference variable como #input referencia un elemento, componente, directiva exportada o TemplateRef, y sólo existe dentro del scope de la view donde se declaró.
  • ng-container agrupa bindings sin crear un nodo DOM. ng-template declara un fragmento que no se renderiza por sí solo; Angular lo representa con TemplateRef y puede instanciarlo mediante NgTemplateOutlet o ViewContainerRef.createEmbeddedView.
Templates y fragmentos
  • NgComponentOutlet y ViewContainerRef.createComponent crean componentes conocidos en runtime. Los helpers inputBinding, outputBinding y twoWayBinding conectan su API al crearlos y evitan asignaciones o subscriptions manuales dispersas.
  • La metadata de un componente conecta una clase con su selector, template, estilos, estrategia de encapsulación, change detection, imports y providers. Los host bindings aplican propiedades, atributos o listeners al elemento anfitrión del componente.
  • input() declara un signal de entrada y output() crea un emisor tipado hacia el padre. model() combina una entrada con su salida nombreChange, lo que habilita two-way binding para controles cuyo valor forma parte de su contrato público.
Composición y render dinámico
  • La proyección con ng-content define slots estáticos. TemplateRef, ng-template, ViewContainerRef y creación dinámica cubren composición avanzada.
  • viewChild y viewChildren consultan la vista propia; contentChild y contentChildren consultan contenido proyectado. Las queries basadas en signals cambian cuando cambia el árbol. Una query required falla si el contrato no encuentra el hijo esperado.
Rendimiento del template
  • Una directiva añade comportamiento; un componente añade comportamiento y vista. Una pipe pura debe transformar sin efectos y devolver el mismo resultado para las mismas entradas.
  • Angular puede evaluar una expresión de template durante cada comprobación de la vista. Una función costosa invocada desde el template repite ese trabajo. computed memoriza una derivación y sólo la recalcula cuando cambia alguno de los signals leídos.

Ejemplo

@Component({
  selector: 'user-picker',
  host: { '[class.disabled]': 'disabled()' },
  template: `
    @let selected = selectedUser();
    <button #trigger (click)="open.set(true)">{{ selected?.name ?? 'Elegir' }}</button>
    <ng-template #row let-user>
      <button (click)="select(user)">{{ user.name }}</button>
    </ng-template>
  `,
})
export class UserPicker {
  disabled = input(false);
  selectedUser = model<User | null>(null);
  open = signal(false);
}

Preguntas y respuestas

¿Input o servicio de estado?Ver respuesta

Un input expresa dependencia del padre y mantiene el componente reutilizable. Un servicio sirve para estado compartido por ramas distantes o un dominio. No ocultes datos de presentación globalizando todo.

¿Content projection o input TemplateRef?Ver respuesta

ng-content funciona para slots fijos y ergonomía declarativa. TemplateRef permite repetir, parametrizar o elegir plantillas en tiempo de ejecución.

¿Qué debería formar parte de la API pública de un componente?Ver respuesta

Sólo inputs, outputs y slots que representan variaciones reales del producto. Si una opción expone detalles internos o combina estados inválidos, prefiero dividir responsabilidades o modelar una unión más precisa.

¿Cuándo crearías una directiva en lugar de un componente?Ver respuesta

Creo una directiva cuando necesito añadir comportamiento a un elemento existente sin imponer markup. Creo un componente cuando la unidad posee estructura visual, estado y una API que deben evolucionar juntos.

¿Property binding o attribute binding?Ver respuesta

Una property binding escribe en la propiedad runtime del elemento o componente, por ejemplo [disabled]. Una attribute binding escribe el atributo HTML con [attr.aria-expanded]. Uso atributos para ARIA, SVG o metadata sin una propiedad DOM equivalente.

¿Qué representa una template reference variable?Ver respuesta

Depende del nodo: en un elemento nativo referencia el HTMLElement, en un componente su instancia, con exportAs una directiva y sobre ng-template un TemplateRef. Su scope pertenece a la view que la declara.

¿Por qué ng-template no aparece en el DOM?Ver respuesta

Declara una receta de view. Angular sólo crea sus nodos cuando una directiva, NgTemplateOutlet o ViewContainerRef instancia su TemplateRef. Esto permite repetir el fragmento y pasarle contexto.

¿Cómo crearías un componente dinámico con bindings?Ver respuesta

Uso ViewContainerRef.createComponent si debe formar parte de esa view y paso bindings con inputBinding, outputBinding o twoWayBinding. Para un caso declarativo puedo usar NgComponentOutlet; para lazy loading visual prefiero evaluar @defer.

¿Cómo te sentís con este tema?

09

Ciclo de vida y render hooks

El orden importa cuando un componente coordina inputs, queries, DOM y recursos externos.

Teoría

Modelo mental
  • Angular crea una instancia, asigna inputs, ejecuta el primer change detection, inicializa contenido y vista, y luego repite los hooks de check en cada recorrido. Cada hook corresponde a un punto concreto de ese proceso y no funciona como un evento genérico.
  • Los hooks de render no se ejecutan durante SSR. afterNextRender sirve para una operación DOM posterior al próximo render y afterEveryRender para una integración que debe acompañar renders sucesivos; ambos requieren cleanup si crean recursos persistentes.
  • El constructor configura dependencias y estado barato. ngOnInit usa inputs inicializados. ngOnChanges reacciona a cambios de inputs y corre antes de ngOnInit en la primera pasada.
Funcionamiento y APIs
  • ngAfterContentInit/Checked se relacionan con contenido proyectado. ngAfterViewInit/Checked se relacionan con la vista propia y queries.
  • afterNextRender ejecuta un callback después del siguiente render completo; afterEveryRender lo hace tras cada render. Agrupar escrituras DOM antes de lecturas geométricas evita alternar style recalculation y layout forzado.
Decisiones, riesgos y verificación
  • DestroyRef registra cleanup en el mismo contexto donde nace un recurso. takeUntilDestroyed completa una suscripción cuando ese contexto se destruye. Observers, timers y listeners creados fuera de Angular requieren también su función explícita de limpieza.
  • ExpressionChangedAfterItHasBeenCheckedError aparece en desarrollo cuando una expresión cambia después de que Angular ya verificó esa vista dentro del mismo ciclo. La causa suele ser un flujo de datos que escribe hacia un ancestro o modifica estado durante un hook tardío; diferir con un timer oculta la inconsistencia.

Fuentes del tema

Preguntas y respuestas

¿Constructor o ngOnInit?Ver respuesta

El constructor pertenece a TypeScript y DI. ngOnInit pertenece al ciclo de Angular y recibe inputs listos. Evitá I/O en ambos si un resolver, store o recurso expresa mejor la carga.

¿Cómo evitás leaks?Ver respuesta

Uso AsyncPipe, signals o takeUntilDestroyed; limpio APIs externas con DestroyRef.onDestroy. Después verifico navegación repetida con profiler y tests.

¿ngAfterViewInit o afterNextRender para medir DOM?Ver respuesta

ngAfterViewInit confirma que Angular inicializó la vista, pero una medición puede depender de un render posterior. afterNextRender ejecuta trabajo después del siguiente render del árbol y permite separar fases de escritura y lectura.

¿Qué ventaja aporta DestroyRef?Ver respuesta

Coloca la limpieza junto al recurso que la necesita y evita concentrar teardown sin contexto en ngOnDestroy. Lo uso con listeners, observers y takeUntilDestroyed para vincular su vida al contexto de inyección.

¿En qué orden corre la primera inicialización?Ver respuesta

Angular asigna inputs, ejecuta ngOnChanges, ngOnInit, hooks de content, hooks de view y completa el render. Los hooks Checked vuelven a correr en recorridos posteriores; los Init corren una vez.

¿afterNextRender funciona durante SSR?Ver respuesta

No. Los render callbacks dependen del navegador. Los uso para medir o integrar DOM después del render y mantengo el camino SSR libre de esa API.

¿Cuándo usarías ngOnChanges frente a computed?Ver respuesta

ngOnChanges sirve cuando necesito comparar cambios de inputs o ejecutar una adaptación imperativa. Un computed expresa mejor una derivación pura de signal inputs porque mantiene la relación sin sincronización manual.

¿Por qué un setTimeout puede esconder un ExpressionChanged?Ver respuesta

Mueve la mutación a otra task y evita la comprobación actual, pero conserva un flujo de datos mal ubicado. Corrijo quién posee el estado o muevo el trabajo al hook y fase adecuados.

¿Cómo te sentís con este tema?

10

Change detection, Signals y zoneless

Esta sección suele separar experiencia reciente de conocimiento heredado. Explicá quién notifica a Angular, qué vista queda dirty y cuándo se recalcula una derivación.

Teoría

Recorrido y notificaciones
  • Change detection recorre las views que Angular considera necesarias, evalúa sus bindings y compara el resultado con el valor anterior. Una notificación marca una view y sus ancestros para que el próximo recorrido incluya esa rama.
  • OnPush puede saltar un subárbol limpio. Un input con referencia nueva, un evento manejado en la view, una lectura de signal que cambia, AsyncPipe, setInput o markForCheck vuelven a marcar trabajo.
  • linkedSignal conserva un estado editable que se reinicia o adapta cuando cambia una dependencia. resource y httpResource modelan carga asíncrona reactiva; su conveniencia no reemplaza una política explícita de caché, invalidación y errores.
Signals y estado derivado
  • Default verifica un subárbol con mayor frecuencia. OnPush permite saltar subárboles cuando no reciben nuevos inputs ni notificaciones.
  • Un signal writable usa set o update; computed deriva estado, memoriza y rastrea dependencias dinámicas; effect conecta estado reactivo con una API no reactiva.
  • computed representa estado derivado: lee otros signals, memoriza el resultado y permanece libre de efectos. effect ejecuta una operación cuando cambian sus dependencias. Copiar una derivación mediante effect crea dos fuentes de verdad y puede producir ciclos o escrituras redundantes.
OnPush y zoneless
  • Signals comparan por Object.is salvo función de igualdad. Una mutación profunda conserva la referencia y puede ocultar el cambio.
  • untracked lee un signal sin registrar dependencia. Usalo cuando la lectura sea incidental, no para tapar un grafo mal diseñado.
Diagnóstico y rendimiento
  • Zoneless reduce parches y checks innecesarios. Requiere que las actualizaciones lleguen mediante APIs que notifican a Angular.
  • Signals y RxJS se complementan: signals para estado síncrono leído por la vista; RxJS para flujos asíncronos, cancelación, concurrencia y eventos.

Ejemplo

private readonly query = signal('');
readonly normalizedQuery = computed(() => this.query().trim().toLowerCase());
readonly results = computed(() =>
  this.items().filter(x => x.name.toLowerCase().includes(this.normalizedQuery()))
);

Preguntas y respuestas

¿OnPush vuelve inmutable la app?Ver respuesta

No. OnPush cambia cuándo Angular verifica la vista. La inmutabilidad facilita detectar cambios por referencia y evita estado compartido corrupto.

¿Cuándo usar effect?Ver respuesta

Para logging, almacenamiento, canvas, APIs del navegador o integración externa. Las derivaciones de UI pertenecen a computed.

¿Qué rompe al quitar ZoneJS?Ver respuesta

Código que muta campos sin emitir una notificación compatible, además de dependencias en eventos de NgZone. Migraría estado a signals o marcaría la vista.

¿Por qué una mutación profunda puede no actualizar la vista?Ver respuesta

Un signal compara el valor nuevo con el anterior mediante Object.is por defecto. Mutar una propiedad conserva la referencia y no publica otro valor. Creo una nueva referencia o modelo el campo como un signal independiente.

¿Cómo elegís entre computed y effect?Ver respuesta

computed calcula estado derivado y sólo depende de otros signals. effect sincroniza el grafo reactivo con una frontera externa como storage, logging o canvas. No copio estado derivado mediante efectos.

¿Qué marca una view OnPush como dirty?Ver respuesta

Una referencia nueva en un input, un evento manejado dentro de la view, un signal leído por la plantilla que cambia, AsyncPipe, setInput o markForCheck. Una mutación interna de un objeto sin notificación conserva la misma referencia y puede dejar la UI vieja.

¿markForCheck o detectChanges?Ver respuesta

markForCheck agenda la view para el próximo recorrido y mantiene el flujo normal. detectChanges ejecuta una comprobación inmediata de esa view y sus hijos; lo reservo para integraciones controladas porque puede introducir trabajo y orden inesperados.

¿Qué resuelve linkedSignal?Ver respuesta

Modela un estado editable que depende de otra señal y necesita reajustarse cuando cambia esa fuente, como una selección que debe seguir siendo válida al reemplazar la lista. Evita un effect dedicado a copiar y corregir estado.

¿Cómo investigás demasiados renders?Ver respuesta

Grabo una interacción con Angular DevTools y el Performance panel, identifico qué notificación marcó la rama y reviso referencias, funciones de template y efectos. Cambio una causa y vuelvo a medir scripting e INP.

¿Cómo te sentís con este tema?

11

Dependency Injection en profundidad

Angular resuelve dependencias en jerarquías. La ubicación del provider define vida útil, visibilidad y aislamiento.

Teoría

Modelo mental
  • Dependency Injection separa la creación de una dependencia de su consumo. Angular busca un provider para un token, ejecuta su factory cuando corresponde y conserva la instancia según el injector que la posee.
  • La resolución comienza en el injector asociado al nodo o environment actual y asciende por la jerarquía. Un provider de componente crea una instancia por subárbol; uno de ruta vive con ese entorno lazy; providedIn: 'root' comparte la instancia en la aplicación.
  • useClass, useValue, useFactory y useExisting expresan distintas formas de producir un token. Los multi providers acumulan varios valores bajo el mismo token y sirven para pipelines extensibles.
Funcionamiento y APIs
  • providedIn: 'root' crea un singleton por root EnvironmentInjector y permite tree shaking. Un provider de componente crea una instancia por componente.
  • La resolución busca primero ElementInjectors y después EnvironmentInjectors. Lazy routes pueden crear contextos e instancias separadas.
  • useClass crea una clase para un token; useValue entrega un valor existente; useExisting crea un alias; useFactory calcula la dependencia con otras inyecciones. Los multi providers acumulan varios valores bajo un token e InjectionToken representa contratos que no existen como clase en runtime.
Decisiones, riesgos y verificación
  • providers es visible para vista y contenido descendiente; viewProviders oculta el provider al contenido proyectado.
  • self, skipSelf, host y optional limitan la búsqueda. Usalos para contratos intencionales, no como parche.
  • inject() necesita injection context: inicializador, constructor administrado por DI, factory o runInInjectionContext.

Ejemplo

export const ANALYTICS = new InjectionToken<Analytics>('analytics');

export const appConfig: ApplicationConfig = {
  providers: [
    { provide: ANALYTICS, useClass: BrowserAnalytics },
    { provide: HTTP_INTERCEPTORS, useClass: AuditInterceptor, multi: true },
  ],
};

Preguntas y respuestas

¿Un servicio Angular es siempre singleton?Ver respuesta

Es singleton dentro del injector que lo provee. Dos injectors pueden crear dos instancias. La frase 'singleton global' omite el scope.

¿Por qué usar InjectionToken?Ver respuesta

Permite inyectar configuración, funciones o interfaces borradas en runtime. El token conserva identidad y puede definir factory y tipo.

¿Qué diferencia hay entre useClass y useExisting?Ver respuesta

useClass pide al injector que construya otra instancia de la clase indicada. useExisting crea un alias hacia una instancia registrada. Uso el alias cuando dos tokens deben compartir identidad y estado.

¿Cómo afecta el lugar del provider a una feature?Ver respuesta

El injector que registra el provider define su alcance y vida útil. Un provider de componente aísla instancias por subárbol; uno de ruta puede vivir con la feature lazy; root comparte la instancia en la aplicación.

¿Cómo busca Angular un provider?Ver respuesta

Empieza en el injector del contexto actual, consulta providers del nodo y environment, y asciende hasta encontrar el token. Los modificadores de resolución cambian ese recorrido; si ningún provider existe y no es optional, Angular lanza un error.

¿Provider de componente o de ruta?Ver respuesta

El provider de componente crea una instancia asociada a ese subárbol y se destruye con él. El provider de ruta comparte estado entre componentes de la feature lazy y vive con su environment injector.

¿Para qué sirve un multi provider?Ver respuesta

Permite que varias partes registren valores bajo el mismo token y que el consumidor reciba un array. Lo uso para plugins, validadores o pipelines extensibles donde cada feature aporta una implementación.

¿Qué riesgo tiene providedIn: 'root'?Ver respuesta

Convierte el servicio en singleton de aplicación. Si guarda estado de pantalla o usuario sin una política de reset, puede mezclar ciclos de navegación y sesiones. El scope debe coincidir con la vida útil del dato.

¿Cómo te sentís con este tema?

12

RxJS y concurrencia

La entrevista Senior suele plantear búsquedas, guardado, polling o eventos concurrentes. Elegí el operador a partir de la política de concurrencia.

Teoría

Contrato Observable
  • Una subscription representa la ejecución y su teardown. complete y error cierran el contrato; unsubscribe lo termina desde el consumidor. El producer debe registrar cleanup para liberar timers, listeners, sockets o requests cancelables.
  • La ubicación de catchError cambia el alcance del fallo. Dentro de un flattening operator recupera una operación interna y mantiene viva la fuente; afuera termina o reemplaza el flujo completo.
  • shareReplay comparte una subscription y conserva emisiones para suscriptores tardíos. Antes de usarlo como caché hay que decidir tamaño de buffer, refCount, reset, errores, vida útil y aislamiento por usuario.
Operadores y concurrencia
  • Cold observables crean el productor por subscription; hot observables comparten un productor externo. share y shareReplay cambian esa relación.
  • switchMap cancela el inner anterior; sirve para búsqueda. concatMap serializa; sirve para preservar orden. mergeMap permite concurrencia. exhaustMap ignora disparos mientras uno está activo.
  • map transforma valores; tap ejecuta efectos; filter decide emisiones; scan acumula; catchError define el límite del error.
Errores y teardown
  • La ubicación de catchError define qué stream termina. Dentro de switchMap o de otro flattening operator, el error se reemplaza sólo para esa petición y el stream exterior puede seguir escuchando. Fuera del operador, el error finaliza la cadena completa salvo que se retorne otro observable.
  • combineLatest reacciona a últimos valores; forkJoin espera que todos completen; withLatestFrom toma contexto cuando la fuente emite.
Sharing y caché
  • Subject no conserva un valor, BehaviorSubject guarda el último y exige uno inicial, y ReplaySubject reproduce una cantidad o ventana de emisiones. Exponer sólo asObservable() impide que consumidores externos escriban en el estado del productor.
  • shareReplay({bufferSize: 1, refCount: true}) puede cachear, pero necesitás invalidación, manejo de error y semántica de vida útil.

Ejemplo

results$ = this.query.valueChanges.pipe(
  debounceTime(250),
  distinctUntilChanged(),
  switchMap(query => this.api.search(query).pipe(
    catchError(error => of({ items: [], error }))
  )),
  shareReplay({ bufferSize: 1, refCount: true })
);

Preguntas y respuestas

¿Por qué no subscribirse dentro de subscribe?Ver respuesta

Anida ciclos de vida y errores, complica cancelación y crea carreras. Un operador de flattening expresa la política y devuelve una sola subscription.

¿Cómo cancelás una búsqueda anterior?Ver respuesta

Debounceo, elimino duplicados y uso switchMap. El unsubscribe cancela la petición XHR/fetch cuando el backend y el cliente lo permiten.

¿switchMap cancela el trabajo en el servidor?Ver respuesta

Unsubscribe detiene la observación y puede abortar el request si la fuente integra cancelación, como HttpClient. El servidor puede haber iniciado el trabajo. Las operaciones con efectos necesitan idempotencia o un protocolo de cancelación propio.

¿Qué riesgo tiene shareReplay?Ver respuesta

Puede retener el último valor y mantener viva la suscripción más tiempo del esperado. Defino buffer, refCount y política de reset según el ciclo de vida. También decido cómo invalidar errores y datos stale.

¿Dónde colocás catchError dentro de switchMap?Ver respuesta

Dentro si cada request puede fallar y la fuente debe seguir escuchando búsquedas. Fuera si cualquier error termina o reemplaza el flujo completo. La posición determina qué subscription se cierra.

¿Qué debe hacer el teardown de un Observable?Ver respuesta

Detiene el recurso creado por esa suscripción: remueve listeners, limpia timers, cierra sockets o aborta I/O compatible. También debe tolerar llamadas repetidas sin producir efectos inválidos.

¿Cuándo shareReplay(1) puede producir un leak?Ver respuesta

Cuando mantiene la fuente suscripta después de irse el último consumidor o conserva un valor pesado sin política de reset. Configuro refCount y resets según si necesito una caché persistente o sólo compartir consumidores simultáneos.

¿Cómo elegir entre los cuatro flattening operators?Ver respuesta

Elijo la política de concurrencia: switchMap reemplaza, concatMap encola, mergeMap permite paralelismo y exhaustMap ignora nuevas entradas mientras una sigue activa. La semántica del negocio decide cuál pérdida u orden resulta válido.

¿Cómo te sentís con este tema?

13

Estado: local, servicios, Signals y NgRx

No existe una herramienta única. Un Senior reduce el alcance del estado y aumenta la estructura cuando la complejidad lo exige.

Teoría

Modelo mental
  • El estado pertenece al dueño más cercano que necesita escribirlo. Un componente resuelve estado efímero; un servicio de feature coordina varias vistas; un store formaliza eventos y efectos cuando muchas partes modifican el mismo dominio.
  • Estado fuente y estado derivado deben estar separados. Signals computed y selectors calculan vistas del mismo dato; copiar el resultado a otra variable exige sincronización y permite inconsistencias.
  • Estado local de componente: UI efímera. Servicio de feature: coordinación de una rama. Store global: datos compartidos, flujos complejos, auditoría o herramientas de desarrollo.
Funcionamiento y APIs
  • Server state es una copia local de datos remotos y necesita caché, stale time, invalidación, deduplicación y reintentos. Client state nace en la interfaz, como selección, filtros o un wizard, y su ciclo de vida depende de la navegación y del alcance de la feature.
  • En NgRx, una action describe un evento, un reducer calcula el siguiente estado sin efectos, un selector deriva y memoriza consultas, y un effect conecta eventos con I/O. Entity normaliza colecciones como un diccionario de ids más una lista ordenada.
  • El estado derivado se calcula desde la fuente mediante selectors o computed; almacenarlo por separado exige sincronizar copias. Las actions expresadas como hechos de dominio, por ejemplo invoiceSubmitted, permiten que varios efectos reaccionen sin acoplarse al botón que originó el evento.
Decisiones, riesgos y verificación
  • ComponentStore y SignalStore encapsulan estado de una feature sin crear un store global. La elección depende de la estabilidad de la API, el ecosistema disponible y la experiencia del equipo con el modelo reactivo.
  • Una actualización optimista modifica la UI antes de recibir confirmación. El diseño necesita rollback o reconciliación cuando falla, una clave idempotente para evitar duplicados y una regla para conflictos entre la versión local y la remota.

Preguntas y respuestas

¿Cuándo elegir NgRx?Ver respuesta

Cuando varios flujos comparten estado, necesitás trazabilidad, efectos coordinados o reglas complejas. Para un formulario aislado, un store global aumenta costo sin beneficio.

¿Qué nunca guardarías en el store?Ver respuesta

Derivaciones recalculables, objetos no serializables sin necesidad y estado DOM efímero. Guardaría la fuente mínima de verdad.

¿Cómo separás server state de client state?Ver respuesta

Server state es una copia de datos remotos y requiere stale time, caché e invalidación. Client state nace en la interacción, por ejemplo filtros o pasos de un wizard. Separarlos evita que un store trate ambos ciclos de vida con la misma política.

¿Qué señales justifican introducir NgRx?Ver respuesta

Lo considero cuando varios flujos escriben el mismo dominio, necesito trazabilidad de eventos, efectos coordinados o reglas de actualización compartidas. Un formulario local o una pantalla aislada no justifican ese costo por sí solos.

¿Cuándo un servicio con Signals deja de alcanzar?Ver respuesta

Cuando varias features escriben el mismo dominio, necesito historial claro de eventos, efectos coordinados, herramientas de inspección o reglas consistentes de actualización. En ese punto un store formal reduce caminos implícitos.

¿Qué es estado derivado?Ver respuesta

Es un valor calculable desde estado fuente, como el total de un carrito. Lo expreso con computed o un selector y no lo guardo por separado, porque dos copias pueden divergir.

¿Cómo modelás una optimistic update?Ver respuesta

Aplico un cambio local con un identificador de operación, envío la request y confirmo o revierto según el resultado. Resuelvo concurrencia, duplicados y mensajes de error sin perder una edición posterior.

¿Qué pondrías en el store global?Ver respuesta

Estado de dominio compartido cuya vida cruza rutas y necesita coordinación. Estados de foco, accordion o formulario temporal permanecen cerca del componente salvo que otra parte de la aplicación deba controlarlos.

¿Cómo te sentís con este tema?

14

Routing y navegación

El router define fronteras de carga, autorización y datos. Diseñá rutas como parte de la arquitectura.

Teoría

Modelo mental
  • El Router compara la URL con un árbol de rutas, ejecuta redirects, guards y resolvers, activa componentes en outlets y conserva snapshots más streams de cambios. La navegación puede cancelarse o redirigirse antes de crear la vista.
  • loadComponent y loadChildren crean fronteras lazy. Los providers declarados en una ruta pertenecen a su environment injector y permiten aislar servicios por feature.
  • Component input binding puede llevar params, query params, datos estáticos y resolvers a inputs del componente. Esa API reduce subscriptions manuales, pero el nombre y la ausencia de cada valor siguen formando parte del contrato de ruta.
Funcionamiento y APIs
  • loadComponent y loadChildren crean fronteras de lazy loading que descargan una feature al navegar. Un chunk por componente pequeño aumenta requests y overhead; una frontera por capacidad de producto suele equilibrar carga inicial y reutilización.
  • Guards controlan navegación en el cliente; el servidor debe repetir autorización. CanMatch evita seleccionar rutas; CanActivate decide activación.
  • Resolvers reducen estados intermedios cuando la ruta necesita datos antes de mostrar. Para pantallas tolerantes al loading, una carga dentro de la feature mejora percepción.
Decisiones, riesgos y verificación
  • Los path params identifican recursos dentro de la ruta; los query params representan filtros o estado compartible; el fragment apunta a una sección del documento. Rutas hijas componen layouts, outlets muestran árboles paralelos, redirects normalizan URLs y route data aporta metadata estática.
  • Una RouteReuseStrategy puede conservar la instancia y el DOM de una ruta al navegar. También conserva memoria, estado y suscripciones; una política de invalidación decide cuándo destruir ese snapshot.
  • RouterTestingHarness crea un router de prueba, navega por URL y expone el componente activado. Permite comprobar parámetros inválidos, redirects, guards rechazados y errores de resolvers desde el comportamiento observable.

Ejemplo

export const routes: Routes = [{
  path: 'users/:id',
  loadComponent: () => import('./user.page').then(m => m.UserPage),
  canActivate: [authGuard],
  resolve: { user: userResolver },
  providers: [UserFeatureStore],
}];

Fuentes del tema

Preguntas y respuestas

¿Guard equivale a seguridad?Ver respuesta

No. Un usuario controla el cliente. El guard mejora UX y evita navegación accidental; la API autoriza cada operación.

¿Resolver o carga en componente?Ver respuesta

Resolver cuando la vista no tiene sentido sin el dato o necesitás coherencia antes de activar. Carga en componente para streaming, skeletons o contenido parcial.

¿Qué diferencia hay entre CanMatch y CanActivate?Ver respuesta

CanMatch decide si una configuración de ruta puede participar del matching y permite probar otra ruta. CanActivate actúa después de elegirla y decide si se activa. Ninguno reemplaza la autorización del servidor.

¿Cuándo evitarías un resolver?Ver respuesta

Evito bloquear navegación para datos secundarios o lentos. La pantalla puede mostrar estructura, loading y recuperación parcial. Uso resolver cuando el dato define si la ruta tiene sentido o cuando entrar sin él produciría un estado inválido.

¿En qué orden intervienen guards y resolvers?Ver respuesta

Angular reconoce la ruta, evalúa guards y, si permiten continuar, ejecuta resolvers antes de activar el componente. Un redirect o cancelación corta la navegación; los errores necesitan una política de navegación o error handler.

¿Un guard protege datos?Ver respuesta

No. Controla la navegación del cliente y mejora la experiencia. La API debe autenticar y autorizar cada operación porque un usuario puede llamar el endpoint sin pasar por el Router.

¿Cuándo usarías un resolver?Ver respuesta

Cuando la ruta no tiene sentido sin un dato pequeño y crítico o necesito decidir antes de activarla. Para contenido secundario prefiero cargar dentro de la vista y mostrar estados parciales, porque un resolver largo retrasa toda la navegación.

¿Cómo probás el Router?Ver respuesta

Uso RouterTestingHarness con rutas reales, navego una URL y compruebo componente, redirects y estado visible. Tests aislados cubren la lógica de guards o resolvers y los de integración cubren el orden de navegación.

¿Cómo te sentís con este tema?

15

Formularios complejos

Los formularios Senior incluyen tipado, composición, validación asíncrona, accesibilidad y rendimiento.

Teoría

Modelo mental
  • Reactive Forms crea un árbol de controles en TypeScript. Cada control conserva valor, estado de validación, interacción y disabled; el grupo agrega los estados de sus hijos y emite cuando cambia el modelo.
  • updateOn: 'blur' o 'submit' reduce validaciones y requests durante escritura. Un validador de grupo compara campos relacionados y devuelve el error en el nivel que posee la regla.
  • Un validador asíncrono debe completar y resolver carreras. Debounce, switchMap y una caché corta evitan requests innecesarias; la UI distingue PENDING, error de red y valor inválido.
  • Reactive Forms modela el formulario en TypeScript; template-driven sirve para casos pequeños. Typed Forms reduce casts y errores.
Funcionamiento y APIs
  • FormControl, FormGroup, FormArray y FormRecord cubren formas fijas, listas y claves dinámicas.
  • Un validador síncrono devuelve ValidationErrors | null; uno asíncrono devuelve Promise u Observable y necesita cancelación o debounce según el caso.
  • ControlValueAccessor conecta un control propio con Angular Forms mediante cuatro operaciones: escribir un valor, registrar cambios, registrar touched y aplicar disabled. El control no debe volver a emitir como cambio el valor que Forms acaba de escribirle, porque eso crea un loop.
Decisiones, riesgos y verificación
  • Copiar cada emisión de valueChanges a otro objeto crea dos representaciones del formulario que pueden divergir. El FormGroup puede ser la fuente de verdad durante la edición y el submit puede mapear su valor a un comando o DTO.
  • Los errores se muestran después de interacción o submit para evitar ruido antes de que el usuario actúe. aria-describedby asocia el mensaje con el control; el foco debe llegar al primer campo inválido cuando un submit no puede continuar.
  • Signal Forms ofrece un modelo nuevo en versiones recientes. Presentalo como opción a evaluar, no como reemplazo automático de Reactive Forms.

Ejemplo

readonly form = new FormGroup({
  email: new FormControl('', {
    nonNullable: true,
    validators: [Validators.required, Validators.email],
    asyncValidators: [uniqueEmailValidator(this.http)],
    updateOn: 'blur',
  }),
  addresses: new FormArray<FormGroup<AddressControls>>([]),
});

Fuentes del tema

Preguntas y respuestas

¿Cómo diseñarías 60 formularios dinámicos?Ver respuesta

Defino un schema tipado, componentes por tipo de campo, reglas de visibilidad derivadas y validadores registrables. Separo datos, layout y comportamiento; pruebo el motor con casos de contrato.

¿Qué falla en un CVA?Ver respuesta

Emitir durante writeValue, olvidar estado disabled o no marcar touched. Eso crea loops y rompe la semántica del formulario.

¿Qué contrato debe cumplir un ControlValueAccessor?Ver respuesta

Debe escribir el valor externo sin emitir un cambio de usuario, registrar callbacks de cambio y touched, y respetar el estado disabled. También necesita una representación clara para null y valores parciales.

¿Cómo evitás carreras en validación asíncrona?Ver respuesta

Modelo la validación como un flujo que cancela la consulta anterior al cambiar el valor. Aplico debounce cuando corresponde y distingo error de transporte, valor inválido y estado pendiente en la interfaz.

¿Dónde ubicarías una validación entre dos campos?Ver respuesta

En el FormGroup que posee ambos controles. El validador devuelve un error del grupo y la presentación decide en qué campos anunciarlo sin mutar errores ajenos.

¿Qué cambia con updateOn: 'blur'?Ver respuesta

El control actualiza valor y validación al perder foco. Reduce trabajo y requests mientras se escribe, pero cambia cuándo valueChanges emite y cuándo la UI puede mostrar el resultado.

¿Cómo tipás un FormArray?Ver respuesta

Declaro el tipo del control repetido, por ejemplo FormArray<FormGroup<AddressControls>>. El tipo describe controles, mientras getRawValue produce el valor incluyendo controles disabled.

¿Cómo enfocás el primer error al enviar?Ver respuesta

Marco controles como touched, localizo el primer elemento inválido siguiendo el orden visual, lo enfoco y conecto el mensaje con aria-describedby. Un resumen de errores puede enlazar cada campo en formularios largos.

¿Cómo te sentís con este tema?

16

HTTP, APIs, errores y caché

El cliente debe modelar contratos, cancelación y fallos. Los interceptors resuelven preocupaciones transversales, no lógica de dominio.

Teoría

Modelo mental
  • HttpRequest y HttpHeaders son inmutables. Un interceptor usa request.clone para cambiar URL, headers, params o body y luego entrega la request al siguiente handler.
  • Los interceptors funcionales se ejecutan en el orden de registro para la request y en orden inverso para la response. HttpContextToken permite activar políticas por request sin convertirlas en headers de red.
  • observe: 'events' expone progreso, headers y respuesta final. El progreso de upload requiere un backend que lo soporte; fetch no informa progreso de subida del mismo modo que XHR.
  • provideHttpClient registra el cliente HTTP y los interceptors funcionales forman una cadena alrededor de cada request. Los servicios o repositorios de feature encapsulan URLs, DTOs y reglas de acceso para que los componentes dependan del dominio.
Funcionamiento y APIs
  • Los tipos de TypeScript desaparecen al compilar y no validan el JSON recibido. Un schema runtime comprueba datos externos antes de usarlos; un mapper traduce el DTO del servidor a un modelo interno estable.
  • Un interceptor puede agregar autenticación, correlation IDs y telemetría, o normalizar errores. Un loader global necesita contar requests concurrentes: un booleano se apaga cuando termina la primera aunque otras sigan activas.
  • Un retry repite una operación que falló. Los métodos idempotentes pueden repetirse sin cambiar el resultado; una escritura necesita una clave de idempotencia si existe riesgo de duplicación. Backoff, jitter y un límite evitan amplificar una caída, y los errores funcionales 4xx requieren otra acción.
Decisiones, riesgos y verificación
  • Timeout, cancelación, offline, fallo de red, 401/403, 404, validación y 5xx representan estados distintos. La interfaz puede ofrecer reintento para red o timeout, login para 401, corrección de campos para validación y un fallback ante errores del servidor.
  • httpResource conecta HttpClient con una API de signals para request, valor, loading y error. En dominios grandes, la estrategia todavía necesita claves de caché, invalidación, aislamiento por usuario y coordinación con otras escrituras.
  • Una caché se define por su clave, vida útil, política de invalidación y aislamiento. La deduplicación comparte una petición en curso; stale-while-revalidate entrega el valor anterior mientras actualiza. Incluir el usuario o tenant en la clave evita mezclar datos privados.

Ejemplo

export const authInterceptor: HttpInterceptorFn = (request, next) => {
  const token = inject(AuthStore).token();
  const authenticated = request.clone({
    setHeaders: { Authorization: `Bearer ${token}` },
  });
  return next(authenticated);
};

Fuentes del tema

Preguntas y respuestas

¿Dónde refrescarías un token?Ver respuesta

En una capa de autenticación coordinada por interceptor, con una sola renovación en vuelo y cola controlada. Evito loops y limpio sesión si falla el refresh.

¿Cómo tipar una respuesta HTTP?Ver respuesta

El generic de HttpClient expresa la expectativa, no valida el servidor. En una frontera crítica valido y transformo el DTO antes de exponerlo.

¿Por qué importa el orden de los interceptors?Ver respuesta

Cada interceptor envuelve al siguiente. El request avanza en el orden registrado y la respuesta vuelve en orden inverso. Autenticación, retry, caché y logging pueden cambiar su comportamiento según esa composición.

¿Cómo invalidás una caché después de una mutación?Ver respuesta

Relaciono cada escritura con las keys afectadas. Puedo invalidar, actualizar de forma optimista o reemplazar con la respuesta del servidor. La política incluye rollback y evita borrar datos de dominios no relacionados.

¿Por qué una request de HttpClient se clona?Ver respuesta

HttpRequest es inmutable. clone crea una request con los cambios y conserva el objeto original para que la cadena de interceptors pueda razonar sin mutaciones compartidas.

¿En qué orden corren los interceptors?Ver respuesta

La request atraviesa la lista en el orden de registro. La response vuelve por la cadena en orden inverso, como capas anidadas. El orden afecta auth, cache, retry, loaders y telemetría.

¿Para qué sirve HttpContext?Ver respuesta

Transporta configuración local dentro del pipeline sin enviarla al servidor. Un interceptor puede leer un HttpContextToken para omitir auth, activar cache o cambiar tratamiento de errores para una request concreta.

¿Cómo evitás dos refresh de token simultáneos?Ver respuesta

Comparto una única operación de refresh mientras esté activa, encolo o reintento las requests originales después del nuevo token y limpio el estado al terminar. Si el refresh falla, cierro sesión una sola vez.

¿Cómo te sentís con este tema?

Bloque 03

Plataforma y arquitectura

Browser, DOM, red, límites, patrones, SOLID y evolución del código.

17

Browser internals, DOM, storage y red

Angular corre sobre la plataforma web. Un Senior entiende el costo de DOM, layout, almacenamiento, navegación y protocolos.

Teoría

Fundamentos
  • DOM representa el documento; BOM agrupa APIs del navegador como window, history, location, navigator y screen. Angular abstrae parte del DOM, pero no reemplaza la plataforma.
  • Selección: querySelector, querySelectorAll, getElementById. Eventos atraviesan capture, target y bubble. Delegation aprovecha bubbling para manejar listas dinámicas.
  • preventDefault evita la acción por defecto; stopPropagation detiene propagación. Usarlos sin entender semántica rompe formularios, enlaces y accesibilidad.
  • El navegador parsea HTML y CSS, construye DOM y CSSOM, calcula estilos y layout, pinta y compone capas. Leer layout después de escribir estilos puede forzar reflow.
Mecanismo y aplicación
  • localStorage persiste por origin y ofrece API síncrona; sessionStorage vive por pestaña; IndexedDB almacena datos estructurados de forma asíncrona. Cookies viajan según sus atributos y reglas de request.
  • Same-origin combina scheme, host y port. CORS permite que un servidor autorice lecturas cross-origin; la preflight OPTIONS valida ciertos métodos y headers.
  • HTTP cache usa Cache-Control, validators como ETag y claves que pueden variar. Service Worker puede interceptar requests y agrega otra capa de cache e invalidación.
Decisiones y límites
  • DNS resuelve host; TLS autentica y cifra; HTTP transporta requests. HTTP/2 multiplexa streams; HTTP/3 usa QUIC sobre UDP.
  • SPA actualiza vistas sin recargar documento. History API mantiene URL; el servidor debe redirigir rutas de app al HTML o renderizarlas.
  • Web Worker ejecuta JavaScript fuera del main thread y se comunica por mensajes. No accede al DOM. Service Worker opera como proxy de red y ciclo separado.

Fuentes del tema

Preguntas y respuestas

¿DOM y BOM?Ver respuesta

DOM modela el documento. BOM reúne objetos y APIs del entorno del navegador, como history, location y navigator.

¿localStorage, sessionStorage o IndexedDB?Ver respuesta

Elijo localStorage para pocas preferencias no sensibles, sessionStorage para vida de pestaña e IndexedDB para volumen, queries y trabajo asíncrono.

¿Qué es CORS?Ver respuesta

Una política del navegador que permite al servidor declarar qué origins pueden leer una respuesta. No protege endpoints de clientes no navegador ni reemplaza autorización.

¿Reflow y repaint?Ver respuesta

Layout recalcula geometría; paint genera píxeles; compositing combina capas. Cambios y lecturas intercaladas pueden forzar trabajo síncrono.

¿Qué produce un forced synchronous layout?Ver respuesta

Una escritura invalida estilos o layout y una lectura geométrica posterior, como getBoundingClientRect, obliga al navegador a calcular el resultado en ese momento. Agrupo lecturas y escrituras para evitar repetir ese trabajo dentro de un loop.

¿CORS protege una API contra clientes no autorizados?Ver respuesta

No. CORS controla qué respuestas puede leer JavaScript desde otro origin en un navegador. Un script de servidor puede llamar al endpoint. La API todavía necesita autenticación, autorización y validación.

¿Cómo te sentís con este tema?

18

Arquitectura de aplicaciones Angular

Una arquitectura útil reduce acoplamiento y hace visibles los límites del dominio.

Teoría

Fundamentos
  • La organización por feature agrupa UI, acceso a datos, modelos y rutas que cambian por la misma capacidad de producto. Una organización global por tipo técnico dispersa una modificación entre carpetas distantes y debilita los límites de dominio.
  • Un componente presentacional recibe datos y emite eventos; un orquestador coordina estado, navegación y servicios. La separación reduce dependencias cuando varias vistas reutilizan la presentación, pero añade capas vacías si ambas piezas cambian siempre juntas.
  • Dependency inversion hace que el dominio dependa de un contrato estable y que el detalle implemente ese contrato. En Angular, un InjectionToken más un adapter permite cambiar analytics, storage, pagos o una API externa sin modificar consumidores.
Mecanismo y aplicación
  • La public API de una librería o feature declara qué símbolos pueden consumir otros módulos. Los imports profundos atraviesan ese límite, acoplan al árbol interno de archivos y convierten un refactor local en un cambio global.
  • Un monorepo mejora sharing y refactors coordinados; agrega costo de tooling y ownership. Nx puede imponer boundaries y cachear tareas.
Decisiones y límites
  • Micro-frontends sirven para despliegue y ownership independientes. Aumentan duplicación, integración, observabilidad y consistencia visual.
  • Un Architecture Decision Record conserva el contexto, las alternativas evaluadas, la decisión, sus consecuencias y una fecha de revisión. El registro explica por qué existe una restricción cuando cambia el equipo o el contexto original.

Fuentes del tema

Preguntas y respuestas

¿Clean Architecture en frontend?Ver respuesta

Uso sus límites y dependency inversion donde protegen reglas de negocio. Evito copiar capas backend si solo agregan archivos y mapeos.

¿Cuándo extraer una librería?Ver respuesta

Cuando existe un contrato estable y más de un consumidor real, o cuando el límite necesita ownership y tests propios. Extraer por anticipación congela APIs inmaduras.

¿Cómo detectás una frontera de feature incorrecta?Ver respuesta

Aparecen imports circulares, cambios coordinados entre carpetas supuestamente independientes y servicios compartidos que conocen todos los dominios. Reubico el comportamiento según ownership y expongo una API pequeña por frontera.

¿Qué problema genera una carpeta shared sin reglas?Ver respuesta

Recibe componentes, modelos y servicios de dominios distintos hasta convertirse en una dependencia global. Separo primitives reutilizables de contratos de negocio y dejo cada modelo cerca de la feature que lo posee.

¿Cómo evitás que una feature dependa de detalles de otra?Ver respuesta

Expongo una public API pequeña y contratos de dominio. La feature consumidora no importa componentes internos, stores privados ni rutas de archivos profundas; se comunica mediante servicios, eventos o modelos publicados.

¿Cuándo una capa facade agrega valor?Ver respuesta

Cuando concentra varios stores o servicios, traduce modelos y ofrece casos de uso estables a la UI. Si sólo reenvía cada método con el mismo nombre y tipo, agrega navegación sin reducir acoplamiento.

¿Cómo te sentís con este tema?

19

Patrones, SOLID y calidad de diseño

Los patrones nombran soluciones recurrentes. Una entrevista Senior espera contexto y costo, no una lista memorizada.

Teoría

Fundamentos
  • Strategy para políticas intercambiables; Adapter para integrar contratos externos; Facade para reducir superficie; Factory para construcción variable.
  • Observer aparece en RxJS; Decorator en metadata e interceptors; Command y event patterns aparecen en stores. Singleton depende del injector.
Mecanismo y aplicación
  • SRP separa motivos de cambio. OCP favorece extensión por contratos. LSP exige sustitución válida. ISP mantiene contratos pequeños. DIP invierte dependencias hacia abstracciones.
  • Composition over inheritance evita jerarquías rígidas. Las directivas, providers y content projection forman mecanismos de composición.
Decisiones y límites
  • Un god service acumula motivos de cambio; un shared module masivo crea dependencias implícitas; los barrel cycles ocultan ciclos; los boolean flags multiplican estados; las subscriptions anidadas pierden control de concurrencia y la lógica de negocio en templates se repite y resulta difícil de probar.

Fuentes del tema

Preguntas y respuestas

¿Cómo implementar Singleton?Ver respuesta

En Angular proveo el servicio en un injector compartido. La garantía vale dentro de ese scope; providers locales o múltiples aplicaciones crean otras instancias.

¿Facade sobre NgRx?Ver respuesta

Puede estabilizar la API de la feature y ocultar detalles del store. También puede esconder capacidades y duplicar nombres. La uso cuando protege un límite real.

¿Cómo aplicás Dependency Inversion en Angular?Ver respuesta

El consumidor depende de un contrato expresado por una clase abstracta o InjectionToken. La configuración conecta ese contrato con una implementación. Puedo cambiar la frontera en tests o por entorno sin enseñar detalles al consumidor.

¿Cuándo una facade empeora el diseño?Ver respuesta

Una facade que sólo renombra cada método añade navegación sin reducir acoplamiento. La uso cuando concentra un caso de uso, oculta coordinación entre dependencias o protege a la UI de cambios del subsistema.

¿Cómo aplicarías Strategy en Angular?Ver respuesta

Defino un contrato para la operación, registro implementaciones mediante DI y selecciono la estrategia por configuración o contexto. El consumidor conoce la capacidad, mientras cada algoritmo conserva tests y dependencias propias.

¿Qué señal indica que una abstracción llegó demasiado pronto?Ver respuesta

La interfaz tiene una sola implementación, replica todos sus métodos y cambia junto con el detalle. Espero casos de variación concretos y extraigo la frontera que esos casos comparten.

¿Cómo te sentís con este tema?

Bloque 04

Calidad y operación

Performance, rendering, testing, seguridad, CI/CD y observabilidad.

20

Rendimiento y Core Web Vitals

Optimizar sin medir cambia complejidad por intuición. Un Senior identifica la métrica, captura un perfil y verifica el resultado.

Teoría

Fundamentos
  • LCP mide cuándo aparece el mayor elemento visible, INP observa la latencia de las interacciones y CLS acumula desplazamientos inesperados. Bundle size, long tasks, memoria y frecuencia de renders explican sus causas. Lighthouse usa un entorno sintético; RUM registra dispositivos y redes reales.
  • Lazy routes y @defer sacan JavaScript del bundle inicial. El beneficio depende del waterfall de chunks, preloading, prefetch y caché HTTP: demasiadas fronteras pequeñas pueden intercambiar bytes iniciales por latencia de red.
Mecanismo y aplicación
  • OnPush permite saltar subárboles sin notificaciones, signals marcan consumidores precisos y un track estable conserva nodos de una lista. Virtual scroll limita el DOM visible; la paginación reduce además datos transferidos y trabajo del servidor.
  • Una pipe impura y una función costosa en template pueden ejecutarse en cada check. Listeners globales sin cleanup retienen vistas, las imágenes sin dimensiones causan CLS y una dependencia grande aumenta parse, compile y ejecución además de transferencia.
Decisiones y límites
  • AOT, tree shaking, budgets y source-map analysis detectan regresiones. Un import pequeño puede arrastrar una dependencia grande.
  • Las escrituras DOM invalidan estilos y las lecturas geométricas pueden forzar su cálculo. Agrupar ambas fases evita layout thrashing. Debounce reduce eventos de alta frecuencia; un Web Worker descarga CPU cuando el costo de serializar mensajes resulta menor que bloquear el main thread.

Fuentes del tema

Preguntas y respuestas

La app está lenta, ¿por dónde empezás?Ver respuesta

Defino la interacción lenta, reproduzco con datos reales y grabo performance. Identifico red, scripting, layout o memoria; cambio una causa y vuelvo a medir.

¿trackBy sigue existiendo?Ver respuesta

En *ngFor sí. El control flow moderno usa track. Ambos preservan identidad DOM; una clave inestable anula el beneficio.

¿Cómo empezás una investigación de rendimiento?Ver respuesta

Defino una interacción y una métrica, reproduzco con una build de producción y capturo un perfil. Después separo red, scripting, render y memoria. Optimizo el cuello medido y vuelvo a comparar bajo las mismas condiciones.

¿OnPush corrige una tarea larga de JavaScript?Ver respuesta

No. OnPush puede reducir verificaciones de vistas, pero una función que ocupa el main thread sigue bloqueando input y render. Divido el trabajo, reduzco su complejidad o lo muevo a un Worker si el costo de mensajes lo permite.

¿Cómo investigarías un INP alto?Ver respuesta

Reproduzco la interacción con Performance panel y RUM, localizo long tasks y separo scripting, style, layout y paint. Después reduzco trabajo de la ruta crítica, divido CPU o limita renders y vuelvo a medir en dispositivos reales.

¿Cuándo virtual scroll no alcanza?Ver respuesta

Virtual scroll reduce nodos DOM, pero no reduce datos descargados, filtros costosos ni memoria del modelo completo. Con cientos de miles de filas combino paginación server-side, consultas remotas y una ventana visible accesible.

¿Cómo te sentís con este tema?

21

SSR, SSG, hidratación y rendering híbrido

Elegí estrategia por ruta. SEO, personalización, costo de servidor y tiempo de interacción empujan decisiones distintas.

Teoría

Fundamentos
  • La hidratación reutiliza el DOM producido por el servidor y conecta las views del cliente sin reconstruir la página. El HTML del servidor y el primer render del cliente deben producir la misma estructura.
  • Date.now, Math.random, locale, datos privados y condiciones distintas entre servidor y navegador pueden crear mismatches. El servidor debe transferir el dato determinista o el cliente debe calcularlo después de hidratar.
  • Incremental hydration conserva bloques @defer deshidratados hasta un trigger hydrate on .... Event replay registra interacciones previas y las reproduce cuando la sección ya puede responder.
Mecanismo y aplicación
  • CSR simplifica aplicaciones privadas. SSG sirve contenido estable. SSR sirve HTML fresco y SEO. Hybrid combina estrategias por ruta.
  • Hydration reutiliza el HTML del servidor. El cliente debe producir una estructura compatible; DOM inválido o manipulación directa rompe el proceso.
  • Incremental hydration activa sectores cuando se necesitan y trabaja con @defer. Event replay conserva interacciones previas a la hidratación.
Decisiones y límites
  • window, document, storage y otras APIs del navegador no existen durante SSR. Platform checks, tokens inyectables y render hooks aíslan ese código para que el servidor pueda construir el HTML sin acceder al entorno cliente.
  • Transfer cache reutiliza en el cliente ciertas respuestas obtenidas durante SSR y evita una segunda petición inmediata. La clave y el HTML generado deben aislar datos por usuario para impedir que una respuesta privada termine en otra sesión.
  • Un placeholder con las mismas dimensiones que el contenido final reserva espacio y reduce CLS. El contenido above-the-fold participa en LCP y suele cargarse antes; los bloques secundarios admiten lazy loading o hidratación diferida.

Ejemplo

bootstrapApplication(AppComponent, {
  providers: [provideClientHydration()],
});

// La estructura renderizada debe coincidir en servidor y cliente.
@defer (on viewport; hydrate on interaction) {
  <reviews-panel />
} @placeholder {
  <reviews-skeleton />
}

Preguntas y respuestas

¿SSR mejora todo el rendimiento?Ver respuesta

Mejora entrega de HTML y SEO, pero agrega servidor e hidratación. Puede empeorar TTFB o interacción si el backend y el bundle no acompañan.

¿Qué causa hydration mismatch?Ver respuesta

HTML diferente entre servidor y cliente, fechas o random no deterministas, DOM manipulado antes de hidratar y markup inválido.

¿Cómo elegís estrategia de rendering por ruta?Ver respuesta

Uso SSG para contenido estable, SSR para HTML dependiente de la request y CSR para áreas privadas donde el shell aporta poco al servidor. Evalúo SEO, personalización, latencia, caché y costo operativo por ruta.

¿Qué causa un hydration mismatch?Ver respuesta

El cliente produce un árbol distinto al HTML del servidor por datos no deterministas, acceso al navegador o markup condicional. Comparto el estado inicial, aíslo APIs del browser y mantengo estable la estructura hasta hidratar.

¿Qué produce un hydration mismatch?Ver respuesta

El servidor y el primer render del cliente generan estructuras diferentes. Fechas, random, locale, acceso temprano al DOM o condiciones browser-only son causas comunes. Transfiero datos deterministas y pospongo efectos de navegador hasta después de hidratar.

¿Qué hace event replay?Ver respuesta

Captura interacciones que ocurren sobre HTML SSR antes de que Angular conecte listeners y las reproduce al terminar la hidratación correspondiente. Evita que un click temprano parezca perdido.

¿Qué diferencia full e incremental hydration?Ver respuesta

Full hydration activa la aplicación completa. Incremental hydration conserva límites @defer deshidratados y los activa por triggers como viewport o interaction, reduciendo JavaScript inicial a cambio de más estados y decisiones de carga.

¿Por qué evitarías cambiar el árbol con isPlatformBrowser?Ver respuesta

La condición puede hacer que servidor y cliente creen nodos distintos durante la hidratación. Mantengo la misma estructura y ejecuto sólo la integración browser después del render, o excluyo de hidratación un caso aislado como último recurso.

¿Cómo te sentís con este tema?

22

Testing y estrategia de calidad

Una suite Senior protege comportamiento y contratos. Evitá tests que copian la implementación.

Teoría

Fundamentos
  • Un test útil prepara estado, ejecuta una acción observable y comprueba el resultado. Vitest aporta runner, assertions, spies y fake timers; TestBed agrega el entorno de inyección, compilación y render de Angular.
  • Los component harnesses encapsulan la forma de operar un componente y permiten que los tests usen una API estable. RouterTestingHarness navega rutas reales dentro del test y verifica guards, params, resolvers y componentes activados.
  • Pirámide práctica: muchas pruebas de lógica, componentes para comportamiento DOM, integración en fronteras y pocos E2E de journeys críticos.
Mecanismo y aplicación
  • Angular moderno documenta Vitest junto con TestBed. Bases existentes pueden usar Jasmine/Jest; la estrategia importa más que la sintaxis.
  • Un test de componente interactúa con el DOM mediante roles, labels y eventos, y comprueba el resultado visible. Los métodos privados y la estructura interna son detalles de implementación; afirmar sobre ellos vuelve frágil el test ante refactors sin cambio de comportamiento.
  • HttpTestingController intercepta requests de HttpClient y permite afirmar método, URL, body y headers antes de responder con éxito o error. verify() comprueba al final que ninguna petición haya quedado pendiente.
Decisiones y límites
  • RouterTestingHarness simplifica navegación. Los component harnesses crean APIs de prueba estables para UI reutilizable.
  • Fake timers controlan el reloj de debounce, retry y delays sin esperar tiempo real. Los marble tests representan emisiones RxJS sobre una línea temporal virtual y sirven cuando el orden y la concurrencia forman parte del contrato.
  • Un mock reemplaza una frontera y permite aislar la unidad, pero demasiados mocks pueden describir una integración que ningún proveedor real soporta. Los contract tests verifican que DTOs, adapters y clientes respeten el mismo contrato.

Ejemplo

it('shows the resolved user', async () => {
  const harness = await RouterTestingHarness.create('/users/7');
  const request = http.expectOne('/api/users/7');
  request.flush({ id: 7, name: 'Ada' });
  await harness.fixture.whenStable();
  expect(harness.routeNativeElement?.textContent).toContain('Ada');
});

Fuentes del tema

Preguntas y respuestas

¿Qué test escribirías primero?Ver respuesta

El riesgo más caro: regla de dominio, permiso, pago, migración o interacción que ya falló. La cobertura porcentual no reemplaza esa priorización.

¿Unit test de un componente con servicio?Ver respuesta

Sustituyo la frontera del servicio, ejecuto la interacción por el DOM y verifico el resultado visible y la llamada relevante. No pruebo Angular.

¿Qué probás en una unidad y qué dejás para integración?Ver respuesta

Una unidad cubre reglas puras y estados con pocas fronteras. Un test de integración comprueba template, DI, router o HTTP cuando su composición forma parte del comportamiento. Elijo el nivel más bajo que todavía puede detectar el fallo real.

¿Cómo eliminás un test asíncrono flaky?Ver respuesta

Controlo reloj, scheduler y respuestas externas. Espero una condición observable en lugar de usar delays arbitrarios, cierro requests pendientes y elimino estado compartido entre casos.

¿Qué aporta Vitest y qué aporta TestBed?Ver respuesta

Vitest ejecuta suites, assertions, spies y timers. TestBed crea el entorno Angular de providers, componentes y change detection. Un servicio puro puede no necesitar TestBed; un componente con DI y template sí suele beneficiarse.

¿Cuándo usarías un component harness?Ver respuesta

Cuando varias pruebas o consumidores necesitan operar un componente complejo sin depender de su DOM interno. El harness ofrece acciones y consultas estables y reduce roturas por cambios de markup.

¿Cómo probás debounce y retry?Ver respuesta

Uso fake timers o el scheduler virtual, avanzo el reloj de forma explícita y compruebo emisiones, cancelaciones y número de intentos. El test no espera tiempo real ni depende de la velocidad de la máquina.

¿Qué debe verificar un test de HttpClient?Ver respuesta

Método, URL, params, headers y body que forman parte del contrato; luego responde con éxito o error y comprueba el resultado visible. verify() asegura que no quedaron requests sin resolver.

¿Cómo te sentís con este tema?

23

Seguridad web en Angular

Angular escapa y sanitiza varios bindings, pero el equipo todavía controla autenticación, autorización, dependencias y datos peligrosos.

Teoría

Fundamentos
  • Interpolación y property binding tratan valores como datos. [innerHTML] pasa por sanitización; URLs de recursos y bypass APIs requieren revisión estricta.
  • DomSanitizer.bypassSecurityTrust* no limpia contenido: crea un valor que omite la sanitización de Angular. Su uso concentra una decisión de confianza y necesita una fuente controlada, revisión y auditoría.
  • Content Security Policy limita los orígenes y tipos de recursos que puede ejecutar el navegador. Trusted Types obliga a que sinks DOM peligrosos reciban valores creados por políticas registradas. Juntas reducen el impacto de una inyección que llega al DOM.
Mecanismo y aplicación
  • CSRF aprovecha credenciales que el navegador adjunta de forma automática, como cookies. SameSite, un token XSRF y la validación del servidor prueban que la petición salió de la aplicación esperada. Un bearer token evita ese mecanismo, pero puede ser robado por XSS según dónde se almacene.
  • Un guard decide navegación en el cliente y mejora la experiencia, pero el usuario puede omitirlo o llamar la API de forma directa. La API debe comprobar permisos y ownership para cada operación.
Decisiones y límites
  • El bundle frontend y sus variables de entorno llegan al navegador y cualquier usuario puede inspeccionarlos. Claves privadas, credenciales de servicio y secretos pertenecen al servidor o a un gestor de secretos.
  • Las versiones soportadas de Angular reciben correcciones; el lockfile fija el grafo instalado. Una auditoría de supply chain revisa vulnerabilidades, paquetes abandonados, scripts de instalación y cambios inesperados de mantenedor.

Preguntas y respuestas

¿Angular evita XSS?Ver respuesta

Reduce XSS al escapar y sanitizar contextos conocidos. DOM APIs directas, bypass, librerías y HTML externo reabren el riesgo.

¿LocalStorage o cookies para tokens?Ver respuesta

Depende del modelo de amenaza. Cookies HttpOnly reducen lectura por XSS y exigen CSRF controls. LocalStorage simplifica headers pero expone el token a JavaScript comprometido.

¿Qué implica usar bypassSecurityTrustHtml?Ver respuesta

La llamada no sanitiza el contenido. Declara que la aplicación confía en esa fuente y evita la protección de Angular para ese sink. La restrinjo a una frontera revisada y prefiero transformar datos antes de producir HTML.

¿Por qué un route guard no autoriza una operación?Ver respuesta

El usuario controla el cliente y puede omitir la navegación o llamar la API de forma directa. El guard mejora la experiencia. El servidor verifica identidad, permisos y ownership en cada operación.

¿Cómo mostrarías HTML proporcionado por usuarios?Ver respuesta

Lo sanitizo con una política y librería adecuada en el servidor o una frontera auditada, conservo CSP y evito bypassSecurityTrustHtml. Si el producto admite un subconjunto, permito sólo tags y atributos explícitos.

¿Dónde guardarías un token de sesión?Ver respuesta

Depende del modelo de amenazas. Una cookie HttpOnly reduce robo directo por XSS y requiere protección CSRF; memoria evita persistencia pero se pierde al recargar. No presento localStorage como opción segura por defecto para credenciales de larga vida.

¿Cómo te sentís con este tema?

24

Accesibilidad, HTML y CSS

La accesibilidad forma parte del contrato de UI. Un Senior la integra en componentes y Definition of Done.

Teoría

Fundamentos
  • HTML semántico aporta nombre, rol y comportamiento nativo. button ejecuta acciones, a con href navega, los headings forman el índice, label nombra controles y los landmarks permiten saltar entre regiones.
  • La navegación por teclado necesita un orden de foco que siga la lectura y un indicador visible. Un modal mueve el foco a su interior, impide escapar al contenido de fondo, anuncia su nombre y devuelve el foco al elemento que lo abrió.
Mecanismo y aplicación
  • ARIA añade nombre, rol, estado o relaciones cuando HTML nativo no alcanza. No incorpora por sí sola teclado ni comportamiento; un div role=button todavía necesita foco y activación con Enter y Space.
  • Los errores asociados mediante aria-describedby se leen junto al control. Una live region anuncia cambios asíncronos que no reciben foco, como el resultado de una operación o una validación remota.
Decisiones y límites
  • CSS: cascade, specificity, stacking contexts, box model, Flexbox, Grid, container/media queries y responsive images.
  • Zoom, texto largo y localización cambian las dimensiones del contenido; contraste y high contrast cambian su percepción; reduced motion limita animaciones. Un componente flexible conserva lectura, foco y controles sin depender de alturas fijas.

Fuentes del tema

Preguntas y respuestas

¿Div con click o button?Ver respuesta

Button aporta teclado, foco, rol y activación sin recrear comportamiento. Un div exige implementar y mantener todo eso.

¿Cómo probás accesibilidad?Ver respuesta

Combino lint y axe con teclado real, lector de pantalla en flujos críticos y revisión de foco, contraste y nombres accesibles.

¿Cuándo ARIA empeora un componente?Ver respuesta

ARIA puede contradecir la semántica nativa o anunciar un estado que el comportamiento no implementa. Empiezo por el elemento HTML correcto y agrego nombre, estado o relaciones sólo cuando falta información.

¿Cómo manejás el foco de un modal?Ver respuesta

Muevo el foco a un punto útil dentro del diálogo, mantengo la navegación en su contenido, cierro con Escape cuando corresponde y devuelvo el foco al disparador. El diálogo también necesita nombre y fondo inerte.

¿Cómo probarías un modal accesible?Ver respuesta

Lo abro sólo con teclado, compruebo nombre accesible, foco inicial, ciclo de Tab, Escape y retorno del foco. Después valido el fondo inerte y escucho el flujo con VoiceOver o NVDA.

¿Cuándo usarías una live region?Ver respuesta

Para anunciar un cambio asíncrono relevante que no recibe foco, como un resultado guardado o un error remoto. Evito anunciar cada pulsación o cambio visual porque interrumpe y satura al lector de pantalla.

¿Cómo te sentís con este tema?

25

Build, CI/CD, configuración y upgrades

El frontend llega a producción mediante una cadena que también necesita diseño y ownership.

Teoría

Fundamentos
  • La configuración de build contiene valores públicos que pueden quedar embebidos en los bundles. Los secretos permanecen fuera del frontend. Validar la configuración al arrancar detecta URLs o flags faltantes y evita que cada entorno interprete defaults distintos.
  • Un pipeline de CI ejecuta typecheck, lint, unit tests, build con budgets y recorridos críticos antes de publicar. Una caché usa el lockfile y la configuración como parte de su clave para no reutilizar dependencias o resultados incompatibles.
Mecanismo y aplicación
  • Los assets con hash pueden usar caché larga porque una modificación cambia su URL. El HTML conserva una política corta para descubrir el release nuevo. Un rollback necesita artefactos anteriores y compatibilidad temporal entre el frontend nuevo y la versión previa de la API.
  • Un feature flag separa despliegue de exposición. Owner, métricas y fecha de retiro controlan su ciclo de vida; un flag permanente mantiene dos caminos de código y duplica combinaciones de prueba.
Decisiones y límites
  • ng update y los schematics transforman configuración y código para una versión nueva. Actualizar una major por vez reduce combinaciones no soportadas; las deprecations, el bundle y las métricas runtime muestran qué trabajo queda después de compilar.
  • Los source maps relacionan el bundle minificado con el TypeScript original. En producción requieren acceso restringido porque revelan estructura y código; asociarlos con release, commit y evento permite reconstruir el stack correcto.

Preguntas y respuestas

¿Cómo desplegás sin romper usuarios con pestañas abiertas?Ver respuesta

Mantengo compatibilidad temporal de API, manejo chunk-load errors, uso assets versionados y evito borrar archivos previos antes de que expire su caché.

¿Qué mirás después de un upgrade?Ver respuesta

Errores, tests, bundle, Web Vitals, warnings, cambios de browser support y dependencias pares. Después retiro compatibilidad obsoleta.

¿Cómo diseñás un feature flag seguro?Ver respuesta

Defino owner, audiencia, fallback, métricas y fecha de retiro. El backend mantiene las reglas de autorización. Los dos caminos permanecen probados mientras el flag exista y retiro el código cuando termina el rollout.

¿Publicarías source maps en producción?Ver respuesta

Los genero para relacionar errores minificados con el TypeScript, pero restrinjo su acceso al sistema de observabilidad. Asocio cada mapa con release y commit para simbolizar el stack correcto.

¿Cómo diseñás un rollback de frontend?Ver respuesta

Conservo artefactos inmutables por release, mantengo compatibilidad temporal con la API y puedo volver a apuntar el hosting al build anterior. Base de datos y contratos nuevos necesitan una estrategia forward-compatible para que el bundle viejo siga funcionando.

¿Qué presupuesto pondrías en CI?Ver respuesta

Límites de bundle inicial y chunks críticos, typecheck, tests y métricas del recorrido principal. Un presupuesto debe fallar cerca de la causa y tener owner; una cifra ignorada en cada pipeline no protege rendimiento.

¿Cómo te sentís con este tema?

26

Observabilidad, errores y debugging

Un Senior diseña cómo detectar y explicar fallos antes de que aparezca el incidente.

Teoría

Fundamentos
  • La frontera global captura errores que ninguna feature manejó. El registro conserva tipo, causa y contexto técnico sin exponer stack traces, tokens ni datos personales en la interfaz.
  • Release, ruta, acción, correlation ID, usuario anonimizado y breadcrumbs permiten reconstruir una falla. El mismo correlation ID propagado por gateway y backend conecta el error del navegador con logs y traces del servidor.
Mecanismo y aplicación
  • La tasa de errores indica frecuencia, la latencia por endpoint localiza esperas, Web Vitals describe experiencia de render e interacción y el éxito de journeys mide tareas completas. Un log sin una pregunta operativa ni una acción asociada añade volumen sin diagnóstico.
  • Angular DevTools muestra árbol, DI y profiling. Chrome Performance, Network, Memory y Coverage completan el diagnóstico.
Decisiones y límites
  • Un leak se vuelve visible al repetir navegación y comparar heap snapshots. Detached DOM nodes, listeners, timers y caches sin límite muestran qué referencia mantiene viva una vista que Angular ya destruyó.
  • Un error boundary de feature contiene el fallo y ofrece una salida: retry, fallback, estado parcial o contacto de soporte. Un toast genérico desaparece y no conserva la operación que el usuario necesita recuperar.

Preguntas y respuestas

¿Cómo investigás un bug que no reproducís?Ver respuesta

Aumento contexto observable, comparo versión, navegador y ruta de datos, y creo una hipótesis verificable. Evito cambios especulativos sin señal.

¿Qué reportarías en un error HTTP?Ver respuesta

Endpoint normalizado, status, duración, correlation ID y operación. Redacto o elimino body, tokens y datos personales.

¿Cómo usás un correlation ID desde el frontend?Ver respuesta

Propago un identificador permitido en requests y lo registro junto con ruta, release y acción. Backend y gateway conservan el mismo valor para unir el fallo visible con logs y traces sin guardar datos personales.

¿Cómo confirmás un memory leak de navegación?Ver respuesta

Repito el recorrido, fuerzo garbage collection en un entorno de diagnóstico y comparo heap snapshots. Busco componentes retenidos, detached DOM nodes, listeners, timers y caches que conservan referencias.

¿Cómo distinguís un error del frontend de uno de API?Ver respuesta

Relaciono el evento del navegador con request, status, correlation ID y trace del backend. Si la API respondió bien, reviso parsing y render; si falló, el mismo identificador permite seguir la operación por gateway y servicio.

¿Qué datos evitarías enviar a telemetría?Ver respuesta

Tokens, passwords, bodies sensibles, datos personales sin necesidad y HTML completo. Defino una allowlist, anonimizo identificadores y aplico sampling y retención según el propósito operativo.

¿Cómo te sentís con este tema?

Bloque 05

Criterio Senior

System design, liderazgo y conversaciones de entrevista.

27

System design frontend

En una entrevista de diseño, empezá por requisitos y recorré datos, límites, fallos, rendimiento y operación.

Teoría

Fundamentos
  • Los usuarios, flujos críticos, SEO, offline, tiempo real, volumen, permisos, localización y objetivos de rendimiento forman las restricciones del diseño. Cada restricción modifica las fronteras, la estrategia de datos o el modo de rendering.
  • Un diagrama frontend ubica features, router, estado, API layer, componentes compartidos y fronteras de dominio. La propiedad de cada dato determina quién puede escribirlo, quién lo deriva y cuánto tiempo debe vivir.
Mecanismo y aplicación
  • Una estrategia de caché define key, TTL e invalidación. La consistencia establece cuándo aceptar datos stale, cómo reconciliar optimistic updates, qué hacer ante conflictos y cómo mantener cursores o páginas al cambiar la colección.
  • WebSocket ofrece comunicación bidireccional persistente, SSE envía un stream unidireccional sobre HTTP y polling repite requests. La solución necesita reconexión, orden, deduplicación y backpressure para no procesar eventos más rápido de lo que la UI puede consumirlos.
Decisiones y límites
  • Un diseño completo incluye autorización, accesibilidad, telemetría, niveles de prueba, estrategia de despliegue y migración. Esas fronteras determinan si el sistema puede operarse y evolucionar después del primer release.
  • La primera versión cubre la escala y los riesgos conocidos con el menor número de piezas. Umbrales observables, como latencia, volumen o frecuencia de incidentes, indican cuándo una estrategia deja de servir y justifican el siguiente cambio.

Preguntas y respuestas

Diseñá un dashboard con datos en vivoVer respuesta

Agrupo widgets por frecuencia y ownership, uso un servicio de conexión con multiplexing, normalizo eventos, aplico backpressure y renderizo con signals. Pauso streams invisibles y mido INP.

Diseñá una librería de componentesVer respuesta

Defino tokens de diseño, accesibilidad y APIs pequeñas; publico harnesses, documentación y semver. Pruebo keyboard, themes, SSR y breaking changes.

¿Cómo elegís entre WebSocket, SSE y polling?Ver respuesta

WebSocket sirve para comunicación bidireccional, SSE para un stream servidor a cliente sobre HTTP y polling para cambios poco frecuentes o infraestructura simple. Comparo reconexión, proxies, orden, volumen y soporte del backend.

¿Qué debe definir una estrategia de caché?Ver respuesta

Define key, TTL, invalidación, deduplicación y comportamiento stale. También explica cómo reconciliar optimistic updates, conflictos y cambios de paginación sin mezclar datos de usuarios o filtros distintos.

¿Cómo diseñarías datos en tiempo real sin saturar la UI?Ver respuesta

Defino frecuencia útil por widget, agrupo eventos, deduplico por versión y aplico backpressure. Pauso consumidores fuera del viewport y separo el ritmo de recepción del ritmo de render.

¿Qué incluirías en una propuesta de system design además del diagrama?Ver respuesta

Contratos de datos, ownership, estrategia de caché, errores, seguridad, accesibilidad, métricas y rollout. También dejo umbrales que indiquen cuándo la primera solución necesita otra arquitectura.

¿Cómo te sentís con este tema?

28

Liderazgo técnico y trabajo en equipo

El nivel Senior incluye decisiones compartidas, mentoring, manejo de incidentes y entrega predecible.

Teoría

Fundamentos
  • Un code review evalúa corrección, seguridad, diseño y tests. Un comentario bloqueante describe un defecto que impide integrar; una sugerencia propone una mejora opcional. Explicar el motivo permite que el autor aplique el criterio en código futuro.
  • Una decisión técnica documentada contiene contexto, alternativas y consecuencias. La fecha de revisión evita tratar como permanente una elección tomada bajo restricciones que pueden cambiar.
Mecanismo y aplicación
  • Mentoring hace visible el modelo mental, aumenta la dificultad de forma gradual y devuelve la decisión a quien aprende. Resolver cada problema por la otra persona concentra conocimiento y convierte al mentor en cuello de botella.
  • Durante un incidente, el equipo primero estabiliza el servicio, comunica impacto, asigna roles y conserva evidencia. El postmortem reconstruye causas y cambia código, alertas o proceso sin buscar culpables.
Decisiones y límites
  • La negociación de alcance compara riesgo, dependencias, costo de demora y una entrega incremental. Exponer incertidumbre permite reservar tiempo, instrumentar el resultado o reducir el alcance antes de comprometer una fecha.
  • Lead time, defectos, costo de mantenimiento, adopción y carga cognitiva describen salud técnica desde resultados. Líneas de código y cantidad de tickets premian volumen aunque el sistema sea más complejo o menos estable.

Preguntas y respuestas

¿Cómo resolvés un desacuerdo técnico?Ver respuesta

Alineo restricciones, comparo opciones con criterios, hago un spike si falta evidencia y documento la decisión. Después apoyo la opción acordada.

¿Cómo manejaste feedback negativo?Ver respuesta

Describí el caso de modularización de formularios: escuchaste, revisaste estándares, refactorizaste por responsabilidad, pediste otra revisión y aplicaste el aprendizaje.

¿Qué convierte un comentario de review en bloqueante?Ver respuesta

Bloqueo por corrección, seguridad, pérdida de datos, contrato roto o una deuda que impide operar el cambio. Marco preferencias como sugerencias y explico el riesgo para que el autor pueda aplicar el criterio.

¿Qué incluís en un ADR?Ver respuesta

Registro contexto, restricciones, opciones consideradas, decisión y consecuencias. Añado owner y fecha de revisión cuando las condiciones pueden cambiar. El documento permite discutir la elección sin depender de memoria oral.

¿Cómo resolvés un desacuerdo de arquitectura?Ver respuesta

Acordamos objetivo y restricciones, escribimos alternativas con el mismo criterio y ejecutamos un spike si la incertidumbre lo requiere. La decisión queda registrada con consecuencias y fecha de revisión.

¿Cómo elevás la calidad sin convertirte en cuello de botella?Ver respuesta

Automatizo reglas repetibles, documento ejemplos y distribuyo ownership. En reviews explico el criterio y permito que otras personas tomen decisiones con límites claros.

¿Cómo te sentís con este tema?

29

Cómo razonar y responder como Senior

Esta sección convierte conocimiento técnico en respuestas claras. La meta es demostrar qué ocurre, qué decisión tomarías, por qué la tomarías y cómo comprobarías que funcionó.

Teoría

Fundamentos
  • Respondé primero qué es el concepto en una frase. Después explicá el mecanismo que produce su comportamiento, elegí una aplicación concreta y cerrá con el límite de esa elección. Ejemplo: switchMap reemplaza la suscripción interna anterior; lo elegiría en un buscador porque sólo interesa la consulta más reciente, pero no para guardar acciones que deben completarse todas.
  • Separá mecanismo de decisión. «OnPush reduce comprobaciones» describe un efecto. «Uso OnPush con estado inmutable porque los cambios llegan por inputs y signals» explica una decisión. La segunda respuesta permite evaluar si entendés cuándo la herramienta encaja.
  • Nombrá las restricciones que cambian la solución: volumen de datos, frecuencia de actualización, SEO, latencia, accesibilidad, seguridad, soporte de navegadores y capacidad del equipo. Si la pregunta no las informa, declaralas como supuestos en vez de inventar un escenario silenciosamente.
Mecanismo y aplicación
  • Compará alternativas con el mismo criterio. Para cada opción indicá beneficio, costo y modo de falla. Por ejemplo, SSR mejora el HTML inicial y el SEO, pero agrega infraestructura y exige código compatible con servidor; CSR simplifica la operación, pero depende más de JavaScript para el primer contenido.
  • Explicá cómo validarías la decisión. Rendimiento se comprueba con métricas como LCP, INP, tamaño de bundle o tiempo de tarea; una migración se valida con tests, telemetría, despliegue gradual y rollback; una mejora de equipo se valida con lead time, defectos o carga operativa.
  • Una respuesta débil enumera herramientas: «usaría Signals, OnPush y lazy loading». Una respuesta sólida conecta problema y evidencia: «el perfil mostró demasiadas vistas comprobadas; moví el estado local a Signals, mantuve referencias inmutables y medí menos scripting sin cambiar el comportamiento».
Decisiones y límites
  • Si no recordás una API exacta, no inventes. Explicá el modelo que sí conocés, aislá el detalle dudoso y decí cómo lo verificarías en la documentación o con una prueba mínima. El razonamiento correcto es más valioso que una firma memorizada incorrectamente.
  • Para una experiencia real usá Contexto, Decisión, Acción y Resultado. El resultado debe incluir una señal verificable: latencia, errores, conversión, tiempo de entrega, incidentes evitados o feedback del equipo. Si no hubo medición, decí qué observaste y qué medirías hoy.

Preguntas y respuestas

¿Qué diferencia una respuesta Senior?Ver respuesta

No es la cantidad de APIs nombradas. Es poder explicar el mecanismo, elegir según restricciones, comparar alternativas y proponer una forma de validar el resultado. Por ejemplo, no basta con decir «uso switchMap»: hay que explicar que conserva sólo la operación interna más reciente y por qué esa política coincide con el problema.

¿Qué hacés si no sabés una API exacta?Ver respuesta

Decí qué parte conocés, razoná desde el modelo de Angular y explicá cómo verificarías el detalle. Inventar una firma daña más que reconocer un borde.

¿Cómo evitás responder «depende» sin tomar una posición?Ver respuesta

Nombrá dos o tres condiciones decisivas, fijá un escenario razonable y elegí. Por ejemplo: «si la página necesita SEO y contenido inicial rápido, elegiría SSR; si es una herramienta interna autenticada, empezaría con CSR». Después explicá qué dato haría cambiar la decisión.

¿Cómo convertís una opinión en una decisión técnica defendible?Ver respuesta

Definí el objetivo, compará alternativas con los mismos criterios y acordá una señal de éxito. «Prefiero Signals» es una opinión; «uso Signals para estado local síncrono porque simplifica derivaciones y verifico el impacto con legibilidad, tests y profiling» es una decisión discutible y medible.

¿Cómo estructurás una respuesta técnica extensa?Ver respuesta

Empiezo con una definición de una frase, explico el mecanismo y tomo una decisión para un escenario concreto. Cierro con el costo, la alternativa y cómo comprobaría el resultado. Si la pregunta es amplia, aviso esa estructura para que el entrevistador pueda profundizar donde le interese.

¿Qué hacés cuando la pregunta no incluye suficiente contexto?Ver respuesta

Pido las restricciones que realmente cambian la respuesta: volumen, frecuencia de cambio, SEO, latencia, consistencia, seguridad y capacidad del equipo. Si no están disponibles, declaro un supuesto, elijo bajo ese escenario y digo qué dato me haría cambiar de opción.

¿Cómo te sentís con este tema?

30

Preparación personal y respuestas conductuales

Tu experiencia ofrece material sólido. Convertí cada proyecto en evidencia medible y ajustá la introducción al rol.

Teoría

Fundamentos
  • Un pitch de 60 a 90 segundos conecta especialidad, años de experiencia, dominios, dos logros y motivación para el rol. Recorrer cada empleo del CV consume tiempo sin mostrar el criterio que une la trayectoria.
  • STAR: situación y tarea breves; acción centrada en tus decisiones; resultado con métrica, aprendizaje o reducción de riesgo.
Mecanismo y aplicación
  • Un banco conductual cubre conflicto, error, feedback, liderazgo, deadlines, incertidumbre, incidentes, rendimiento y arquitectura. Cada historia puede responder varias preguntas si identifica con precisión la decisión y el resultado.
  • El caso de formularios dinámicos demuestra arquitectura, Redux o NgRx, escalabilidad y coordinación. Cantidad de formularios, tiempo de entrega y defectos antes y después convierten la historia en evidencia medible.
Decisiones y límites
  • La experiencia desde Angular 2 permite comparar cambios del framework a través del tiempo. Una adopción acertada muestra beneficio y migración; una API rechazada muestra restricciones y costo que superaban ese beneficio.
  • Las preguntas al entrevistador revelan arquitectura, prácticas de calidad, organización del equipo, roadmap, manejo de incidentes, autonomía y criterio de éxito. Las respuestas permiten evaluar el alcance real del rol.

Preguntas y respuestas

Contame sobre vosVer respuesta

Soy Frontend Developer especializado en Angular, con experiencia desde Angular 2 y equipos distribuidos. He diseñado formularios dinámicos a escala y productos de datos. Busco un rol donde pueda combinar arquitectura, entrega y mentoring.

¿Por qué querés cambiar?Ver respuesta

Enfocá crecimiento, alcance técnico y tipo de producto. Evitá hablar mal del equipo actual o usar una respuesta genérica.

¿Cómo evitás que una respuesta STAR se vuelva demasiado larga?Ver respuesta

Resumo situación y tarea en pocas frases. Dedico la mayor parte a mis decisiones, alternativas y coordinación. Cierro con un resultado medible y el aprendizaje que cambió mi trabajo posterior.

¿Cómo contás un error sin debilitar tu perfil?Ver respuesta

Elijo un error real, explico la decisión que lo produjo y asumo mi parte. Describo cómo limité el impacto, qué señal agregué y qué cambio de código o proceso evitó repetirlo.

¿Cómo respondés sobre un conflicto técnico?Ver respuesta

Describo la restricción, la posición de cada parte y cómo llevé la discusión a evidencia. Explico la decisión final, mi contribución y qué cambió en el producto o en la forma de trabajar.

¿Cómo hablás de un proyecto sin métricas históricas?Ver respuesta

Uso señales verificables como incidentes, tiempo de entrega, defectos o feedback, y aclaro qué no se midió. Cierro con la métrica que instrumentaría hoy en lugar de inventar un número.

¿Cómo te sentís con este tema?

Bloque 06

Banco rápido

Definiciones breves para responder con precisión antes de ampliar con mecanismo, caso y trade-off.

¿Tipos primitivos?Respuesta

undefined, null, boolean, number, bigint, string y symbol.

¿typeof null?Respuesta

Devuelve object por compatibilidad histórica; verificá null de forma explícita.

¿NaN === NaN?Respuesta

False. Usá Number.isNaN u Object.is.

¿null y undefined?Respuesta

Null suele expresar ausencia intencional; undefined expresa falta de valor o propiedad.

¿Truthy y falsy?Respuesta

La conversión booleana determina branches; objetos y arrays vacíos son truthy.

¿Temporal Dead Zone?Respuesta

Es el tramo entre la entrada al bloque y la inicialización de un binding let, const o class. El binding ya pertenece al scope, pero leerlo lanza ReferenceError; por ejemplo, console.log(total); let total = 1;.

¿Hoisting?Respuesta

El entorno registra declaraciones antes de ejecutar; la disponibilidad depende del tipo de declaración.

¿this?Respuesta

Receiver de una llamada según call-site, salvo arrow que captura el binding exterior.

¿call, apply, bind?Respuesta

Call invoca con argumentos; apply con array-like; bind crea otra función con receiver o argumentos fijados.

¿Coerción?Respuesta

Conversión entre tipos. Puede ser explícita con Number, String o Boolean, o implícita cuando un operador o contexto necesita otro tipo.

¿Closure?Respuesta

Una función conserva los bindings del entorno léxico donde fue creada, incluso si se ejecuta después de que terminó la función exterior. Conserva bindings vivos, no una copia congelada de sus valores.

¿Spread y rest?Respuesta

Misma sintaxis: spread expande; rest reúne valores restantes.

¿Destructuring default?Respuesta

Se aplica ante undefined, no ante null.

¿Shallow copy?Respuesta

Crea un contenedor nuevo y conserva las mismas referencias anidadas. Con const copy = { ...original }, copy !== original, pero copy.user === original.user si user es un objeto.

¿structuredClone?Respuesta

Clona estructuras soportadas y ciclos; no clona funciones.

¿Prototipo?Respuesta

Objeto delegado que JavaScript consulta cuando una propiedad falta en el receiver.

¿Own property?Respuesta

Propiedad definida en el objeto, comprobable con Object.hasOwn.

¿for...in o for...of?Respuesta

In recorre claves enumerables; of recorre valores de un iterable.

¿Métodos de array mutables?Respuesta

Push, pop, shift, unshift, splice, sort, reverse, fill y copyWithin.

¿find o filter?Respuesta

Find devuelve el primer match; filter crea un array con todos.

¿Pure function?Respuesta

Mismo resultado para mismas entradas y sin efectos observables.

¿Currying?Respuesta

Convierte una función de varios argumentos en una secuencia de funciones.

¿Debounce o throttle?Respuesta

Debounce espera silencio; throttle limita ejecuciones por intervalo.

¿Promise.all?Respuesta

Conserva orden y rechaza al primer rechazo observado.

¿allSettled?Respuesta

Espera todos y devuelve el estado de cada operación.

¿AbortController?Respuesta

Emite una señal de cancelación que consumen fetch y otras APIs.

¿Async bloquea el thread?Respuesta

No. Await cede la continuación; CPU síncrono sigue bloqueando.

¿Unhandled rejection?Respuesta

Promise rechazada sin handler; registrala y corregí la cadena, no la ocultes.

¿DOM?Respuesta

Árbol de nodos y APIs que representan el documento.

¿BOM?Respuesta

APIs del navegador fuera del documento, como history, location y navigator.

¿Event bubbling?Respuesta

El evento asciende desde el target por ancestros que participan.

¿Event delegation?Respuesta

Listener en un ancestro que decide según el target; reduce listeners y cubre hijos dinámicos.

¿preventDefault?Respuesta

Evita la acción predeterminada si el evento es cancelable.

¿localStorage?Respuesta

Almacenamiento síncrono string por origin y persistente.

¿IndexedDB?Respuesta

Base asíncrona del navegador para datos estructurados y mayor volumen.

¿Same-origin?Respuesta

Coincidencia de scheme, host y port.

¿Preflight?Respuesta

Request OPTIONS con la que el navegador consulta permiso CORS.

¿ETag?Respuesta

Validador de representación para revalidación condicional.

¿Service Worker?Respuesta

Worker con lifecycle que intercepta red y habilita offline/push.

¿Web Worker?Respuesta

Thread para JavaScript sin acceso directo al DOM.

¿Etiqueta semántica?Respuesta

Elemento cuyo nombre comunica rol y estructura al navegador y tecnologías asistivas.

¿head?Respuesta

Metadata y recursos del documento, no contenido principal visible.

¿alt?Respuesta

Alternativa textual que depende de la función de la imagen; decorativas usan alt vacío.

¿iframe sandbox?Respuesta

Restringe capacidades del documento embebido y se abre con tokens explícitos.

¿GET o POST en form?Respuesta

GET expresa consulta y deja datos en URL; POST envía body para una operación.

¿Submit default?Respuesta

Un button dentro de form usa submit si no declarás type.

¿defer o async script?Respuesta

Defer preserva orden y espera parseo; async ejecuta al descargar.

¿Box model?Respuesta

Content, padding, border y margin.

¿Specificity?Respuesta

Peso de un selector dentro de la cascada después de origen, importancia y layer.

¿box-sizing:border-box?Respuesta

El width declarado incluye padding y border.

¿Margin o padding?Respuesta

Margin separa cajas; padding agrega espacio dentro del borde.

¿Position absolute?Respuesta

Sale del flujo y se posiciona respecto de su containing block.

¿Position sticky?Respuesta

Participa en flujo y se fija dentro de su scroll container al cruzar un umbral.

¿Stacking context?Respuesta

Ámbito que limita la comparación de z-index entre descendientes.

¿Pseudo-clase o pseudo-elemento?Respuesta

Pseudo-clase selecciona estado; pseudo-elemento representa una parte generada o conceptual.

¿BEM?Respuesta

Convención Block, Element, Modifier para nombres de clases.

¿Preprocesador o framework?Respuesta

Preprocesador extiende sintaxis; framework aporta reglas, utilidades o componentes.

¿Media o container query?Respuesta

Media consulta viewport/dispositivo; container consulta tamaño o estilo del contenedor.

¿Reflow?Respuesta

Recalculo de geometría provocado por cambios o lecturas que requieren layout.

¿CLS?Respuesta

Movimiento inesperado de contenido; reservá espacio para imágenes y contenido asíncrono.

¿Componente o directiva?Respuesta

El componente posee vista; la directiva agrega comportamiento a un host.

¿Pipe pura?Respuesta

Angular puede reutilizar el resultado mientras no cambien las referencias de entrada.

¿@for track?Respuesta

Asocia identidad de datos con nodos DOM para minimizar creación y conservar estado.

¿computed o effect?Respuesta

computed deriva estado; effect sincroniza con una API externa.

¿Signal o BehaviorSubject?Respuesta

Signal para estado síncrono de UI; BehaviorSubject cuando necesitás semántica y operadores RxJS.

¿switchMap?Respuesta

Cancela el inner anterior al llegar una nueva emisión.

¿concatMap?Respuesta

Encola inner observables y conserva orden.

¿exhaustMap?Respuesta

Ignora nuevos disparos mientras el inner sigue activo.

¿mergeMap?Respuesta

Ejecuta inner observables en paralelo con concurrencia configurable.

¿forkJoin?Respuesta

Emite una vez cuando todos completan; falla si alguno falla y no sirve para streams infinitos.

¿Cold observable?Respuesta

Cada subscription crea su propio productor.

¿shareReplay?Respuesta

Comparte y reproduce valores; necesita política de refCount, error e invalidación.

¿providedIn: root?Respuesta

Provider tree-shakeable en el root EnvironmentInjector.

¿providers local?Respuesta

Nueva instancia en el ElementInjector del componente y sus descendientes visibles.

¿viewProviders?Respuesta

Oculta esos providers al contenido proyectado.

¿InjectionToken?Respuesta

Token runtime tipado para valores, funciones o interfaces.

¿OnPush?Respuesta

Permite saltar subárboles hasta que una notificación relevante marca la vista.

¿Zoneless?Respuesta

Angular recibe notificaciones explícitas y evita usar ZoneJS para inferir cambios.

¿markForCheck?Respuesta

Marca la vista para una próxima verificación.

¿detectChanges?Respuesta

Ejecuta verificación local; su uso frecuente suele indicar un flujo defectuoso.

¿Standalone?Respuesta

Componente que declara dependencias en imports y no necesita declaración en NgModule.

¿Lazy route?Respuesta

Carga código al navegar a la feature, reduciendo el bundle inicial.

¿Guard?Respuesta

Control de navegación en cliente; no reemplaza autorización del servidor.

¿Resolver?Respuesta

Obtiene datos antes de activar la ruta.

¿Reactive Form?Respuesta

Modelo explícito y observable en TypeScript, apto para composición y validación compleja.

¿CVA?Respuesta

Contrato que conecta un control custom con Angular Forms.

¿Async validator?Respuesta

Validador que completa con errores o null; controlá cancelación y frecuencia.

¿Interceptor?Respuesta

Middleware de requests y responses para preocupaciones transversales.

¿Retry?Respuesta

Solo con política, límite y seguridad de idempotencia.

¿XSS?Respuesta

Ejecución de script no confiable; evitá sinks peligrosos y mantené sanitización y CSP.

¿CSRF?Respuesta

Petición autenticada inducida desde otro origen; afecta sobre todo credenciales automáticas como cookies.

¿CSP?Respuesta

Política del navegador que limita fuentes de scripts, estilos y otros recursos.

¿Trusted Types?Respuesta

Restringe asignaciones a sinks DOM peligrosos a valores creados por políticas confiables.

¿SSR?Respuesta

Render por request en servidor; ayuda SEO y HTML inicial, agrega costo operativo.

¿SSG?Respuesta

HTML generado en build para contenido estable.

¿Hydration?Respuesta

Angular reutiliza HTML de servidor y conecta comportamiento cliente.

¿@defer?Respuesta

Divide dependencias y carga una vista según trigger o condición.

¿LCP?Respuesta

Tiempo hasta renderizar el mayor elemento visible.

¿INP?Respuesta

Latencia observada de interacciones durante la sesión.

¿CLS?Respuesta

Suma de cambios inesperados de layout.

¿Tree shaking?Respuesta

El bundler elimina código no alcanzable cuando el formato y las dependencias lo permiten.

¿AOT?Respuesta

Compila templates en build, reduce trabajo runtime y detecta errores antes.

¿NgRx reducer?Respuesta

Función pura que calcula nuevo estado desde estado y action.

¿NgRx effect?Respuesta

Reacciona a eventos y coordina I/O u otros efectos.

¿Selector?Respuesta

Consulta derivada y memorizada sobre el store.

¿Optimistic update?Respuesta

Actualiza UI antes de confirmar y define rollback o reconciliación.

¿Facade?Respuesta

API estable que reduce superficie de un subsistema; puede ocultar demasiado si no protege un límite.

¿Adapter?Respuesta

Traduce un contrato externo al modelo interno.

¿Strategy?Respuesta

Encapsula políticas intercambiables detrás de un contrato.

¿SRP?Respuesta

Una unidad concentra responsabilidades que cambian por el mismo motivo.

¿DIP?Respuesta

El código de alto nivel depende de abstracciones, no de detalles concretos.

¿unknown?Respuesta

Tipo seguro para valor no validado; obliga a estrechar antes de usar.

¿never?Respuesta

Representa estados imposibles y permite checks exhaustivos.

¿Microtask?Respuesta

Cola de promesas que se drena antes de la siguiente macrotask.

¿Closure?Respuesta

Función junto con su entorno léxico: puede seguir leyendo o modificando los bindings capturados cuando se ejecuta fuera de la llamada que los creó.

¿Inmutabilidad?Respuesta

Crear nuevas referencias en lugar de mutar estado compartido; mejora previsibilidad y detección.

¿Object.freeze?Respuesta

Congelación superficial; no protege objetos anidados sin trabajo adicional.

¿Unit test?Respuesta

Prueba una unidad con fronteras controladas y feedback rápido.

¿Integration test?Respuesta

Verifica colaboración entre varias unidades o una frontera real.

¿E2E?Respuesta

Prueba un recorrido del usuario a través del sistema desplegado o equivalente.

¿Harness?Respuesta

API estable para interactuar con un componente en tests sin depender de su DOM interno.

¿Memory leak típico?Respuesta

Subscription, listener, timer, observer o cache que conserva una vista destruida.

¿Correlation ID?Respuesta

Identificador que conecta eventos frontend, gateway y backend de una operación.

¿Feature flag?Respuesta

Control temporal de exposición con owner, métricas y plan de retiro.

¿Micro-frontend?Respuesta

Unidad de frontend con ownership y despliegue independiente, a cambio de integración y duplicación.

¿ADR?Respuesta

Registro corto de una decisión, alternativas y consecuencias.

Bloque 07

Casos prácticos

  1. 01

    Buscador cancelable

    Construí un buscador con debounce, cancelación, estados loading/error/empty, caché por query y tests con tiempo controlado. Explicá por qué elegiste switchMap y qué cambia si el endpoint no soporta cancelación.

  2. 02

    Motor de formularios dinámicos

    Diseñá un schema para tipos, validación, layout, visibilidad y permisos. Sumá un CVA, validación asíncrona, persistencia parcial y una estrategia de versionado del schema.

  3. 03

    Dashboard en tiempo real

    Diseñá seis widgets con frecuencias distintas. Incluí WebSocket o SSE, reconexión, backpressure, pausa fuera del viewport, caché, permisos y métricas de INP.

  4. 04

    Migración entre cinco versiones mayores

    Proponé etapas para actualizar majors, convertir features a standalone, introducir control flow, Signals y zoneless. Definí pruebas, métricas, feature flags y rollback.

  5. 05

    Lista de 100.000 filas

    Compará paginación server-side, virtual scroll, filtros remotos y caché. Medí memoria, scripting, layout e interacción sin perder navegación por teclado ni soporte de lector de pantalla.

  6. 06

    Carrera de refresh de autenticación

    Varias requests reciben 401 al mismo tiempo. Diseñá un refresh único, cola, cancelación, logout seguro, telemetría y tests deterministas de concurrencia.

  7. 07

    Event loop

    Predecí el orden de logs que mezclen Promises, queueMicrotask, timers, async/await y eventos. Verificá el resultado en navegador y justificá cada transición entre colas.

  8. 08

    Tabla accesible

    Construí una tabla ordenable y paginada con caption, headers, estados de orden, teclado, foco, loading y empty state. Validala con lector de pantalla.

  9. 09

    Layout responsive sin CLS

    Implementá una card que cambie con container queries, respete reduced motion y no produzca saltos. Explicá cascade, stacking contexts, overflow y containment.

  10. 10

    Caché offline

    Diseñá caché HTTP, IndexedDB y Service Worker para una pantalla de lectura. Definí invalidación, conflictos, cuotas, logout y tratamiento de datos sensibles.