{
 "subtemas": [],
 "tarjetas": [
  {
   "sub": "WebFlux y reactivo",
   "q": "WebFlux vs MVC: cuál elegir con datos en la mano",
   "a": "MVC (bloqueante, 1 request = 1 hilo): óptimo para CRUD, JPA, ecosistema maduro. Con 200 hilos sostiene cientos de req/s.\nWebFlux (no bloqueante, pocos event-loop threads): gana con MUCHA concurrencia + I/O (streaming, proxies, backpressure), no con más CPU.\nRegla: si tu stack es JPA/JDBC (bloqueante), WebFlux NO ayuda — jdbc bloquea el event loop. R2DBC para reactivo real.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "WebFlux y reactivo",
   "q": "Mono y Flux: semántica exacta",
   "a": "Mono<T>: 0 o 1 elemento (o vacío/error) — la versión reactiva de Optional/Future.\nFlux<T>: 0..N elementos con señal de completado y backpressure.\nNADA se ejecuta sin SUBSCRIBIRSE: flux.map(...) devuelve otro flujo frío; el trabajo arranca en subscribe/return al framework.\nOperaciones declarativas: map/flatMap/filter/concatWith/merge.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "WebFlux y reactivo",
   "q": "flatMap vs concatMap y threading en WebFlux",
   "a": "flatMap: ejecuta en PARALELO (intercala elementos según llegan) — para llamadas independientes.\nconcatMap: SECUENCIAL, respeta orden.\npublishOn/subscribeOn cambian de scheduler (boundedElastic para código bloqueante: blockHound detecta violaciones).\nNunca bloquear el event loop: JDBC, sleep, CompletableFuture.get → boundedElastic.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "WebFlux y reactivo",
   "q": "Backpressure en una frase + estrategias",
   "a": "El consumidor avisa cuántos elementos puede procesar (request(n)) para que el productor no lo ahogue.\nEstrategias: buffer (acumular con límite), drop/latest (descartar), onError (abortar), limitRate en pipelines.\nLa cola con límite es backpressure aplicado a mensajes.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "WebFlux y reactivo",
   "q": "R2DBC: JPA pero reactivo — qué cambia",
   "a": "spring-boot-starter-data-r2dbc + DatabaseClient/repositorios reactivos (ReactiveCrudRepository).\nSQL no bloqueante real (driver por BD), transacciones con ReactiveTransactionManager.\nSin lazy loading (JOIN FETCH no existe): proyecciones/queries explícitas.\nMenos conveniente que JPA; usar solo en pipelines reactivos.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "WebFlux y reactivo",
   "q": "SSE vs WebSocket vs Long-polling",
   "a": "SSE (text/event-stream): unidireccional servidor→cliente, reconexión automática, HTTP puro — notificaciones, dashboards.\nWebSocket: bidireccional full-duplex (STOMP sobre WS en Spring) — chat, edición colaborativa.\nLong-polling: legado/fallback.\nEscalar WS/SSE: sesiones pegajosas o pub/sub compartido (Redis) entre réplicas.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "Maven lifecycle: clean, compile, test, package, verify, install, deploy",
   "a": "Fases en orden: compile < test < package (jar/war) < verify (integration tests, jacoco check) < install (al repo local) < deploy (a repo remoto).\nEjecutar 'mvn package' corre TODAS las fases anteriores.\nPlugins se enganchan a fases (surefire→test, boot plugin→repackage crea el jar ejecutable).\n-mvn clean borra target/.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "scope de dependencias en Maven",
   "a": "compile (default): todo. provided: compilación sí, runtime NO lo empaca (servlet-api, Lombok). runtime: solo runtime. test: solo tests (JUnit, Mockito). import: solo en dependencyManagement (importar BOMs).\nTransitivo: compile fluye; test/provided no.\nConflicto de versiones: gana el más CERCANO al root (nearest wins) — dependency:tree para diagnosticar.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "dependencyManagement vs dependencies (y BOMs)",
   "a": "dependencyManagement: declara VERSIONES sin incluir la dependencia (los hijos heredan versión).\nBOM (bill of materials): pom solo de versiones importado con scope=import — spring-boot-dependencies alinea 300 librerías.\nResultado: cambiar versión en un solo lugar; sin versiones duplicadas ni 'inferno de dependencias'.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "Exclusions y conflictos: el flujo de diagnóstico",
   "a": "mvn dependency:tree -Dincludes=com.google.guava:guava → quién trae qué versión.\nExcluir en la dependencia culpable: <exclusions><exclusion>...</exclusion></exclusions>, y declarar la versión deseada explícita (o en dependencyManagement).\nSíntomas típicos: NoSuchMethodError/ClassNotFoundError en runtime por versiones mezcladas.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "Maven vs Gradle: diferencias prácticas",
   "a": "Maven: XML declarativo, convención sólida, el más extendido en empresas; build ligeramente más lento.\nGradle: Kotlin/Groovy DSL, build incremental y cacheado (más rápido), flexible (a veces demasiado).\nAmbos con mvnw/gradlew wrapper para versionar el build tool. Elije el estándar del equipo.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Ecosistema y build",
   "q": "Lombok: anotaciones y las 2 trampas",
   "a": "@Getter/@Setter, @RequiredArgsConstructor (inyección de dependencias final — la base del estilo constructor), @Builder, @Data (¡incluye equals/hashCode/toString con TODOS los campos — peligro en entidades JPA!), @Slf4j, @Value (inmutable), @EqualsAndHashCode(onlyExplicitlyIncluded).\nTrampas: IDE sin plugin no compila; @Data en entidades rompe equals/hashCode con lazy/collections.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Puedes inyectar en un campo static? ¿Y por qué no?",
   "a": "NO. Spring inyecta en instancias de beans; los static pertenecen a la clase, y el contenedor no los toca (además fomenta estado global no testeable).\nSoluciones: ponerlo en un bean normal e inyectar, o pattern holder inicializado por el contexto (ApplicationContextHolder) solo como último recurso legado.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Dos beans del mismo tipo pero uno con @Profile: es ambiguo?",
   "a": "No, si los perfiles son excluyentes: solo un bean EXISTE por perfil activo → sin ambigüedad en runtime.\nPero en tests sin perfil activo ambos pueden no existir o existir según configuración.\nPatrón: impl por entorno (FakeNotificador dev / EmailNotificador prod) — ejemplo perfecto de @Profile.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cuándo se crea un bean @Lazy si nadie lo usa? ¿Y si otro bean lo inyecta?",
   "a": "Nunca se crea si nadie lo pide.\nCon @Lazy en la inyección: el CONSUMIDOR recibe un proxy; el bean real se crea al PRIMER método llamado.\nSin @Lazy pero con singleton eager (default): se crea al arrancar aunque nadie lo use.\n@Lazy en @Configuration(proxyBeanMethods) es otra cosa: proxies de métodos @Bean.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Un bean prototype con @PostConstruct corre por cada instancia?",
   "a": "Sí: el ciclo completo (inyección, @PostConstruct) corre por CADA creación.\nPero @PreDestroy NO se llama: Spring no rastrea prototipos tras entregarlos — cleanup manual por el cliente (destroy callback o try-with-resources).\nPor eso prototipos con recursos necesitan destroyMethod o gestión explícita.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Qué pasa si dos @Bean devuelven el MISMO tipo y un tercero lo inyecta por constructor?",
   "a": "NoUniqueBeanDefinitionException al arrancar (o al primer uso si lazy).\nResolve: @Primary en uno, @Qualifier en el punto de inyección, o nombre de parámetro = nombre del bean (por nombre).\nEn @ConfigurationProperties-style: clases wrapper distintas para cada configuración.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿getBean() dentro de un service es buena idea?",
   "a": "Casi nunca: service locator pattern oculta dependencias (constructor no muestra lo que usa), imposible de mockear limpio, y ancla al contexto.\nExcepciones aceptables: código legacy no-manageado, resolución dinámica de plugins (con ObjectProvider mejor), herramientas.\nPreferir: inyección explícita siempre.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿@Transactional en la clase Y en un método: qué gana? ¿Y anidado en un bean interno?",
   "a": "Método SOBREESCRIBE el de la clase (regla de resolución: el más específico).\nMétodos SIN anotación propia heredan la de la clase.\nBeans internos (nueva clase anidada como @Component) SÍ son beans con su propio proxy → transacción funciona.\nCuidado con clases de configuración anidadas no anotadas: son configuración, no beans.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Diferencia entre @ComponentScan con lazy-init global y @Lazy?",
   "a": "spring.main.lazy-initialization=true: TODOS los beans se crean perezosamente — arranque rapidísimo, pero errores de config aparecen tarde y primer request más lento.\n@Lazy: quirúrgico por bean.\nEn prod se prefiere eager (fail-fast); lazy global útil para arranques en dev o lambdas con cold start.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo inyectarías un valor que cambia en runtime (refresh)?",
   "a": "@ConfigurationProperties + @RefreshScope + actuator/refresh (Cloud Config) — o re-lectura del Environment.\nCon Kubernetes: spring-cloud-kubernetes reloada ConfigMaps (polling/eventos).\nAnti-patrón: @Value cacheado sin refresh (nunca cambia).\nPara config MUY dinámica: feature flags service (LaunchDarkly/Unleash) o DB-backed properties con caché corta.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cuál es la diferencia entre filtros de servlet y @WebFilter vs FilterRegistrationBean?",
   "a": "@WebFilter: anotación servlet nativa — requiere @ServletComponentScan y da poco control.\nFilterRegistrationBean (recomendado en Boot): bean que registra el Filter con URL patterns, ORDEN explícito y activación condicional.\nOrden importa: auth debe correr antes que logging de negocio; security filter chain corre antes de los tuyos si así lo ordenas.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo arrancar código justo después de levantar el contexto?",
   "a": "CommandLineRunner (args crudos) / ApplicationRunner (ApplicationArguments parseados) — beans ejecutados al final del arranque.\n@EventListener(ApplicationReadyEvent.class): equivalente desacoplado.\nSmartLifecycle para procesos con start/stop (consumers).\n@PostConstruct NO: corre durante la creación del bean (contexto aún incompleto).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo harías un feature flag en Spring sin librería externa?",
   "a": "@ConfigurationProperties(prefix=\"features\") record Features(boolean nuevoCheckout, ...) + consumidor inyecta Features.\nToggle por properties/env → redeploy o refresh.\nCon @ConditionalOnProperty para beans enteros (activar/desactivar implementaciones).\nNivel siguiente: flags runtime (Unleash/LaunchDarkly) sin redeploy, con % de usuarios.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Qué es un 'fat jar' de Boot y por qué importa en contenedores?",
   "a": "Jar único con TODAS las dependencias (BOOT-INF/lib) + loader propio (java -jar directo).\nContra: rebuild = reenviar 80MB aunque solo cambió tu código → layered extraction resuelve (capas por cambio de dependencias).\nAlternativa: war en Tomcat externo (legado).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Diferencia entre @RequestParam, @RequestPart y @ModelAttribute para forms?",
   "a": "@RequestParam: campo individual del form/query.\n@ModelAttribute: objeto completo binded por setters (form-encode).\n@RequestPart: parte de un MULTIPART con converter propio (JSON + archivo juntos).\nMultipart simple: @RequestParam MultipartFile alcanza.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo proteger contra N+1 sin notarlo en dev?",
   "a": "Tests de conteo: datasource-proxy o Hibernate statistics (assert selectCount == esperado).\nhibernate.generate_statistics + log WARN de hibernate (session-metrics).\nEn prod: métricas por query, trace con spans por SELECT (Micrometer + JDBC observer).\nCode review: toda relación navegada en loop = sospechoso.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Qué mide y qué no spring.jpa.open-in-view?",
   "a": "OSIV (default TRUE): mantiene la sesión de Hibernate abierta durante TODO el request → los controllers pueden tocar relaciones lazy 'gratis'.\nProblemas: conexiones del pool retenidas durante serialización (menos throughput), queries sorpresa en la vista, N+1 invisible.\nProd recomendado: false + DTOs/JOIN FETCH explícitos. Boot lo avisa al arrancar.",
   "nivel": "avanzado",
   "clave": true
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo probarías que un @Scheduled corre con la frecuencia correcta?",
   "a": "No esperes tiempos reales: extraer la lógica a un método y testearlo directo.\nPara el wiring: Awaitility con ventana corta, o SchedulerTask de test con fixedDelay mínimo y count.\nEn cluster: verificar ShedLock (una sola ejecución con 2 instancias en test de integración).\nMétricas de ejecución en prod (contador + timer).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "Si debes garantizar que un email se envía EXACTAMENTE una vez tras crear un pedido, ¿qué haces?",
   "a": "Exactamente una vez es un mito distribuido → lo asegurable: AL MENOS una vez + idempotencia.\nDiseño: outbox (evento PedidoCreado en tabla, misma tx) → worker publica → consumidor envía email guardando dedup por eventId (si ya existe, skip).\nReintentos con backoff + dead letter para fallos persistentes.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo migrarías una propiedad de @Value a @ConfigurationProperties sin romper nada?",
   "a": "1) Crear el POJO con el campo igual (mismo nombre relaxed binding).\n2) Registrar (@Component o @EnableConfigurationProperties).\n3) Migrar consumidores uno a uno (compilación guía).\n4) Tests de binding con ApplicationContextRunner.\n5) Deprecar la clave vieja con placeholder de compatibilidad temporal: @Value(\"${vieja:${nueva:default}}\").",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cuándo un @Bean de @Configuration se llama 'lite mode' y qué pierde?",
   "a": "@Bean en una clase SIN @Configuration (en @Component por ejemplo) = lite mode: los métodos NO pasan por proxy CGLIB → llamar un @Bean desde otro NO reutiliza el singleton (crea instancia nueva cada vez).\nCon @Configuration completo: intercepta llamadas internas a métodos @Bean y devuelve el singleton.\nPierde inter-bean references — bug sutil y clásico.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo expondrías 2 versiones de la misma API sin duplicar todo?",
   "a": "Núcleo único (service) + controllers por versión que mapean DTOs distintos: /v1 y /v2 comparten la lógica; transformadores versionados.\nSolo duplicar lo que ROMPIÓ compatibilidad.\nDeprecación: header Sunset + métricas de uso por versión para saber cuándo retirar v1.\nNunca: fork del service por versión.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Qué pasa si una ExceptionHandler lanza otra excepción?",
   "a": "Spring cae al handler de último recurso (ErrorAttributes/BasicErrorController) → 500 genérico; la excepción original se loguea por el stack.\nEl catch-all Exception.class del advice DEBE ser infalible: construir respuesta simple sin lógica que pueda fallar (y loguear AMBAS excepciones).\nNunca confiar en datos del request ahí (pueden ser la causa).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo validar límites de rate por usuario SIN librerías?",
   "a": "Filtro/interceptor + Redis INCR con EXPIRE (ventana fija) o script Lua para sliding window: clave por userId+minuto.\n429 con Retry-After; headers X-RateLimit-Limit/Remaining informativos.\nDistribuido porque Redis comparte entre réplicas.\nProducción seria: Bucket4j + Redis o el rate limiter del gateway.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "Un archivo de 500MB rompe el upload: config correcta",
   "a": "spring.servlet.multipart.max-file-size=600MB y max-request-size mayor; gateway/ingress también tiene su límite (client_max_body_size en nginx, timeout de LB).\nStreaming: guardar el InputStream directo a disco/S3 (nunca byte[] completo).\nAlternativa para archivos grandes: upload directo a Cloud Storage con signed URL — tu API solo firma y registra.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Diferencia entre @Scheduled y un Queue worker (Cloud Tasks)?",
   "a": "@Scheduled: periódico fijo, en-proceso, no distribuido sin lock, jobs 'batch'.\nQueue worker (Tasks/PubSub): por-evento, con retry persistido, dead-letter, visibilidad del fallo, escala horizontal natural.\nRegla: cron interno para mantenimiento simple; colas para trabajo que NO puede perderse.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Trampas de entrevista",
   "q": "¿Cómo loguear el SQL real con parámetros en prod sin reventar performance?",
   "a": "Nunca show-sql en prod. Opciones: datasource-proxy/psql wrapper con log SLOW queries (>200ms) + parámetros en DEBUG activado dinámicamente (actuator loggers level cambian SIN restart).\nPG: pg_stat_statements / slow query log del motor.\nTracing: span por query con el statement (sampling) — equilibrando PII.",
   "nivel": "intermedio",
   "clave": false
  }
 ]
}