A callback is "executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time" ([L1 ], [L2 ]).
In some cases, as when a -command option is passed to lsort, the receiving command (see below) immediately executes the code (blocking, or synchronous, callback). More commonly, a callback is specified in an after, bind, trace, I/O handler, or widget -command option, etc, and called when appropriate (event-oriented, deferred, or asynchronous, callback). A deferred callback must be able to execute even if the context where it was defined has since been destroyed.
In Tcl, callbacks are typically either expressions, lambda expressions, or scripts. An expression is evaluated using expr and a lambda expression by apply. They will not be discussed further here.
Callback scripts are lists of words (with wrinkles such as when one prepends a + character to a bind callback, which is part of the callback but not of the script proper). The script can be a command prefix, i.e. a script that expects that further arguments will be concatenated to it at the point of execution, at the receiving command's discretion.
The receiving command (not a standard term) is the command with which the callback is registered. It usually executes the callback immediately or at a later time (it might in practice arrange for the callback to be executed by some other entity).
When executing a callback script, the executing code will typically use something like eval or uplevel (or other evaluating command), or a command substitution ([{*}$script]). It adds arguments of its own if the script is used as a command prefix, using some variation of, e.g.
eval $script [list $a $b] if <condition> [concat $script [list $a $b]] [{*}$script $a $b]
As the script is executed, any command and variable names in the script that aren't namespace qualified already will get resolved to the best of the interpreter's ability. Name resolution is in itself a predictable procedure, but it is affected by dynamic conditions that might be unknown when writing the program. This makes passing a script as an argument and executing it either wonderfully versatile or frighteningly ambiguous, depending on one's point of view and how clever one tries to be.
namespace path [linsert [namespace path] 0 [namespace parent]]
The raison d′etre for some callback receivers is to introduce a layer of error handling, and the purpose of some callbacks is to prevent or handle errors (e.g. -validatecommand options). The following is about errors in callback scripts.
Handling errors in the callback script itself is a questionable practice: callbacks should have high cohesion (handling errors inside commands called by the callback script is of course still good practice).
Deferred callbacks can lead to errors that may be triggered asynchronously by different kinds of events and don't propagate through the stack: interp bgerror should be your friend.
When writing a receiving command, a basic sanity check on callback scripts can be reasonable, but most generic receiving commands should not presume to assess the wisdom of the callback script. Receivers might still want to be careful with callback scripts, executing them in slave interpreters or by try / catch (an injection attack is basically just a form of callback).
The grandfathered protocol for callbacks is to use only global names, because that was originally the only option. This continues to work both in pre-namespace versions of Tcl and in modern ones, with the caveat (or opportunity) that later, namespace-aware, versions may resolve a name to an unexpected namespace since the global namespace is the last one to be considered during the resolution procedure.