- Class-level and member-level documentation helps you understand what behavior to expect without having to jump into the actual implementation details. It should also explain the responsibilities and the roles the class plays in the overall design, or the member within the class. This kind of documentation is not a replacement for proper naming. Both are crucial, and one should reinforce the other. So always document the expected behavior of all your public and protected members. But, don't add documentation that repeats the name of the member. I'd rather have no documentation at all than that kind of useless crap.
- Inline comments, which you should use scarcely, are there to help you understand the thought process the original developer went through, to point you in the right direction, or to emphasize a particular non-trivial design decision. I use the word scarcely purposely, because in most cases, refactoring the code can help you avoid the need to have in-line comments. For instance, wrapping a complex expression in a method which name explains the goal of the algorithm can already make your code a lot easier to understand. Robert C. Martin's book Clean Code contains numerous of these refactorings.
- Commit comments should not only explain the nature of the change, but more importantly, why a particular change was needed in the first place. When a lot of people are involved, it becomes pretty important to be able to see why something that happened a long time ago happened. Yeah, an internal blog can help, but ultimately, it’s the source control history that will contain the really interesting changes. I've seen numerous occasions where I noticed some code that seem to be superfluous, but ended up being quite critical. The commit message for that change helped me understand why. Unfortunately I've also seen less helpful commit messages requiring me to figure out the reason the hard way.
In my opinion, these three kinds of documentation are required for every professional code base.