Side Effects & Immutability
Pure computation versus I/O and mutation, the functional-core / imperative-shell split, and what immutability buys and costs.
Computation returns a value; an effect changes something. Database writes and network calls are the obvious ones — clocks and randomness are the two that make a function look pure and behave otherwise.
Gather the inputs, decide with pure logic, then perform the effects the decision asked for. It makes the interesting part trivially testable, and it charges you for fetching data you might not need.
A value that cannot change is a value you can reason about once. That buys local reasoning and cheap change detection, and it charges copying, allocation and awkwardness in the places that genuinely want to mutate.
Mutation is not the problem. Mutation of something with no clear owner is. Local ownership, a stated lifecycle and a boundary it does not cross make it the right design surprisingly often.
A value reachable from everywhere is a dependency nobody declared. It shows up as tests that pass alone and fail together, functions whose behaviour depends on what ran first, and a concurrency bug you cannot reproduce.
A pure function needs no setup: you call it and assert. Every line of setup a test requires is the design telling you what that code depends on, which makes test friction the cheapest design signal available.
Push effects to the edges so the middle can be reasoned about — and know the limit: some domains are effects all the way down, and there the honest design is to make each effect a modelled step rather than to pretend there is a pure core.