Update AV2305: Document all public, protected and internal types and members#381
Conversation
Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| **Note:** For internal types and members that are only used within a single assembly, XML documentation is optional. However, a brief summary comment can still be helpful for future maintainers. | ||
|
|
||
| **Note:** You don't need to use `/// <inheritdoc/>` on overriding or implementing members. Visual Studio and Rider will automatically inherit documentation from the base type or interface. |
There was a problem hiding this comment.
Are you sure about that? DocFX certainly doesn't pick them up when emitted. There's an additional pitfall to watch out for; the following is wrong:
/// <summary>A worker</summary>
public interface IWorker;
/// <inheritdoc/>
public class Worker: IWorker;Instead, it needs to be:
/// <inheritdoc cref="IWorker"/>
public class Worker: IWorker;Without the cref, you'll get the documentation for System.Object on Worker.
There was a problem hiding this comment.
Interesting, I didn't know about that. Vanilla VS behaves the same. I know that Roslyn provides richer information when the source is available (in the same solution), so I tried referencing an assembly (with an .xml file next to it, but not a .pdb), and even then, it works for docs on members. But once <GenerateDocumentationFile>True</GenerateDocumentationFile> is added to the project file, it still emits CS1591 warnings.
But apparently, the warnings are by design:
That inheritdoc behavior is purely an IDE-time thing to help with features like quick-info. It's not a feature of the language, and docs are not inherited as far as the compiler and the rules around doc files are concerned. So the compiler is working as intended here.
Also, it doesn't work on types at all.
What API documentation generator is used by FluentAssertions, and does that also show docs on members from the interface without using <inheritdoc/>?
There was a problem hiding this comment.
I never inherit type-level documentation from an interface or base-class. I always write something new as the type is different and usually has a different purpose.
There was a problem hiding this comment.
It's less common, I suppose in depends on the kind of project. For example, a public library with well-documented interfaces for extensibility plus internal default implementations.
There was a problem hiding this comment.
Here's an example:
// lots of docs, describing lifetime, constraints, which errors to raise, etc.
public interface IRepository<T>;
/// <inheritdoc cref="IRepository{T}"/>
internal class ArticleRepository : IRepository<Article>;Can you answer the following?
What API documentation generator is used by FluentAssertions, and does that also show docs on members from the interface without using
<inheritdoc/>?
There was a problem hiding this comment.
Here's an example:
// lots of docs, describing lifetime, constraints, which errors to raise, etc.
public interface IRepository;
///
;
internal class ArticleRepository : IRepository
Even in this example, I really don't see the value of inheriting the documentation from the interface. In this particular case, most of the code only has to deal with the interface and never see the implementation. And for the folks that need to maintain the implementation, they can read the docs from the interface to understand how their implementation should behave.
What API documentation generator is used by FluentAssertions, and does that also show docs on members from the interface without using ?
What generator are you talking about? The .xml files are generated by the dotnet build CLI.
There was a problem hiding this comment.
For example, this page: https://xceed.com/documentation/xceed-fluent-assertions-for-net/FluentAssertions~FluentAssertions.Primitives.GuidAssertions.html. Which tool renders the XML files?
There was a problem hiding this comment.
I have no clue what they use for that. I'm not involved in that process. I can ask if it is really important
There was a problem hiding this comment.
Oh, right. Well, it looks pretty much like docfx with a custom style template, so it likely won't pick up docs from base members if inheritdoc is omitted.
| --- | ||
| Documenting your code allows Visual Studio, [Visual Studio Code](https://code.visualstudio.com/) or [Jetbrains Rider](https://www.jetbrains.com/rider/) to pop-up the documentation when your class is used somewhere else. Furthermore, by properly documenting your classes, tools can generate professionally looking class documentation. | ||
|
|
||
| **Note:** For internal types and members that are only used within a single assembly, XML documentation is optional. However, a brief summary comment can still be helpful for future maintainers. |
There was a problem hiding this comment.
This used to be a solid rule, but now it has become subjective. It depends on how each developer feels about when something is helpful, and making it optional means that some developers will never add them. This defeats the goal of a universal style in the codebase, regardless of who implements it.
The accompanying analyzer flags when internal members are not documented (the other types are already covered by other tools). How is the analyzer supposed to handle the changed rule?
There was a problem hiding this comment.
You're suggesting to remove the note completely?
There was a problem hiding this comment.
I don't have a strong opinion on what should be documented on internal members, but I think the rule should be deterministic. Some options:
- Don't document internal types/members at all
- Internal types/members require at least a summary
- Internal types/members need to be fully documented (summary, type parameters, parameters, and return value)
There was a problem hiding this comment.
For internal types and members that are only used within a single assembly
This implies that when using InternalsVisibleTo (for example, for tests), internal types and members must be fully documented. Is that desired?
There was a problem hiding this comment.
I don't have a strong opinion on what should be documented on internal members, but I think the rule should be > deterministic. Some options:
Don't document internal types/members at all
Internal types/members require at least a summary
Internal types/members need to be fully documented (summary, type parameters, parameters, and return value)
This is a problem for me. I want people to document everything, but only if it adds value. I don't want to be dogmatic about it. Repeating the name of the method in a single-line doc is a waste of effort. And quite often I don't even bother documenting some parameters or return values. E.g. nobody needs to understand what a CancellationToken is or what the purpose is of the returned Task.
There was a problem hiding this comment.
Well, in that case, we remove that note as the consensus seems to be to also document internal types and members. A tech lead or architect can always reduce the level of that rule to suggestion
There was a problem hiding this comment.
I'd like to better understand the tooling impact first, before providing suggestions. As soon as GenerateDocumentationFile is turned on, something must appear to silence compiler warnings: empty summaries or inheritdocs. This enables tracking missing places to document while making it easy and explicit to not document certain places where it doesn't add value. Undocumented new code triggers a warning.
For teams that don't produce a docs website or aren't too serious about documenting, they would typically leave GenerateDocumentationFile off, be fine with a partially documented codebase (and not being alerted when new code isn't documented at all), and rely on the unofficial in-IDE expansions for inheritdocs.
The rule seems to have been created for the former scenario, not the latter.
There was a problem hiding this comment.
I'm a bit lost on what you're trying to achieve. In terms of documentation, it is subjective. I don't think we can make this rule deterministic as it depends on the member involved. If I have to document it myself, I decide on a case-by-case basis, and if I'm using an AI Agent, it is usually more rigid on this.
There was a problem hiding this comment.
Two concerns:
- Whether a type/member needs to be documented is subjective. Don't state the obvious. I agree with that.
- How does that deterministically work in the C# compiler? By turning on
GenerateDocumentationFileand silencing the resulting warnings with either documentation, an empty summary, or inheritdoc.
If this rule didn't mention internal, then no custom analyzer would be needed. Its only purpose is to behave the same as the built-in C# warning for internal types and members. That's how it behaves today, and it works well. Neither requires adding returns and parameter documentation. If desired, there are StyleCop analyzers to enforce that.
So we can make this rule deterministic if you're willing to accept adding empty summaries or inheritdoc as the way to express that no documentation is needed. I still believe that works best.
Therefore, my proposal would be:
- Revert all changes to this PR
- Addition: Turn on
GenerateDocumentationFileto track places to potentially document - Addition: Don't state the obvious; use an empty summary or inheritdoc instead (with example)
- Extra tip (rare cases perhaps): use cref in inheritdoc for interfaces, otherwise it points to
System.Object
Regarding the example:
Prefer:
/// <summary />
public void StartWorker(TimeSpan timeout);Instead of:
/// <summary>Starts the worker.</summary>
/// <param name="timeout">The timeout.</param>
public void StartWorker(TimeSpan timeout);Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Updates guideline AV2305 to better explain the value of functional documentation, and adds guidance/exceptions around when to document members.
Changes:
- Rewords the introductory explanation for documenting types/members.
- Adds a “See also” cross-reference to AV2306.
- Adds an exception for obvious/repetitive documentation and a note about
<inheritdoc/>.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| severity: 2 | ||
| --- | ||
| Documenting your code allows Visual Studio, [Visual Studio Code](https://code.visualstudio.com/) or [Jetbrains Rider](https://www.jetbrains.com/rider/) to pop-up the documentation when your class is used somewhere else. Furthermore, by properly documenting your classes, tools can generate professionally looking class documentation. | ||
| Good functional documentation allows Visual Studio, [Visual Studio Code](https://code.visualstudio.com/) or [Jetbrains Rider](https://www.jetbrains.com/rider/) to display the documentation when your class is used somewhere else. Furthermore, by properly documenting your classes, tools can generate professionally looking class documentation. |
| Documenting your code allows Visual Studio, [Visual Studio Code](https://code.visualstudio.com/) or [Jetbrains Rider](https://www.jetbrains.com/rider/) to pop-up the documentation when your class is used somewhere else. Furthermore, by properly documenting your classes, tools can generate professionally looking class documentation. | ||
| Good functional documentation allows Visual Studio, [Visual Studio Code](https://code.visualstudio.com/) or [Jetbrains Rider](https://www.jetbrains.com/rider/) to display the documentation when your class is used somewhere else. Furthermore, by properly documenting your classes, tools can generate professionally looking class documentation. | ||
|
|
||
| See also [{{ site.default_rule_prefix }}2306](/documentation-guidelines#{{ site.default_rule_prefix }}2306) |
|
|
||
| **Exception:** Sometimes the purpose is so obvious that the documentation would become repetitive. Don't do that. | ||
|
|
||
| **Note:** You don't need to use `/// <inheritdoc/>` on overriding or implementing members. Visual Studio and Rider will automatically inherit documentation from the base type or interface. |
* Make sure the site works properly with Ruby 3.1 * Add AV0100: Understand the boundaries of your codebase (#300) * Add AV0105: Use design patterns to communicate intent (#301) * Add AV0110: Prefer composition over class inheritance (#302) * Add AV0110 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/0110.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Add AV0112: Apply the Principle of Least Surprise (#303) * Add AV0115 guideline (#304) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV0120: You Ain't Gonna Need It (YAGNI) (#305) * Add AV0120 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/0120.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Update _rules/0120.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Update _rules/0120.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Add AV0125: Don't Repeat Yourself (DRY) within boundaries (#306) * Update explanation of code boundaries and coupling Clarified the reasoning for preventing unnecessary coupling between components. * Add AV0135 guideline (#308) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1582 guideline (#318) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1608 guideline (#323) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1610 guideline (#324) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1708 guideline (#372) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1562 guideline (#368) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1800 guideline (#375) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1521 guideline (#361) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1520 guideline (#360) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1140 guideline (#356) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove AV1530: Don't change a loop variable inside a `for` loop or a collection in a `foreach` loop (#363) * Update AV1530 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review comments on AV1530: update title and add foreach collection example Co-authored-by: dennisdoomen <572734+dennisdoomen@users.noreply.github.com> Agent-Logs-Url: https://github.com/dennisdoomen/CSharpGuidelines/sessions/983488b6-53b6-4360-876c-4576219b2d4f * Remove AV1530 rule file entirely Co-authored-by: dennisdoomen <572734+dennisdoomen@users.noreply.github.com> Agent-Logs-Url: https://github.com/dennisdoomen/CSharpGuidelines/sessions/0ce85de6-5278-491a-87c7-607ef017b67b --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dennisdoomen <572734+dennisdoomen@users.noreply.github.com> * Update AV1250 guideline (#358) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1115 guideline (#352) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1025 guideline (#351) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1020 guideline (#350) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1011 guideline (#348) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1010 guideline (#347) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1003 guideline (#345) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1618 guideline (#326) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1615: Prefer inline literals over constant variables in tests (#325) * Add AV1615 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/1615.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Update AV1835 guideline (#378) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1825 guideline (#377) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update AV1000: A class or interface should have a single purpose (#344) * Update AV1000 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/1000.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Remove AV2235 guideline (#342) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1602: Postfix test classes with `Specs` instead of `Tests` (#321) * Add AV1602 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rename nested class Order_placement to OrderPlacement Renamed nested class for better readability. --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add AV1605: Test behavior, not implementation details (#322) * Add AV1605 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/1605.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Add General Guidelines page (#385) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add Testability Guidelines page (#386) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update Links & Articles page (#387) * Update C ov er An dS ty le s page (#384) * Update AV2400 guideline (#382) * Split minor site updates from PR 298 (#383) * Add AV1600 guideline (#320) * Add AV1578 guideline (#317) * Update AV1706: Don't use abbreviations (#371) * Update AV1701 guideline (#370) * Update AV1755: Only use `Async` or `TaskAsync` as a suffix when a method has both synchronous and asynchronous versions (#374) * Clarify suffix usage for Async methods (#393) Follow-up for #374, to align with the replacement of "version" with "variant". * Update AV2305: Document all `public`, `protected` and `internal` types and members (#381) * Update AV2202: Prefer language syntax over explicit calls to underlying implementations (#379) * Update AV1820: Only use `async` for I/O-bound or long-running activities (#376) * Remove AV1580 guideline (#392) * AV0125: Consider duplicating simple logic across modules to reduce coupling (#369) * Update AV1554 guideline (#367) * Update AV1553 guideline (#366) * Update AV1546 guideline (#365) * Update AV1545: Don't use an `if`-`else` construct instead of a simple (conditional) assignment (#364) * Update AV1523 guideline (#362) * Update AV1500: Methods should not exceed 15 statements (#359) * Remove AV1230 guideline (#357) * Update AV1137: Keep parameters as specific and narrow as possible (#355) * Update AV1130: Return interfaces to unchangeable collections (#354) * Update AV1125: Don't expose stateful objects through static members (#353) * Update AV1013: Don't cast a base class to one of its derived classes (#349) * Add LSP example with polymorphism to AV1011 (#396) * Update AV1004: Use an interface rather than a base class to support multiple implementations (#346) * Remove AV2307 guideline (#343) * Remove AV2221 guideline (#341) * Remove AV2201 guideline (#339) * Remove AV1738 guideline (#338) Split from #298. Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove AV1737 guideline (#337) * Remove AV1568 guideline (#336) * Remove AV1532: Avoid nested loops (#334) * Remove AV1525 guideline (#333) * Remove AV1510 guideline (#332) * Remove AV1220 guideline (#331) * Add AV2308: Document what a member tries to do, not what it does or how it does it (#330) * Add AV2225: Use deconstruction to simplify variable assignments (#329) * Add AV1622: Test concrete implementations as part of a larger integration scope (#328) * Add AV1622 guideline Split from #298. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update _rules/1622.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Add AV1620: Test reusable components separately from their consumers (#327) * Add AV1585: Make properties required when they must be set during initialization (#319) * Add AV1155: Use the `field` keyword in auto-properties when additional logic is needed (#316) * Add AV1150: Avoid local functions (#315) * Add AV1145: Use extension members to add behavior without modifying the original type (#314) * Add AV1035: Use primary constructors when they improve readability (#313) * Add AV1032: Consider a delegate instead of an interface with a single method (#312) * Add AV1030: Know when to use a record and when to use a class (#311) * Add AV1002: Only pass things to a constructor that most or all members need (#309) * Remove AV2207 guideline (#340) * Add AV0130: Apply the three pillars of object-oriented programming (#307) * Fix c# fencing to use csharp in assertion-comparison.html Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove severity field from all rules The severity rating is no longer used. This removes: - The severity front matter field from all 130 rule files - The severity icon <img> from the rule-category layout - The unused severity images (1.png, 2.png, 3.png) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Dennis Doomen <dennis.doomen@greenchoice.nl> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dennisdoomen <572734+dennisdoomen@users.noreply.github.com>

This PR updates guideline AV2305.
It was split out of #298 so the change can be reviewed independently.
Files:
Part of the replacement for #298.