An external call hands control to another contract, which may call back into you before your state updates finish. Order every function as checks, then effects (state writes), then interactions (external calls), and add a reentrancy guard for anything touching balances.
uint256 amount = balance[msg.sender]; // checks
balance[msg.sender] = 0; // effects
(bool ok,) = msg.sender.call{value: amount}(""); // interactions
require(ok);Cross-function and read-only reentrancy exist too: a view function can be called mid-attack and return stale data.