I'll use ffmpeg as an example of being an edge case. It's hard to get ffmpeg to give a nonzero exit code. What might be a problem for the user wasn't necessarily a problem for the app, so the app thinks it is completed and does its thing exiting with zero. For example, if a file is being read as input that is corrupted causing ffmpeg to no longer be able to read from the source, it will happily close your file cleanly so it is usable (just shorter than expected) and report it completed successfully. If all you do is check the exit code, you'll think your file is completed. Much more due diligence is necessary to be sure.
If one wants to use a pager (like I sometimes do, though most of the time I just scroll up), they'll just use `foo 2>&1 | less`.
If user asks a program to print help message, the help text is the processed command output!
1: https://www.gnu.org/prep/standards/html_node/_002d_002dhelp....
In the case of `git diff | grep FOO`, the diff output should go to stdout.
In the case of `git --help | grep FOO` the help output should go to stdout.
In the case of `git --omg-wtf | grep FOO`, it's fine if there is only output on stderr.