When you own the loop
A full manual loop is the second rung: you stop letting fit() drive and write the epoch and batch iteration yourself. This buys total control over loop structure — you can interleave evaluation, branch on a custom schedule, or run anything between batches — but you also inherit nothing. No progress bar, no callbacks, no checkpointing, no distribution. Every convenience becomes a line you write.
The skeleton
The shape is always the same: outer loop over epochs, inner loop over the dataset, and inside each step a forward pass, a loss, a gradient computation, and an optimizer update. The forward-and-loss part is portable, but the gradient step is backend-native — the example below uses TensorFlow's tf.GradientTape. On PyTorch you'd call loss.backward(); on JAX you'd transform with jax.grad. That single block is the whole reason there's no one true backend-agnostic loop.