I am always thinking, what can I print so that I know my program works? I try to make this easy and still useful. Sometimes a little effort to make it easy pays off big.
In Fortran 4, what a pain.
PRINT (5, 20) I, J 20 FORMAT (2HI=, I10, 4H, J=, I10)
In C, a little better.
printf(stderr, "i=%d, j=%d\n", i, j);
In Javascript, nice, and works for objects too.
console.log({i:i, j:j})
Javascript works as well as it does because there is an excellent "console" that collects the prints and lets one further inspect or even compute with the results.
In tight situations I've built my own mechanisms for collecting debugging print output.
PL/1 Compiler used a macro to print any register legibly and non-destructively.
TRACE "Symbol Table Size", B6
State Ports let me write a byte to a custom made peripheral for debugging early multi-microprocessors.
OUT 12, I
Spinometer similarly modulated a single microcontroller pin to move a meter showing variables.
meter (10, threshold, 1024);
Test Point logged objects in a circular buffer without any storage allocation or print formatting until read.
trace("dispatch", thisEvent)
Exploratory Parsing captured failed cases as legitimate input and tallied it with all other results.
fact = key value | other_char
Rubific parsed a subset of Ruby expressions tracing recursive entry and exit as it went.
trace("( ... ") trace(" ... )")