Executing a block a second time in the same session changes the numbers — what makes a block safe to re-execute?
answer
- look at the assignment, not the step
- reads a name it also writes
- accumulate, grow, narrow, adjust
- a fresh output name per block
- so file order is a valid order
basics
~20 sA block is safe to execute again when it reads only names bound earlier in the file and does not write a name that also appears on its own right-hand side. Blocks that accumulate into, grow or adjust their own input drift on every run.
solid answer
~50 sThe test is the shape of the assignment, not the content of the step. In an interactive session — a long-lived process you send blocks to one at a time, which keeps every value those blocks ever bound — a **block** can be executed again at any moment, and people do it constantly after fixing something below it. If the block reads only names bound earlier in file order and writes a fresh name, executing it twice recomputes the same value and nothing moves. If it assigns to a name that also appears on its own right-hand side — `totals = totals + this_period`, a collection grown onto itself, a threshold scaled again — then its input has already moved by the second execution, and the result is wrong by one run. Nothing raises: the value is well formed, just wrong.
code
pseudocode · 9 lines# drifts: each block writes a name that appears on its own right-hand side
totals = totals + this_period # run twice -> this_period counted twice
history = history + new_rows # run twice -> new_rows appear twice
cutoff = cutoff * 0.9 # run twice -> cutoff is 0.81 of base
# safe: each block reads names bound earlier and writes a fresh name
totals_to_date = opening_totals + this_period
full_history = opening_history + new_rows
final_cutoff = base_cutoff * 0.9go deeper
Recall that a block can be executed again at any moment, and that a block adding into the very name it reads will count the same thing twice. Watch for the same name on both sides of the assignment.
Explain the read-write shape and name the ways it appears: accumulating, growing, narrowing and adjusting a binding in place. Say why none of them raise and why the value still looks plausible.
Show how you keep this out of a file by construction — a fresh output name per block, accumulation derived from a stable opening figure — and how you find an existing drift once a number is already suspect.
Weigh the memory a checkpoint-per-block costs against the reviewability it buys, and decide what your team's files are allowed to look like before someone else has to trust one.
## The rule, in one line A block is safe to execute a second time in the same session when it **reads only names bound earlier in file order and writes a name that does not appear on its own right-hand side**. The moment a block assigns to something it also reads, executing it twice stops being the same as executing it once. Two definitions first, because both matter. An **interactive session** is a long-lived process you send blocks of code to one at a time, and it keeps every value those blocks ever bound. A **block** is one runnable piece of the file that can be executed on its own, in any order — and because it can be executed in any order, it can also be executed twice. People do this constantly: after fixing a typo below it, after scrolling back, after a wandering train of thought that ends with pressing run again to see the output. ## The shapes that drift - **Accumulation.** `totals = totals + this_period`. Every execution adds the period again. The value stays well formed and is simply too large by one run. - **Growth.** Rows, keys or records concatenated onto the very binding they came from. A second run duplicates them, and the duplication is invisible until something downstream counts. - **Successive narrowing.** A block that removes rows, columns or categories and writes the smaller result back to the same name. The first run removes ten; the second removes ten more from what is left. - **Stepwise adjustment.** A rate, a threshold or a scaling factor multiplied or shifted and written back. Each execution moves it further from where the file says it should be. - **Effect outside the process.** A block that writes a file, appends to a record store or posts something. The bindings may be untouched and the machine is not, and a later block that reads what was written sees the second copy. The careful version of the rule: the hazard is the read-write shape, not an absolute law. A block that assigns to a name it reads can still be harmless when the expression is genuinely idempotent over that value — clipping to a fixed ceiling, or applying a condition that is already satisfied. But you cannot see that from the outside, a later edit can quietly destroy it, and the reader cannot see it either. So treat the shape itself as the thing to avoid. ## Why nothing tells you | | What the screen shows | What actually happened | |---|---|---| | After one execution | a plausible value | one application of the step | | After two | a plausible value | two applications of the step | | Difference | none you can see | the answer is wrong by one run | That table is the whole reason this is asked in interviews. A drifting block does not raise, does not warn and does not change type. It returns something of the right shape and the right order of magnitude, which is the failure mode that survives review. ## Making a block safe 1. **Give the output a name of its own.** The single highest-value habit here: read `raw`, write `trimmed`; read `trimmed`, write `enriched`. Names cost nothing and each one is a checkpoint you can re-execute freely. 2. **Derive from the earliest binding, not the latest.** If a step needs an accumulated value, accumulate from a stable opening figure rather than from whatever the name currently holds. 3. **Push the repetition into the expression, not the name.** Sum a collection in one expression rather than adding into a running name across executions. 4. **When a block must change something outside the process, make it state the whole desired result** rather than a delta, so the second execution asserts the same end state instead of adding to it. ## The session design changes your exposure, not the rule Designs differ, and it is worth knowing which one you are on. A session that simply executes whatever you send it offers no protection at all — the rule is entirely yours to keep. Dataflow-style designs record which blocks read which bindings and re-execute stale downstream blocks from their inputs, so a block is normally recomputed from a consistent upstream state rather than from its own previous output; some of them reject a read-write cycle outright. Do not assume you are on one of those, and do not assume a colleague opening your file is either. ## Why it matters past your own screen If every block reads only earlier bindings, then file order is a valid execution order, and a **clean run from empty** — a new process with nothing bound, executing every block once in the order the file lists them — actually means something. Drifting blocks break that link. The file may still run top to bottom, but the number it produced on your screen depends on how many times each block was pressed, and nothing anywhere records that.
- If every block writes a fresh name, the session holds far more values at once. Is that a problem?It costs memory, and on a large working set that can matter. But the cost is usually small next to what it buys: every intermediate becomes a checkpoint you can re-execute or inspect without rebuilding the chain, and the file gains a valid execution order. Where the footprint genuinely bites, keep the fresh names and release the ones you have finished with deliberately, rather than reusing one name as a rolling buffer.
- A block reads a name it also writes, but you are sure the operation is idempotent. Leave it?Prefer not to. It may well be harmless today — clipping to a fixed ceiling, or applying a condition already satisfied. But nothing in the file says so, a reader cannot see it, and one later edit that turns the ceiling into a decrement destroys the property silently. A fresh output name costs one line and removes the question entirely.
saying these in an interview costs you the question
- Re-running a block always reproduces its previous result.
- If a block drifts, the second execution raises an error.
- Only blocks that write files are unsafe to run again.
- Reusing the same name is fine as long as the block is one line.
- Running every block twice is a good way to check a file.