Kotlin Lab

Aprender / Interview English — las ⭐ en inglés

Interview English — las ⭐ en inglés

La vacante pide inglés fluido y en CoFa se trabaja con equipos en Alemania. Aunque la entrevista sea en español, pueden cambiar de idioma en cualquier momento para comprobar tu nivel. Aquí tienes las 20 preguntas ⭐ de docs/ASSESSMENT.md respondidas como se dicen en voz alta: frases cortas, sin jerga de documento.

Cómo practicarlo: lee la pregunta, responde en inglés sin mirar, grábate y compara. No memorices palabra por palabra; memoriza las palabras clave en negrita.


1. Frases para la entrevista oral

SituaciónFrase
Ganar tiempo"Good question — let me think about that for a second."
Estructurar"There are two parts to this. First… Second…"
No lo sabes"I haven't used that in production, but my understanding is… / I'd approach it like this…"
Razonar en voz alta"I'm torn between A and B. I'd lean towards A, because…"
Pedir aclaración"Just to make sure I understand: do you mean… or…?"
Asumir algo"I'll assume X. If that's not the case, this part would change."
Trade-off"The trade-off here is… The cost is… If the requirement changed, I'd…"
Defender tu respuesta"I'd still go with X, because… but I see your point about Y."
Corregirte"Actually, let me correct myself — …"
Hablar de Kontor"In my demo project, Kontor, I did X. In production I'd change it to Y, because…"
Cerrar una respuesta"So, in short: …"

2. Las 20 ⭐

5. lateinit or by lazy?

"lateinit is for a non-null var that I initialise later — for example field injection with Hilt, or a test's @Before. by lazy is for a val computed on first access, and it's thread-safe by default. lateinit doesn't work with primitive types."

6. value class or typealias?

"A value class creates a new type at compile time with no runtime cost in most cases, so I can't pass an AccountId where a UserId is expected. A typealias is just another name — Cents and Long are interchangeable, so it gives no type safety."

17. How do you make Kotlin code nice to call from Java?

"@JvmStatic for companion members, @JvmOverloads to generate overloads for default parameters, @JvmField to expose fields and @Throws for checked exceptions. And I'd annotate nullability at the boundary, because Java gives Kotlin platform types."

19. What is structured concurrency? Why avoid GlobalScope?

"Every coroutine lives in a scope with a lifecycle — like viewModelScope. If the scope is cancelled, all its children are cancelled, and failures propagate to the parent. GlobalScope has no owner, so nothing cancels it: you get leaks and orphaned work."

23. What's wrong with runCatching inside a coroutine?

"It catches every Throwable, including CancellationException. So a cancellation becomes a Result.failure and the code after it keeps running. You need to rethrow CancellationException — for example with a small runSuspendCatching helper."

26. StateFlow, SharedFlow or Channel for one-off events?

"StateFlow is for state: it always has a value and it conflates, so events can be lost or not re-fired. A Channel with receiveAsFlow() delivers each event once, and it buffers it if nobody is collecting — for example during a rotation. Google also recommends modelling events as state that the UI consumes and then clears."

27. What does stateIn do, and why WhileSubscribed(5_000)?

"It turns a cold Flow into a hot StateFlow with an initial value. With WhileSubscribed(5_000) the upstream stays alive for five seconds after the last collector leaves. A rotation takes less than that, so it doesn't restart the work — but if the app goes to the background, it stops."

31. What happens to your state on rotation? And on process death?

"On rotation, the Activity is destroyed and recreated — it's a configuration change. The ViewModel survives, remember doesn't. On process death, the system kills the app in the background; when the user comes back, Android recreates the same screen, but the ViewModel is new and empty. Only rememberSaveable, SavedStateHandle and whatever is on disk survive."

32. Why not keep an Activity Context in a ViewModel?

"Because the ViewModel outlives the Activity. After a rotation it would hold a reference to a destroyed Activity, which is a memory leak. If I need resources, I map them in the UI, or use the application context in the data layer."

33. What is an ANR and how do you avoid it?

