Kotlin Lab

Aprender / Simulacros de entrevista técnica oral

Simulacros de entrevista técnica oral

Cinco simulacros con guion de una entrevista técnica oral con dos seniors, de unos 20 minutos cada uno. Están pensados para que los preguntas encadenadas, como pasa en una entrevista real: cada pregunta nace de la respuesta anterior.

Cómo usarlos

Con Claude (recomendado): escribe "simulacro 2" en el chat. Claude hace de los dos seniors, pregunta de una en una, repregunta según lo que digas y al final te da feedback con esta misma rúbrica. No leas antes el simulacro que vas a hacer.

Solo: tápate la columna derecha, lee la pregunta, respóndela en voz alta con el móvil grabando y después compara con "qué quieren oír". Sé honesto con las señales de alarma.

Orden: el 1 primero, porque todo lo demás se apoya en él. Después, en cualquier orden. Repite un simulacro cuando hayan pasado al menos dos días.

Rúbrica (la misma para todos)

Puntúa cada pregunta de 0 a 2:

  • 0: no sabías, o has dicho una señal de alarma.

  • 1: idea correcta, pero vaga, sin vocabulario preciso o sin aguantar la repregunta.

  • 2: correcta, con los términos exactos, y has aguantado la repregunta o has razonado en voz alta.

Con 9 o más sobre 12 en un simulacro, ese bloque está listo. Por debajo de 6, vuelve al documento que se indica en ese simulacro.

Reglas que se aplican a todas las respuestas

  1. Razona en voz alta. "No lo tengo claro; necesitaría algo que… así que probablemente…" vale más que el silencio o "no sé".

  2. No cambies una respuesta buena porque te repreguntan. Muchas veces solo comprueban si sabes defenderla.

  3. Usa los términos exactos: "se cancela", no "se pierde"; "se destruye y se recrea", no "se reinicia".

  4. Nombra un trade-off siempre que puedas: "lo haría así porque…; el coste es…".

  5. Conecta con Kontor cuando venga a cuento, incluso para decir qué cambiarías.


Simulacro 1 — Plataforma y ciclo de vida

Base: docs/ANDROID_BASICS.md entero y el nivel 8 del lab.

#PreguntaQué quieren oírRepregunta probableSeñal de alarma
1"¿Qué pasa con una pantalla de Compose cuando el usuario gira el móvil?"Es un cambio de configuración: la Activity se destruye y se recrea. El ViewModel sobrevive y remember no; rememberSaveable sí."¿Y si declaro configChanges en el manifest?" → Evita recrear al girar, pero no protege de process death."No pasa nada, se repinta" (eso es Flutter).
2"El usuario rellena medio formulario, se va a otra app y el sistema mata el proceso. ¿Qué ve al volver?"La misma pantalla, recreada, pero vacía: el ViewModel es nuevo. Solución: SavedStateHandle / rememberSaveable."¿Qué guardarías ahí y qué no?" → IDs y lo que escribió; no listas de la API (límite del Bundle y datos caducados)."Vuelve a la home" o "hago que no se pueda cerrar".
3"¿Cuándo se destruye un ViewModel?"Cuando su pantalla sale para siempre de la pila (atrás), con finish() o con process death. Se llama a onCleared()."¿Y al navegar a otra pantalla encima?" → Sigue vivo, su entrada está en la pila."Al girar".
4"Lanzas el envío de una transferencia en viewModelScope y el usuario pulsa atrás. ¿Qué pasa?"El scope se cancela y la coroutine también. La petición pudo llegar al servidor: no sabes si se hizo. Para dinero, un scope de aplicación o WorkManager, más idempotency key."¿Cuándo generas la key?" → Al entrar en revisión; se guarda en SavedStateHandle; se reutiliza al reintentar."La petición llega y no hay problema" o "si la repite, está en su derecho".
5"¿Por qué no deberías guardar un Context en un ViewModel?"El ViewModel vive más que la Activity: retendría una Activity destruida → memory leak. Si hace falta, applicationContext en la capa de datos."¿Cómo lo detectarías?" → LeakCanary.Confundirlo con el BuildContext de Flutter.
6"¿Qué es un ANR y cómo lo evitas?"Main thread bloqueado unos 5 s; el sistema ofrece cerrar la app. I/O y CPU fuera de Main con withContext; StrictMode en debug; Android Vitals en producción."¿Una función suspend puede causarlo?" → Sí, si hace trabajo bloqueante sin cambiar de dispatcher."Con suspend ya no bloquea".

Simulacro 2 — Coroutines y Flow

Base: docs/ANDROID_FLUENCY.md §4, preguntas 18–30 de docs/ASSESSMENT.md y los niveles 6 y 7.

