Should comments explain how the application control flow works? For example, the relationships between classes, how this function is used by other functions, and what we are trying to do with these objects and processes? I suppose what I'm asking is should a person be able to understand how an application works simply from reading your comments and walking through the code? Or is this something we mainly do with other external documentations outside of the code?
In my opinion, comments should explain anything that cannot be made self-evident by the actual code, and any detailed external documentation you need should be generated from those comments (presumably as part of your automated build/deployment system).
Clarifying what a class or function should be used for, what problems it was created to solve, and where it fits into the program as a whole probably are things worth commenting on. Just make sure the comment is right on top of whatever it's talking about, and it's no longer than it needs to be, since we want to make it easy for future devs to keep code and comments in sync. An outdated comment is usually worse than none at all.
"Control flow" normally means things like using if statements and for loops, which should almost never need comments, but it sounds like what you're trying to ask about is more like a big comment on top of main() that summarizes what top-level functions get called in what order and why; that would be fine if you can ensure it stays up to date (which is easier said than done).
For the very, very high-level conceptual view of how your program works, you probably should rely on a short hand-written file outside the source code (such as the README.md file on a Github repo). But once people know that basic information, yes they should be able to dive into any part of the source tree and figure out what's going on from just the code and comments they see in front of them.
If people cannot read the code and understand what the code is doing, that is a sign that your code is poorly designed, overly complex, or poorly named (or some combination thereof). Comments don't fix those problems - they only exacerbate them.
Use comments to describe why code is doing weird things.
More conventional documentation is better for describing the wider architecture/application.
Comments should not explain how the code works. The code is there to explain how it works. If the implementation is changed, the comment becomes incorrect.
Mostly the comments should explain What the purpose of the code is. This allows you to change the implementation over time without invalidating the comment. Changing the purpose of the code is rarely a good idea.
For public interfaces you may want to provide machine extractable documentation which explains:
- What its purpose is and what is returned.
- Describe purpose and valid values are for each parameter.
- Possibly explain how the functionality is expected to / might be used.
There are various tools that extract documentation from contents. Java has javadoc, which extracts certain comments and build a documentation tree for a package of classes.
Good names can be sufficient documentation. Avoid names which explain how functionality is implemented.
If the code has been optimized, it may be appropriate to include a comment explaining why the code was changed and what the results where. This should reduce the chance that the code is refactored to remove the optimization. Likewise, if a particular algorithm is required, a comment explaining the requirement would be appropriate. These two comments may be at odds as a more accurate algorithm may be a slower algorithm.
Comments on bug fixes should be in the version control. These should explain why the change was required/done rather than how it was implemented.