The term "API-first" became a rallying cry for modern app development — design your API contract, build the mobile client around it, ship fast. But for complex apps that have survived 3+ years in production, API-first often means tightly coupled, hard to test, and increasingly fragile as backends evolve.
The Harness Pattern Defined
A "harness" is an abstraction layer that wraps every external dependency your app relies on — REST APIs, Firebase, the camera, GPS, payment SDKs, AI models. Each harness exposes a domain interface your app code calls. The implementation behind that interface can be swapped without touching business logic. Your ViewModel never knows if it's talking to a real API, a local Room cache, or a test fake.
Why This Matters More Than Ever in 2026
Three forces make harnesses essential: (1) AI augmentation — you want to swap an API call for an on-device AI inference without touching business logic. (2) Offline-first — the harness decides whether to hit the network or the local cache. (3) Testing — with a harness, every UseCase can be unit-tested without a running server, emulator, or network.
Implementing a Repository Harness in Kotlin
Define an interface: interface UserRepository { suspend fun getUser(id: String): User }. Create two implementations: NetworkUserRepository (hits the API) and LocalUserRepository (reads Room). A HarnessUserRepository wraps both: try Network, on failure return Local, queue sync. Inject via Hilt — different implementations per build variant or network state.
The AI Augmentation Layer
Add a third implementation: AIUserRepository, which uses an on-device model to predict/prefetch user data before the network responds. Because your ViewModel only knows the interface, you can compose NetworkUserRepository + AIUserRepository through a SmartUserRepository that returns AI-predicted data immediately and then reconciles with the network response — without touching a single line of UI code.
When NOT to Use a Harness
Don't wrap everything. Simple screens that display read-only data from a single stable API don't need harness overhead. Apply harnesses to: core domain operations (orders, payments, user state), any dependency that could be replaced by an AI model, and anything you need to fake in tests. Keep it proportional to complexity.