#PreguntaQué quieren oírRepregunta probableSeñal de alarma
1"¿Qué es structured concurrency?"Toda coroutine vive en un scope con ciclo de vida; cancelar el scope cancela las hijas; los errores se propagan al padre. Por eso no GlobalScope."¿Job o SupervisorJob?" → Con SupervisorJob, el fallo de una hija no cancela a las hermanas; viewModelScope lo usa."Es para que vaya más rápido".
2"Cargas cuenta y movimientos en paralelo. ¿Cómo?"coroutineScope { val a = async { … }; val b = async { … }; a.await() to b.await() }. Si uno falla, se cancela el otro."¿Y si quieres que uno pueda fallar sin cancelar el otro?" → supervisorScope y tratar el error de cada async.Dos launch y variables compartidas.
3"¿Qué problema tiene runCatching en una función suspend?"Atrapa CancellationException: la cancelación se convierte en Result.failure y el código sigue ejecutándose. Hay que relanzarla."¿Y capturando Exception?" → Tampoco: CancellationException es una Exception.No saber que la cancelación se implementa con una excepción.
4"¿StateFlow, SharedFlow o Channel para mostrar un snackbar de 'Transferencia enviada'?"StateFlow no, porque conflaciona. Channel + receiveAsFlow() entrega una vez y no se pierde al girar. Google recomienda modelarlo como estado y consumirlo."¿Por qué no SharedFlow?" → Sin colector (durante el giro) el evento se pierde si no hay replay."Un StateFlow<Boolean> showSnackbar" sin pensar en cómo se resetea.
5"Explícame stateIn(viewModelScope, WhileSubscribed(5_000), initial)."Convierte un Flow frío en StateFlow; el upstream sigue 5 s tras el último colector, así que un giro no reinicia la carga, pero en background se para."¿Por qué no Eagerly?" → Mantendría consultas y sockets vivos en background."Es un delay de 5 segundos".
6"Diseña el buscador de movimientos: cada letra lanza una búsqueda."query.debounce(300).distinctUntilChanged().flatMapLatest { repo.search(it) }. flatMapLatest cancela la búsqueda anterior. Test con runTest + Turbine y tiempo virtual."¿Por qué flatMapLatest y no flatMapMerge?" → Merge ejecutaría todas y los resultados viejos podrían llegar después que los nuevos.Un delay dentro de un launch por cada letra.

Simulacro 3 — Compose

Base: docs/COMPOSE.md, docs/ANDROID_FLUENCY.md §5 y preguntas 34–41 de docs/ASSESSMENT.md.

#PreguntaQué quieren oírRepregunta probableSeñal de alarma
1"¿Qué es la recomposición?"Compose vuelve a ejecutar los composables cuyos parámetros o estado leído cambiaron, y se salta los que no. No reconstruye un árbol entero."¿Cuándo se salta un composable?" → Si sus parámetros son estables y no cambiaron."Es como el build de Flutter" sin matizar el skip.
2"Una lista de 500 movimientos va a tirones al hacer scroll. ¿Qué miras?"LazyColumn con key; cálculos fuera de la composición (ordenar en el ViewModel); estabilidad de los parámetros (List es inestable → ImmutableList o strong skipping); Layout Inspector con contador de recomposiciones; probar en release."¿Por qué en release?" → Debug no tiene R8 ni Baseline Profiles y Compose va más lento: no es representativo."Pongo un Column con verticalScroll".
3"¿Qué es state hoisting y por qué?"El composable recibe value y onValueChange; el estado vive arriba (ViewModel o pantalla). Composables sin estado: reutilizables, previsualizables y testeables."¿Todo el estado va al ViewModel?" → No: lo efímero de UI (un diálogo, una animación, el scroll) se queda en remember."El ViewModel se pasa a todos los hijos".
4"¿LaunchedEffect o rememberCoroutineScope?"LaunchedEffect(key) para efectos dirigidos por el estado, al entrar o al cambiar la key. rememberCoroutineScope para lanzar desde callbacks del usuario (un onClick)."¿Qué pasa con LaunchedEffect(Unit) al girar?" → Se vuelve a ejecutar, porque la composición se recrea.Lanzar una coroutine directamente en el cuerpo del composable.
5"¿Cómo haces accesible una fila de movimiento?"Modifier.semantics(mergeDescendants = true) y una descripción con concepto, importe y fecha; tamaño táctil mínimo de 48 dp; contraste; probarlo con TalkBack."¿Y si el importe está oculto?" → La descripción también lo oculta: "importe oculto"."Pongo contentDescription a todo".
6"¿Cómo testeas la pantalla de transferencia?"Pantalla sin estado (TransferScreen(state, callbacks)) probada con createComposeRule; se construye el estado a mano, sin Hilt ni red; onNodeWithText, performClick, assertIsDisplayed. El ViewModel, aparte, en JVM."¿Y screenshot tests?" → Paparazzi o Roborazzi para detectar regresiones visuales.Testear la pantalla con el ViewModel real y el backend.

