Skip to content

library-third-party-decoupling

Library Third-Party Decoupling — Interface + Provider From Day One

Section titled “Library Third-Party Decoupling — Interface + Provider From Day One”

Status: kit-wide, engine-agnostic, always-loaded, decision-authority rule. Applies to EVERY TheOneKit engine (Unity/DOTS, Cocos, React Native, web, Nakama, any future kit). The operational arm of the library-quality mandate for third-party (non-first-party, non-platform-stdlib) dependencies. Future sessions act on this WITHOUT re-asking the user.

Homing: the principle below is core (theonekit-core). Engine-specific mechanics (how a “package”, “reference”, “provider discovery” map to each stack) live in the per-engine table + each engine kit’s reference doc. Do not fork the principle per kit — extend the table.

No core library package may hard-reference a third-party asset/library (anything not shipped by the platform stdlib or first-party/The1Studio). A consumer MUST be able to remove the third-party dependency and still build and run the library.

Two non-negotiables, on every engine:

  1. Design the seam FIRST, never couple directly. The moment you reach for a third-party API (a paid Unity asset, an npm package, a native module, a SaaS SDK), STOP and define a library-owned interface the core depends on. The third-party call lives behind a provider implementing that interface. This applies from the first line — do NOT write the direct call “for now” intending to extract later. Extract-later is tech debt in disguise.

  2. Third-party providers live in opt-in bridge packages. Core defines ISomethingProvider (dependency-free, generic types only). The concrete VendorProvider : ISomethingProvider lives in a separate opt-in package/module that references the third-party dependency, discovered via a registry / DI / dynamic-import over the library’s OWN interface (core never names the vendor type). Consumers add it ONLY if they own/want the dependency.

Graceful degradation when no provider is present: warn + skip (tooling / one-shot ops) or a NoOp null-object (per-frame / hot-path seams). Never throw by default, never hard-block, never silently swallow without a logged signal at registration time.

DecisionDefault action under this rule
About to call a third-party API in coreBlock. Define the interface + provider seam first.
Third-party import/reference appears in a core packageBlock. Move it to an opt-in vendor package.
Provider absent at run/build timeWarn + skip (or NoOp); keep the feature degraded-but-alive. Never throw by default.
Conditional-compile defines for an asset with no package identityReject — prefer the package-split seam over #if VENDOR/define soup.
Interface signature leaks a vendor typeBlock. Interface uses only platform/first-party types; provider casts internally.
Managed-only effect (juice, tween, haptics, analytics) from core logicLibrary owns the event boundary + I*Bridge seam; the vendor impl is consumer-side.

Narrow exceptions: platform-stdlib / engine-shipped / first-party deps are not “third-party” (hard refs fine); build-only one-offs never shipped to consumers; test-only targets gated from the runtime build.

Canonical engine-neutral pattern, per-engine mechanics map (Unity/Cocos/RN/Web/generic), the 5 objective pass-tests, the Known-Non-Conformant honesty/backlog clause, anti-patterns, and history: docs/library-third-party-decoupling.md.

  • library-quality mandate — Great Lib, Zero Tech Debt; this rule is its third-party arm.
  • development-principles.md § “Errors Over Silent Fallbacks” — warn+skip / NoOp are the documented, logged, surfaced fallbacks this allows; not silent swallows.
  • Skill: t1k-library-decoupling — the engine-agnostic how-to; references/unity-dots.md for Unity mechanics.