"Application Not Responding: the main thread is blocked for about five seconds and Android offers to close the app. I avoid it by moving disk, network and heavy CPU work off the main thread with withContext, and I catch it with StrictMode in debug and Android Vitals in production."

35. What makes a composable skip recomposition?

"Its parameters are stable and haven't changed. A plain List is unstable for the compiler, so you'd use an immutable collection or @Immutable — although with strong skipping, which is now the default, unstable parameters are compared by reference. I'd check it with the Layout Inspector's recomposition counts."

39. LaunchedEffect or rememberCoroutineScope?

"LaunchedEffect is for effects driven by state: it runs when the composable enters the composition or when its key changes. rememberCoroutineScope is for launching coroutines from user callbacks, like an onClick that opens a bottom sheet."

42. When would you add a UseCase, and when not?

"When the logic is reused across ViewModels or combines several repositories. If it only forwards a call to a repository, it's just noise — the official guide treats the domain layer as optional."

43. How would you build an offline-first screen?

"The UI observes Room, and the network only updates Room. So there's a single source of truth, it works offline and queries are reactive. If the refresh fails, I keep showing the cached data with a non-blocking message. For a bank, writes like a transfer are never applied as final offline."

44. What does Hilt give you over manual DI?

"The graph is validated at compile time, it manages scopes and lifecycles like @Singleton and @ViewModelScoped, it integrates with ViewModels and navigation, and in instrumented tests I can swap modules. Manual DI works for a small app, but it doesn't scale well."

49. Fakes or mocks?

"Fakes by default, like Google recommends: a fake repository backed by a MutableStateFlow behaves like the real one, so tests check behaviour and survive refactoring. I'd use mocks only to verify a specific interaction, like an analytics event."

50. implementation vs api in Gradle?

"implementation hides the dependency from modules that depend on mine, so builds are faster and implementation details don't leak. api exposes it — I only use it when that dependency is part of my public API."

51. Why would something crash only in release?

"Most likely R8: it obfuscated or removed a class used through reflection — serialization or Retrofit — because there was no keep rule. I'd deobfuscate the stack trace with the mapping file and reproduce it with a minified build locally."

52. How would you ship a risky release?

"Behind a feature flag, so I can turn it off without a new release. Internal track first, then a staged rollout — 5, 20, 100 percent — watching the crash rate, ANRs and business errors like failed transfers. If something goes wrong, I halt the rollout and switch the flag off."

53. KMP or Flutter? Why move to native?

"Flutter shares UI and logic through its own rendering engine. KMP shares domain and data and keeps the UI native. For a bank with strong native teams and platform requirements — biometrics, accessibility, security — I think KMP fits better. Flutter makes sense for a small team that needs speed. I'm moving to native because I kept needing the platform: in Flutter that meant platform channels; in native it's first-class. And a lot carries over: immutable state and unidirectional data flow from Bloc map directly to Compose and MVVM."


3. Bonus: la transferencia en 60 segundos

Es casi seguro que te pidan explicar un flujo. Ensaya este en voz alta:

"When the user confirms a transfer, the ViewModel already has an idempotency key — a UUID I generate when the user enters the review step, and I keep it in the SavedStateHandle so it survives process death. I send the request from an application-level scope, not viewModelScope, because the transfer must not be cancelled if the user leaves the screen. If I get a timeout, I don't show it as a failure — the server may have executed it. The state becomes 'unknown' and a retry sends the same key, so the server never pays twice. Business errors like insufficient funds come back typed, and I show them on the review step."


4. Vocabulario que debe salirte sin pensar

EspañolInglés
girar el móvil / cambio de configuraciónrotation / configuration change
se destruye y se recreait's destroyed and recreated
muerte del procesoprocess death
se cancelait gets cancelled
fuente de verdadsingle source of truth
hilo principalmain thread / UI thread
fuga de memoriamemory leak
despliegue gradualstaged rollout
reintentoretry
cachécache (pronunciado /kæʃ/, como "cash")
inyección de dependenciasdependency injection
estado inmutableimmutable state
flujo unidireccionalunidirectional data flow
contrapartidatrade-off
compilación minificadaminified (release) build

Fuente: docs/INTERVIEW_ENGLISH.md