Simulacro 4 — Arquitectura, datos y entrega

Base: docs/ANDROID_FLUENCY.md §2 y §6–§7, preguntas 42–52 de docs/ASSESSMENT.md y docs/MOBILE_DESIGN.md §5.

#PreguntaQué quieren oírRepregunta probableSeñal de alarma
1"Cuéntame la arquitectura de Kontor en 90 segundos."Módulos (:app, :core:*, :feature:*), MVVM + UDF, Room como fuente de verdad, repositorio que oculta Retrofit, Hilt, y qué le falta para producción (docs/KONTOR_REVIEW.md)."¿Por qué multi-módulo en una app tan pequeña?" → Para practicar límites y build incremental; reconocer que en un MVP real empezaría más simple.No saber explicar tu propio proyecto.
2"¿Usarías UseCases?"Cuando la lógica se reutiliza o combina varios repositorios. Si solo reenvían, sobran; la guía oficial los considera opcionales."¿Dónde viven y de qué dependen?" → En domain, de interfaces de repositorio, sin Android."Siempre, uno por pantalla".
3"La app debe funcionar en el metro sin conexión. ¿Cómo?"La UI observa Room; la red solo actualiza Room; se muestra la caché con la hora de actualización. Las escrituras de dinero no se aceptan offline como definitivas."¿Qué haces con una cancelación hecha sin conexión?" → Se marca pendiente, se avisa de que no está garantizada y WorkManager la envía al volver la red."Cacheo la respuesta en memoria".
4"¿Fakes o mocks para testear el ViewModel?"Fakes por defecto: un repositorio falso con MutableStateFlow; los tests prueban comportamiento y aguantan refactors. Mocks para verificar interacciones concretas."¿Cómo controlas Dispatchers.Main en tests?" → MainDispatcherRule con un TestDispatcher."Mockeo todo con Mockito" sin saber por qué.
5"Una feature crashea solo en release. ¿Por dónde empiezas?"R8: reflexión sin -keep (serialización, Retrofit); stack trace con mapping.txt; reproducir con un build minificado en local."¿Cómo evitas que vuelva a pasar?" → Tests contra el build minificado en CI y reglas -keep o @Keep donde haya reflexión.No conocer R8 ni la ofuscación.
6"Mañana sale una release con un cambio en pagos. ¿Cómo la publicas?"Feature flag, track interno, staged rollout (5 → 20 → 100 %), vigilar crash rate, ANR y errores de negocio; parar el rollout si algo va mal."¿Parar el rollout deshace la actualización?" → No; por eso el flag es la palanca rápida."La subo y miro las reviews".

Simulacro 5 — Diseño oral

Base: docs/MOBILE_DESIGN.md. Se responde con el guion de 8 pasos; las repreguntas salen de lo que digas.

  1. "Diseña el flujo de enviar una transferencia SEPA." (caso 1)

  2. "Pagos programados que se puedan consultar sin conexión." (caso 2)

  3. "Descarga de certificados fiscales en PDF." (caso 3)

  4. Nuevo, sin respuesta modelo: "Una pantalla que muestra el estado de un pago SEPA Instant en tiempo real: enviado, procesando, abonado o rechazado. ¿Cómo lo harías?"

    Pistas para evaluarte después: ¿preguntaste cuánto tarda y qué pasa si el usuario cierra la app? ¿Comparaste polling, WebSocket/SSE y push? ¿Pensaste en qué ve el usuario si pierde la conexión a mitad? ¿Mencionaste batería y ciclo de vida (dejar de escuchar en background)?

Rúbrica del diseño: 2 puntos por cada paso del guion cubierto con criterio (máximo 16). Lo más importante: ¿preguntaste antes de diseñar? y ¿cuánto tiempo dedicaste a errores y bordes?


La parte no técnica

La entrevista también tendrá preguntas de conversación ("¿por qué Android?", "cuéntame un bug difícil", "¿qué nos preguntarías tú?"). Todo eso está preparado en la guía de Kontor: ../kontor/docs/GUIA_ENTREVISTA_DKB_COFA_ES.md, capítulos 15 (pitch, historias STAR y cómo manejar gaps) y 17 (preguntas para ellos).

Si llevas Kontor, prepárate también para que lo revisen: docs/KONTOR_REVIEW.md tiene lo que señalaría un senior y cómo responder.

Una respuesta que conviene llevar muy ensayada, porque une lo técnico con lo personal, es la pregunta 53 de docs/ASSESSMENT.md: Flutter frente a KMP y por qué te pasas a nativo.

En el primer simulacro, la respuesta "con IA da igual mantener dos códigos y yo elegiría Flutter por gusto" fue la más peligrosa de la entrevista. Ante una empresa que ha elegido nativo, decir que preferirías otra tecnología les da el motivo para descartarte. Usa la versión de la pregunta 53.

Fuente: docs/MOCK_INTERVIEWS.md