Skip to content

chore: Remove single-implementation C# interfaces #3569

Description

@reakaleek

What

Remove interfaces that have exactly one implementation and no test mocks — they add indirection with no polymorphism benefit.

Interfaces to remove

Interface Single impl File
IFormatter IrregularSpaceFormatter src/authoring/Elastic.Documentation.Refactor/Formatters/IFormatter.cs
IPagesProvider LocalPagesProvider src/Elastic.Documentation.LegacyDocs/PagesProvider.cs
IHtmxAttributeProvider DefaultHtmxAttributeProvider src/Elastic.Documentation.Site/Htmx.cs
IDescriptionGenerator DescriptionGenerator — every caller already passes new DescriptionGenerator() src/Elastic.Markdown/DescriptionGenerator.cs
IDocumentationFileOutputProvider GlobalNavigationPathProvider — one optional ctor param consumer src/Elastic.Markdown/DocumentationGenerator.cs
ILinkIndexWriter + ILinkIndexReaderWriter AwsS3LinkIndexReaderWriter — keep ILinkIndexReader (2 impls), fold SaveRegistry onto the concrete writer src/Elastic.Documentation.LinkIndex/ILinkIndexWriter.cs
IChangesService ChangesService src/services/search/Elastic.Documentation.Search/IChangesService.cs
IDocumentGateway single impl, one consumer src/api/Elastic.Documentation.Mcp.Remote/Gateways/IDocumentGateway.cs
IAskAiMessageFeedbackService single impl, one consumer src/api/Elastic.Documentation.Api/AskAi/IAskAiMessageFeedbackGateway.cs
IGitRepository SingleCommitOptimizedGitRepository — 9-method interface consumed only by same-file helpers that already new the concrete type src/services/Elastic.Documentation.Assembler/Sourcing/GitFacade.cs
IProductNameLookup ProductsConfiguration — depend on the config record directly src/services/search/Elastic.Documentation.Search.Contract/Common/IProductNameLookup.cs
ISitemapParser, IAdaptiveCrawler, IDocumentExporter<T> one impl each; AddHttpClient<TConcrete> needs no interface src/tooling/essc/LabsCrawl/
IChangelogMarkdownRenderer redundant with MarkdownRendererBase, which all 6 renderers derive from; type the list as List<MarkdownRendererBase> src/services/Elastic.Changelog/Rendering/Markdown/IChangelogMarkdownRenderer.cs

Excluded (correctly polymorphic or test-mocked — do NOT remove):

  • IDocsSyncPlanStrategy / IDocsSyncApplyStrategy — deliberate named strategy pattern
  • IGitHubReleaseService, IGitHubPrService, IGcpIdTokenProvider, IS3EtagCalculator — mocked via A.Fake<> in tests/ / tests-integration/
  • IRepositoryTracker — 2 impls (IntegrationGitRepositoryTracker, LocalGitRepositoryTracker)
  • IParserResolvers — 2 impls (ParserResolvers record, ParserContext)
  • IDocumentInferrerService (2 impls), navigation tree interfaces (INavigationItem, ITableOfContentsItem, etc. — multiple impls)

How

For each interface: delete the interface declaration, update all constructor parameters and DI registrations to reference the concrete type directly. Update any services.AddScoped<IFoo, FooImpl>() to services.AddScoped<FooImpl>().

~180 lines removed across the batch.

Note

Re-audited 2026-07-07: six interfaces originally listed here (IGitHubReleaseService, IGitHubPrService, IGcpIdTokenProvider, IS3EtagCalculator, IRepositoryTracker, IParserResolvers) were false positives — mocked in tests or with a second implementation — and moved to the excluded list. Thirteen newly verified single-impl interfaces were added.

Part of the broader ponytail over-engineering audit tracked in #3574.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions