Steeltoe
Steeltoe copied to clipboard
Public API Surface Area Review
Steeltoe has a broad surface area of public APIs, probably broader than necessary which can hamper project progress. This epic will track efforts to reduce what is (unnecessarily) public for Steeltoe 4.0.
Considerations
- Review namespaces
- Consider the use of pubternal
- Adjust namespaces based on directory (or adapt project structure to match namespaces)
- ~Don't put extensions methods in System namespaces. While this makes them easy to discover, sooner or later they are going to clash with other libraries, requiring users to write non-intuitive code to workaround the conflict~ (already done solution-wide)
- ~Remove obsolete types and members~ (already done solution-wide)
- Consider making public types/members internal, use
InternalsVisibleToto access them from tests - Seal public types, unless they are designed for inheritance
- Avoid static classes, they are hard to test. Exceptions: extension method containers or classes to wrap a set of immutable constants
- A class or interface should have a single purpose
- A class or interface should have a single purpose within the system it functions in. In general, a class either represents a primitive type like an email or ISBN number, an abstraction of some business concept, a plain data structure, or is responsible for orchestrating the interaction between other classes. It is never a combination of those. This rule is widely known as the Single Responsibility Principle, one of the S.O.L.I.D. principles
- Similarly, a property, method or local function should do only one thing
- Consider adding doc-comments to public types and members, but avoid undocumentation
- Public types should protect the consistency of their internal state: validate inputs and use guards like
AssertNotDisposed(); - Use a method instead of a property when:
- The work is more expensive than setting a field value
- It represents a conversion such as the
Object.ToStringmethod - It returns a different result each time it is called, even if the arguments didn’t change. For example, the
NewGuidmethod returns a different value each time it is called - The operation causes a side effect such as changing some internal state not directly related to the property (which violates the Command Query Separation principle). Populating an internal cache or implementing lazy-loading is a good exception
- Suffix identifier names that represent a
Type,MethodInfo,PropertyInfoetc, to distinguish them from instances. For example: renameMethodInfo connecttoMethodInfo connectMethod - Avoid publicly exposed fields, unless they are
constorstatic readonly. Convert to (auto-)properties or make them private/internal - Prefer collection interfaces such as
ICollection<>andIDictionary<,>over arrays, list and dictionaries for inputs and outputs- Be hesitant with incoming
IEnumerable<>parameters: the implementation may change over time, causing the need for multiple enumerations, which may result in undesired side effects - Consider to return unchangeable collections, such as
IReadOnlyCollection<>,IReadOnlyDictionary<,>andIImmutableList<T>
- Be hesitant with incoming
- Use overloads instead of optional parameters (they have several backwards-compatibility issues, as well as non-intuitive behavior when using interfaces or inheritance). Call the more overloaded method from other overloads
- Properties, arguments and return values representing strings, collections or tasks should never be null. Return an empty collection, an empty string or
Task.CompletedTask/Task.FromResult() - Don’t use ref or out parameters (return compound objects or tuples), unless the method implements the TryParse pattern
- ~Postfix asynchronous methods with Async or TaskAsync.~ (already done solution-wide)
- Consider using
ValueTask/ValueTask<T>in high-performance code paths - Avoid abbreviations
- Use "management" instead of "mgmt", "options" instead of "opts", etc.
- Using "config" in the context of ConfigServer is good, but not:
IConfiguration config - Avoid single character variable names, such as i or q. Use index or query instead
- Use well-known acronyms and abbreviations that are widely accepted or well-known in your work domain. For instance, use acronym UI instead of UserInterface and abbreviation Id instead of Identity
- Remove the "Core" suffix when referring to .NET (ASP.NET Core and Entity Framework Core are okay)
While you're at it, consider annotation for NRT (see #962 for tips).