You can't put a breakpoint on a hardware sketch — at least, not without a debugger and a spare day. But you can do the next best thing: print. Serial.println("got here"); is the print-debug statement of the Arduino world, and the moment you accept that, every difficult bug becomes tractable. The pin doesn't toggle? Print the variable. The interrupt fires twice? Print a counter.
Where the bytes go
When you write Serial.println("hi"), the Arduino runtime hands "h", "i", and a newline to a peripheral called the USART (Universal Synchronous Asynchronous Receiver Transmitter). The USART is a tiny piece of dedicated silicon whose only job is to shift bytes out, one bit at a time, on the TX pin. Pin 1 on the Uno is the TX line.
For each byte, the USART pulls TX low for one bit-time (the "start bit"), then drives the eight data bits LSB first, then releases TX back to its idle-high state (the "stop bit"). At 9600 baud, each bit-time is 1/9600 s ≈ 104 microseconds. A byte takes 10 bit-times — about a millisecond — so a sketch can print roughly 960 characters per second. Faster baud rates exist (115200 is common); they shorten each bit-time proportionally.
The "asynchronous" in USART is important: there's no shared clock between sender and receiver. Both sides have to agree on the baud rate, then trust their own clocks to stay accurate enough that the start bit's falling edge tells the receiver when to sample each subsequent bit. If your baud rates disagree, you get gibberish — and that's exactly what you see when the serial monitor is set to 9600 but your sketch called Serial.begin(115200).
The wire from chip to monitor
On the Uno, the ATmega328P's TX pin doesn't go directly to your computer's USB port. It goes to a second chip (the ATmega16U2 on official Unos, a CH340 on clones) that converts the UART signal to USB packets. Your operating system sees a virtual COM port. The serial monitor opens that COM port and reads bytes.
Three protocols, three implementations, all transparent because the abstraction holds: you call println, you see the line. But it's worth remembering that under the hood are voltage levels, timing diagrams, and a protocol that's been standardised since the 1960s. The same UART that drove dial-up modems is debugging your motor controller. There is something pleasing about that.
What you can do with it
Print state. Print transitions. Print timing. Serial.println(millis()) is the simplest profiler in the world. If your loop should run every 100 ms and you're seeing 250, there's your problem. If you write something to a register and the register reads back different, print both — the chip is telling you something. Serial is a flashlight; bugs prefer the dark.