skip to content

After the system reclaims a backgrounded app's process, how should a case prove the screen rebuilds?

level: seniorimportance: should knowfreq 44%

answer

  1. Rebuild, not resume
  2. Force it, do not wait for it
  3. Stopping a process is not clearing data
  4. Re-enter the returning path, not a fresh start
  5. Assert what must not survive either

basics

~20 s

Force the termination deliberately rather than waiting for it, re-enter through the path a returning person takes, and assert the rebuilt screen carries the same record and the same answers - while checking that nothing half-finished silently resumes.

solid answer

~50 s

A reclaim is a rebuild, not a resume: everything held in memory is gone and the screen is reconstructed from the small state snapshot the platform preserved plus whatever the app wrote to storage. The case must create that condition on purpose, using the platform's control for stopping a running app's process, rather than hoping memory pressure arrives. Stopping the process is **not** the same as clearing the app's stored data - that wipes the persistence the rebuild is supposed to read and guarantees a first-run screen instead. Re-enter the way a returning person does, then assert on what renders: the same record is open, earlier answers in a multi-step flow survive, and derived data is fetched again rather than assumed. Assert the negatives too - no silent resubmission, no masked value coming back readable.

code

pseudocode · 15 lines
pseudocode
case "checkout step three rebuilds after the platform reclaims the process":
    sign_in(as = seeded_user)
    open(screen = "checkout")
    advance_to(step = 3, with = {address: seeded_address, delivery: "standard"})

    expected = read_rendered(["step", "address_line_1", "delivery"])

    stop_app_process()                     # platform-style termination
    assert app_stored_data_present() == true   # precondition: not a data wipe

    resume_app_from_switcher()
    wait_until(screen_settled)

    assert read_rendered(["step", "address_line_1", "delivery"]) == expected
    assert payment_attempts_for(order_id) == 0

go deeper

for a junior

Know that a backgrounded app can be stopped by the platform to free memory, and that coming back then rebuilds the screen instead of resuming an untouched one. Recall that stored data survives this and only what was held in memory is lost.

for a middle

Explain how a case forces the termination instead of waiting for it, and why stopping a process differs from clearing stored data. Be able to name the values a rebuilt screen must carry and say where each of them came from.

for a senior

Show production judgement: assert the negatives, guard the case against decaying into a fresh-start check, and reason about what should be fetched again rather than preserved. Expect to describe a real defect a case like this caught for you.

for a principal

Own where the line sits - which journeys must survive a reclaim at all, and what persisting their values costs the product. Decide how many such cases the suite carries before their slowness outweighs the rarity of the condition in the field.

## Reclaim is a rebuild, not a resume A backgrounded app is a candidate for eviction. When the platform needs memory it can stop the app's process outright; nothing the app held in memory survives that. The next time the person comes back, the screen they were on is **reconstructed** - from the small state snapshot the platform preserved on the app's behalf, plus whatever the app deliberately wrote to its own storage. Everything else has to be fetched or derived again. That is a genuinely different code path from resuming a resident app, and it is the path that carries most of the defects: a screen rebuilt without the identifier of the record it was showing, a multi-step flow that restarts at step one, a half-finished action that fires a second time because the rebuilt screen thinks it has not run yet. ## Force it, and do not confuse it with its neighbours The case must create the condition, not wait for it. Waiting for real memory pressure is not reproducible and turns the case into a lottery. Use the platform's own control for stopping a running app's process. The important discipline is knowing what each nearby action actually destroys. | Action the case takes | What it destroys | What it therefore tests | | --- | --- | --- | | Brief background, then return | nothing; the process stays resident | work that runs on return: refreshes, re-authorization, re-subscription | | The platform stops the process | in-memory values only; stored data survives | rebuild from the preserved snapshot plus persistence | | The person dismisses the app from the recent-apps switcher | in-memory values, and on some platforms the preserved snapshot too | a colder entry than a reclaim; confirm which your platform does before relying on it | | Clearing the app's stored data | everything the app kept on the device | the first-run experience, not restoration | | Removing and re-installing the app | everything, plus anything granted at install | a different subject entirely, owned elsewhere | The two rows people mix up are the middle two. Clearing stored data is the popular shortcut because it is easy to invoke, and it guarantees the case can never observe restoration, because the data the rebuild is meant to read has been deleted. Add an explicit precondition assertion that the app's stored data is still present after the termination, and that whole class of false result disappears. ## What the rebuilt screen must carry Decide, per screen, which values must survive and where each one comes from: 1. **Identity of the thing on screen** - which record, which order, which conversation. Small, irreplaceable, and the first thing to preserve. 2. **Position in the journey** - which step of a multi-step flow, so the person is not sent back to the beginning. 3. **Answers the person supplied** - typed text and selections that cannot be recomputed from anywhere. 4. **Everything else** - prices, availability, lists, avatars. Re-derive these. A preserved copy is stale by definition and stale is worse than a second fetch. Then re-enter through the returning-user path and compare against what renders, not against what the app holds internally. ## Assert what must not survive A rebuild is also an opportunity for the product to do something it should not. - A submission that already succeeded must not be sent again by the rebuilt screen. - A masked or hidden value must come back masked, not readable. - An authorization decision must be made again against current rights, not restored from a snapshot. - Work the person explicitly cancelled before leaving must stay cancelled. Requests that were in flight at the moment of termination deserve their own assertion: the outcome must be exactly one of the two defined ones. Either the service accepted it, and the rebuilt screen shows the result and offers no second attempt; or it did not, and the screen offers a clean retry of that action. ## Keeping the case from decaying into a cold start The failure mode of this case over time is that it quietly stops testing anything. Someone changes the entry path, or the preserved snapshot stops being written, and the case still passes because a freshly started app also renders a valid screen. The guard is one assertion that a cold start could never satisfy - an answer entered on an earlier step still rendered, or the specific record still open - placed deliberately and commented as the reason the case exists. Without it, a green result means only that the app starts, which was never the question.

  • How do you stop this case quietly degrading into a plain fresh-start check?
    Assert the preconditions it depends on - that stored data still exists after the termination and that the entry used was the returning one - and include at least one assertion a fresh start could never satisfy, such as an answer from an earlier step still rendered. Without that guard, the case keeps passing once restoration breaks, and a green result then means only that the app opens.
  • What should the case assert about a request that was in flight when the process was stopped?
    That the outcome is exactly one of the two defined ones and never both. Either the service accepted it, in which case the rebuilt screen shows the result and offers no second attempt, or it did not, in which case the screen offers a clean retry of that action. A rebuild that silently re-sends the request is precisely the defect this case exists to catch.
  • Which values deserve to be preserved across a reclaim, and which are better fetched again?
    Preserve the small, irreplaceable things: which record is open, which step of the flow, what was typed. Fetch everything the service can return again, because a preserved copy is stale by definition and stale is worse than a second call. Never preserve secrets or fully rendered sensitive values - keep the reference and re-fetch under the rights that hold now.

saying these in an interview costs you the question

  • Clears the app's stored data and calls that process termination
  • Waits for memory pressure instead of forcing the termination
  • Asserts only that the app opens again without crashing
  • Treats a fresh start and a returning entry as the same path
  • Lets a half-finished submission re-send itself on rebuild
  • Restores a masked value in readable form after the rebuild