skip to content

When a user re-requests an activation email, how do you establish whether the first link still works?

level: middleimportance: must knowfreq 54%

answer

  1. A repeat request changes what already exists
  2. Two designs, not one obvious answer
  3. The order of attempts decides the evidence
  4. Attempt the older link first
  5. Assert the refusal and the success

basics

~20 s

Trigger the first send, request a resend, then attempt the older link before the newer one. A product either invalidates the earlier link or leaves both usable; the case must assert which one, explicitly, rather than assume.

solid answer

~40 s

A resend nearly always mints a **new** single-use link, so the real question is what became of the one already delivered. Two designs exist: the new link supersedes the old, leaving exactly one usable path, or both stay live until one is consumed or they expire. Build the case in a fixed order - first send, capture its link, resend, capture the second link, then attempt the **older** link first. Order matters, because consuming the newer link completes the flow, after which the older one is refused for an entirely different reason and the two designs become indistinguishable. Assert both halves, the refusal the design predicts and the success that follows, and name the expected design in the case so a silent flip fails the run.

code

pseudocode · 16 lines
pseudocode
first  = request_activation(account)
link_a = link_from(first)

second = request_activation(account)   # the resend
link_b = link_from(second)

# older first: consuming link_b would end the flow
outcome_a = open_link(link_a)
outcome_b = open_link(link_b)

if expected_design == SUPERSEDE:
    assert outcome_a.refused_as(SUPERSEDED)
    assert outcome_b.completed_flow()
else:
    assert outcome_a.completed_flow()
    assert outcome_b.refused_as(ALREADY_COMPLETE)

go deeper

for a junior

Be ready to say what a resend actually does: it usually issues a brand-new single-use link rather than sending the old one again. Know that the earlier link may or may not still work, and that this is worth checking rather than assuming.

for a middle

Explain the two designs and how a case tells them apart: capture both links, attempt the older one first, and assert the outcome each design predicts. Be able to say why attempting the newest link first destroys the evidence.

for a senior

An interviewer expects you to defend the assertion, not just the steps: a specific refusal rather than a generic failure, both halves asserted in one case, and the expected design named so a silent behaviour change fails the run.

for a principal

Own the decision itself. Superseding narrows the window in which a leaked link is live; leaving both usable cuts support load from users who open the older message. Say which you would pick for a given product and what evidence would change your mind.

A resend is not a redelivery. When a user presses "send it again" on a sign-up, password-reset or address-change flow, almost every product mints a **new** single-use link and sends that. What it does to the link it already sent is a separate decision, and that decision is what an automated case has to hold in place. ## Two designs, both legitimate **Supersede on resend.** Issuing the new link invalidates the outstanding one, so exactly one usable path exists at any moment. This is what most security reviews ask for, and it is also what confuses users: the message they opened first, often the one still on their screen, quietly stops working. **Additive resend.** The new link is issued alongside the earlier one and both stay usable until one is consumed or they expire. Gentler on the user who scrolls back to the older message, and it widens the window during which a link that has leaked is still live. Neither design is a bug. What is a bug is a suite that cannot tell you which one you shipped, because that is the state a product drifts into when the record behind the link is rewritten by one change and appended to by the next. | Question | Supersede on resend | Additive resend | | --- | --- | --- | | Usable paths after two sends | one, the newest | two, until consumed or expired | | Older link's outcome | refused as superseded | accepted, completes the flow | | What users report | "the link says it is invalid" | "an old message still let me in" | | What the case asserts | one refusal, one success | two successes, then the policy limit | ## Order the attempts, or the case proves nothing The case has a fixed shape, and the order of the last two steps is the whole design: 1. Trigger the first send and capture the link the message carries. 2. Trigger the resend and capture the second link. Keep both, and do not overwrite the first with whatever is newest. 3. Attempt the **older** link and assert the outcome the design predicts. 4. Attempt the newer link and assert it completes the flow. 5. Assert the account finished in exactly one end state. Step 3 before step 4 is not a style preference. Consuming the newer link **completes the flow**: the account is confirmed, the password is set, the address is changed. Every link then outstanding is refused, not because it was superseded, but because there is nothing left to do. A case that opens the newest link first and then finds the older one refused observes the same result under both designs, and so distinguishes nothing. It is the single most common way this case passes for years while asserting nothing at all. ## Assert the reason, not the absence of success "The older link did not sign the user in" is satisfied by a superseded link, an expired link, an already-completed flow, a routing change and a rendering error. Tighten it: - Assert the **specific** refusal the supersede design produces, distinguishable from "this flow is already complete" and from "this link has expired". - Assert the positive half in the same case. A design that refuses *both* links is broken in a much worse way, and only the success assertion catches it. - Assert that the resend produced one additional message rather than a burst, and read the message the case itself triggered rather than whichever is newest in the recipient mailbox. - Do not assert on the internal shape of the link. It is opaque by design, and asserting its format couples the case to an implementation detail that is free to change. - Name the expected design in the case title, so a reader of a failure knows immediately whether the product changed or the case is wrong. ## How this case quietly stops working - **It re-derives the "first" link from the newest message.** Both attempts then use the same link and the comparison collapses. - **It was written against whatever the build did that day.** Recording behaviour is not asserting it; the expectation has to come from the intended policy, so a flip fails the run. - **It asserts only that a second message arrived.** That passes when the flow resends the identical link, which is a third design and usually an accident. - **It runs the two attempts against different accounts.** There is then no contention between the links, and the question the case was asking disappears. When the team deliberately switches designs, this case should fail loudly and be edited in the same change: one refusal expectation becomes a success expectation, and the case name changes with it. A case that survives that switch untouched was never testing the design at all.

  • Why is asserting that the older link did not sign the user in too weak here?
    That assertion passes for several unrelated reasons: the account was already confirmed, the link expired, the page errored. Assert the specific refusal the supersede design produces, so the case fails both when the product starts refusing for a different cause and when it stops refusing at all.
  • The team switches the flow from leaving both links usable to superseding. What should happen to this case?
    It should fail loudly and then be edited in the same change: one refusal expectation becomes a success expectation and the other flips with it, and the case name changes to match. A case that keeps passing across that switch was never asserting the design in the first place.

Asking a hotel desk for a replacement room key: some desks deactivate the card you are already carrying, others leave both working. You cannot tell which kind of desk you are at without trying the old card first.

saying these in an interview costs you the question

  • Assumes every product invalidates the earlier link on resend
  • Opens the newest link first and calls the case complete
  • Asserts only that a second message arrived
  • Treats any refusal of the older link as proof of superseding
  • Expects the resend to redeliver the same link unchanged