Steeltoe icon indicating copy to clipboard operation
Steeltoe copied to clipboard

Public API Surface Area Review

Open TimHess opened this issue 3 years ago • 1 comments

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.

TimHess avatar Jul 27 '22 15:07 TimHess

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 InternalsVisibleTo to 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.ToString method
    • It returns a different result each time it is called, even if the arguments didn’t change. For example, the NewGuid method 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, PropertyInfo etc, to distinguish them from instances. For example: rename MethodInfo connect to MethodInfo connectMethod
  • Avoid publicly exposed fields, unless they are const or static readonly. Convert to (auto-)properties or make them private/internal
  • Prefer collection interfaces such as ICollection<> and IDictionary<,> 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<,> and IImmutableList<T>
  • 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).

bart-vmware avatar Jul 28 '22 12:07 bart-vmware