In general, the stdout stream is a potential candidate for data IO in AVRDUDE. If a filename in the -U option is given as -, it will internally be replaced by using the stdout stream on output. This can be useful to e.g. verify fuse settings within a Makefile, and it is potentially also usable by various non-integrated GUI frontends that exist.
This usage of stdout requires that AVRDUDE itself does never use that stream for any status or informational messages etc. Therefore, all messages of any kind go to stderr instead.
There are two possible exceptions from this rule:
Debugging output that is not enabled by default, but requires a particular #define or -D compiler option, so it is only used by software developers and never by end-users.
Output in environments where it is safe to assume file data could never go to stdout. The only example by now is witihin terminal mode.
All backend modules, i.e. everything that eventually goes into libavrdude.a must not call exit() directly. The library could be potentially used by a future GUI frontend, and the GUI environment might get fairly upset about a library module suddenly exiting upon encountering a backend problem. Also, more sophisticated frontends than the current CLI frontend might not immediately want to give up in case of a problem, but could offer the user to start over.