Bindings do not change; state changes happen through explicit references like atoms and refs.
In Clojure, a local binding does not change value. That is the default. Instead of reassigning, you compute a new value and bind it (often in a new scope).
1(let [x 1
2 x (inc x)]
3 x) ;; => 2
That second x is not “mutating x”. It is introducing a new binding that shadows the old one.
When you truly need changing state, Clojure makes it explicit: you use references like atoms, refs, or agents (covered later). This keeps most code pure while still letting programs model reality.
Java mental model: locals are effectively final, and state is pushed into dedicated concurrency-safe containers instead of being scattered across objects.