The Senior Signal Quiz
BEHAVIORAL › Soft Skills
You are asked to migrate a 200-screen XML app to Compose while features keep shipping. What is the strongest approach?
- Freeze all feature work and rewrite the entire app UI in Compose in one branch
- Migrate incrementally with ComposeView/AndroidView interop, leaf-first
- Keep XML forever since Compose interop is still not production-ready
- Rewrite only the navigation graph and leave every screen still in XML
Answer: Migrate incrementally with ComposeView/AndroidView interop, leaf-first
Compose and Views interoperate, so an incremental, flag-gated migration of leaf screens keeps features shipping and de-risks the change, unlike a big-bang rewrite.
During a staged rollout your crash-free users rate drops from 99.7 to 98.9 percent. What does a senior do first?
- Push to 100 percent so you can collect crash data faster across users
- Ignore it; 98.9 percent is still comfortably above a 95 percent bar
- Pause rollout, triage the top crash, then roll back or fix forward
- Delete the app from the Play Store right away to stop all new installs
Answer: Pause rollout, triage the top crash, then roll back or fix forward
The point of a staged rollout is to limit blast radius; a regression in crash-free rate means halt, investigate the top issue, and only widen once stability recovers.
What is the key difference between a feature flag and a Play staged rollout?
- A flag toggles runtime behavior; a rollout controls who gets the binary
- They are ultimately the same mechanism just given two different names
- A feature flag can only ever be toggled at compile time, never at runtime
- A staged rollout lets you A/B test individual UI behaviors server-side
Answer: A flag toggles runtime behavior; a rollout controls who gets the binary
Flags decouple behavior from deployment and allow server-side kill switches and ramps, while a staged rollout governs binary distribution; they solve different problems.
Which statement about a Java to Kotlin migration is correct?
- You must convert the whole module at once because Kotlin and Java cannot mix
- Kotlin and Java interoperate, so you can migrate file by file in one module
- The IDE converter gives perfectly idiomatic Kotlin, so no cleanup is needed
- You should rewrite all stable Java classes just to adopt Kotlin syntax sooner
Answer: Kotlin and Java interoperate, so you can migrate file by file in one module
Kotlin/Java interop lets you convert incrementally; the auto-converter is a starting point that still needs idiomatic cleanup, and rewriting stable code for style alone adds risk without value.
You suspect UI jank in a Compose list. What is the most senior first step?
- Guess with remember everywhere and add keys before measuring anything.
- Measure first with tracing, Jank stats, and recomposition counts.
- Rewrite it to RecyclerView now, since Compose is always the slower choice.
- Lengthen animations so dropped frames are less obvious, not actually fixed.
Answer: Measure first with tracing, Jank stats, and recomposition counts.
Performance work starts with measurement; profiling recomposition and frame timing identifies the real bottleneck before applying targeted fixes like stable keys or hoisting state.
When converting an RxJava codebase to coroutines, which approach minimizes risk?
- Delete all Rx code and rewrite every stream in one giant pull request
- Use kotlinx-coroutines-rx adapters and migrate one layer at a time
- Wrap each Observable in runBlocking so it behaves like a suspend call
- Keep RxJava and coroutines fully separate, duplicating every stream by hand
Answer: Use kotlinx-coroutines-rx adapters and migrate one layer at a time
The rx interop adapters (asFlow, await, rxSingle) let Rx and coroutines coexist so you can migrate incrementally; runBlocking blocks threads and a single giant PR is high-risk.
What best reflects senior thinking about multi-module boundaries?
- More modules always speed builds, so split every class into its own module
- Feature modules should depend on each other directly to make code sharing easy
- Define boundaries by responsibility, with features depending on core
- Module structure has no impact on build time, coupling, or team workflow
Answer: Define boundaries by responsibility, with features depending on core
Clear, responsibility-based boundaries with one-directional dependencies (features to core) improve build parallelism and reduce coupling; cross-feature dependencies and over-splitting both cause problems.
An interviewer asks what an ANR is and how you would treat it in production. Which statement is most accurate?
- An ANR means the main thread blocked ~5s on input; vitals flags a 0.47% ANR rate
- ANRs are only ever caused by uncaught exceptions thrown on background threads
- ANRs cannot be observed in production and can only ever be reproduced on a debug build
- An ANR is triggered the instant any network request is started on the main thread
Answer: An ANR means the main thread blocked ~5s on input; vitals flags a 0.47% ANR rate
An ANR comes from a blocked main thread (roughly 5s for input dispatch), and Android vitals reports a user-perceived ANR rate with a 0.47 percent bad-behavior threshold that can affect Play discoverability.
Your app's cold-start p95 has regressed after a few releases. Which approach best reflects senior judgment?
- Add a longer splash animation to mask the delay and just consider it handled
- Profile with Macrobenchmark, check Baseline Profile, fix Application.onCreate
- Move all initialization eagerly into Application.onCreate so nothing is deferred
- Disable R8 minification to shorten the build and assume startup just improves
Answer: Profile with Macrobenchmark, check Baseline Profile, fix Application.onCreate
Macrobenchmark measures real cold start, and a current Baseline Profile plus trimming Application.onCreate work targets the actual cost; eager init and disabling R8 typically make startup worse.
A screen leaks memory that grows on every rotation. What is the most reliable senior diagnostic step?
- Call System.gc() in onDestroy to force leaked objects to vanish
- Set android:largeHeap=true so the app has more memory headroom
- Take a heap dump, then trace the retained chain back to the GC root
- Wrap all Activity and View references in try/catch blocks to avoid leaks
Answer: Take a heap dump, then trace the retained chain back to the GC root
A heap dump plus the retained reference chain pinpoints what is holding the object (often a static or long-lived listener); System.gc() and largeHeap mask the problem rather than locate it.
An interviewer asks whether you would adopt MVI for a new feature. What signals a senior answer?
- Declare MVI universally superior and immediately mandate it across the whole app codebase
- Refuse to commit to any pattern and keep all the business logic inside the Activity
- Decline to give any opinion because architecture choices are purely subjective anyway
- Weigh the trade-offs: MVI's unidirectional state fits complex screens, not simple ones
Answer: Weigh the trade-offs: MVI's unidirectional state fits complex screens, not simple ones
Senior signal is reasoning from the feature's complexity and the team's familiarity rather than dogma; MVI's value depends on context, not a blanket rule.
Why do senior engineers monitor p95 and p99 latency rather than the average?
- Averages are technically impossible to even compute on constrained mobile devices
- Tail percentiles expose the slow experiences a minority hit that averages hide
- p99 always equals the mean whenever the latency data is normally distributed
- Percentiles need far fewer data points to compute than a plain average does
Answer: Tail percentiles expose the slow experiences a minority hit that averages hide
A mean is dominated by the fast majority and conceals the tail; p95/p99 surface the worst real-user experiences, which are the ones that cause complaints, ANRs, and churn.
A feature already at 100 percent rollout starts causing crashes, but it sits behind a server-controlled flag. What is the fastest safe mitigation?
- Build a new version and wait for Play review before shipping the rollback
- Tell affected users to clear app storage and cache to stop the crashes
- Turn the server-side flag off to disable it instantly, then fix forward
- Halt the rollout in Play Console, which removes it from installed devices
Answer: Turn the server-side flag off to disable it instantly, then fix forward
A server-side kill switch disables the broken behavior immediately for all users without shipping or waiting on review; a new build is far slower and halting a 100 percent rollout does not undo installs.