compile + fit + callbacks. Three lines that hide a state machine, an optimizer step, a loss reduction, a metrics accumulator, and a callback bus. The track unpacks each piece so when fit() is doing something surprising, you know exactly which knob is loose.
What compile actually wires
model.compile() doesn't touch your weights or run a single forward pass. It's pure configuration: it binds three components onto the model so that fit() knows how to learn. The optimizer decides how a gradient becomes a weight update. The loss is the scalar the optimizer drives toward zero. The metrics are read-only observers that report progress without ever touching the gradient.
The Code section shows the two shapes you'll write constantly — a classification compile and a regression compile. Notice that the only things that change between them are loss and metrics; the call structure is identical.
Strings or instances — and why it matters later
You can pass string shortcuts ("adam", "mse") for speed, or class instances when you need real control. The string "adam" gives you Adam at its default learning rate; keras.optimizers.Adam(learning_rate=1e-3) lets you set it. Reach for instances the moment you want to tune a hyperparameter, attach a learning-rate schedule, or guarantee the exact config is the one that gets saved with the model. The one hard rule: always call compile() before fit() — an uncompiled model has no idea what "better" means.