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:
-
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.
-
Third-party providers live in opt-in bridge packages. Core defines
ISomethingProvider(dependency-free, generic types only). The concreteVendorProvider : ISomethingProviderlives 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.
Decision authority
Section titled “Decision authority”| Decision | Default action under this rule |
|---|---|
| About to call a third-party API in core | Block. Define the interface + provider seam first. |
| Third-party import/reference appears in a core package | Block. Move it to an opt-in vendor package. |
| Provider absent at run/build time | Warn + skip (or NoOp); keep the feature degraded-but-alive. Never throw by default. |
| Conditional-compile defines for an asset with no package identity | Reject — prefer the package-split seam over #if VENDOR/define soup. |
| Interface signature leaks a vendor type | Block. Interface uses only platform/first-party types; provider casts internally. |
| Managed-only effect (juice, tween, haptics, analytics) from core logic | Library 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.
Full details
Section titled “Full details”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.
Related
Section titled “Related”- 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.mdfor Unity mechanics.