Toolset · Kotlin, Coroutines, Jetpack Compose
Android
- Kotlin
- Jetpack Compose
- Coroutines
The modern Android stack is Kotlin-first, coroutine-driven, and Compose for UI. These are the libraries we standardise on for a production app that has to survive lifecycle churn, concurrency, and background work.
Where two tools compete, the one marked our default is what we reach for.
Build & dependencies
1 toolThe build system and dependency manager for Android; use the Kotlin DSL and a version catalog (libs.versions.toml) to keep dependencies declarative and centralised.
Dependency injection
2 toolsPragmatic, runtime DI with no annotation processing — fast builds, easy to read, low learning curve. Our default for most apps.
val appModule = module {
single { ApiClient(get()) } // singleton
factory { UserRepository(get()) } // fresh instance each time
viewModel { UserViewModel(get()) }
}
startKoin { modules(appModule) }
// inject: private val repo: UserRepository by inject()Compile-time DI over Dagger with full graph validation — the compiler verifies the entire dependency graph. Reach for it when the team is large (>5 engineers), the codebase is heavily modularised (multiple feature modules), and compile-time safety of the whole graph is worth the build-time cost. Decision rule: start with Koin; move here only when that build-time safety outweighs the build-time cost and complexity.
Networking
3 toolsType-safe HTTP client over OkHttp; declare the API as an interface, get coroutines support and pluggable serialization. Our default.
interface Api {
@GET("users/{id}")
suspend fun user(@Path("id") id: String): User
}
val api = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(KotlinSerializationConverterFactory.create())
.build()
.create(Api::class.java)The HTTP engine under Retrofit; add interceptors for auth, logging, and — importantly — certificate pinning.
Kotlin-native, multiplatform HTTP client; the pick when the app is Kotlin Multiplatform.
Push notifications
1 toolStandard push delivery — device registration, message delivery, and lifecycle integration; required for any app that notifies users. Handle payloads in a FirebaseMessagingService, keep the receiver light (offload heavy work to WorkManager, never block in onMessageReceived), and drive targeted campaigns from the Firebase Console or the HTTP v1 API. Our default.
Async & concurrency
1 toolStructured concurrency and cold reactive streams — the backbone of async on Android. Scope work to lifecycles; never leak a job across a config change.
Local storage
2 toolsSQLite with compile-time-checked queries and Flow observation. Our default for structured local data — reach for it when the data is relational, queryable, large, or complex.
Typed, coroutine-based key-value / preferences store; the modern replacement for SharedPreferences. Use it for small, simple values — preferences, settings, a small JSON blob. Decision rule: simple key-value → DataStore; relational, queryable, or complex → Room.
Security
2 toolsHardware-backed secure storage for tokens and secrets — never plaintext columns (see the mobile-credential blueprint). The right home for auth tokens and short key-value secrets.
Google's well-audited, multi-platform crypto library with safe, misuse-resistant high-level APIs — encrypt arbitrary data (not just preferences), sign and verify payloads, and manage keys correctly. Reach for it for any app handling sensitive data beyond simple tokens; it removes most of the ways to get cryptography wrong. Our default for application-level encryption.
Serialization
2 toolsOur default — Kotlin-native, so it works seamlessly with data classes via @Serializable; reflection-free, avoiding the runtime-reflection pitfalls that bite on Android; and it pairs cleanly with Retrofit through its converter factory.
Square's JSON library with codegen adapters — battle-tested. Remains a solid option for existing codebases or when you need complex custom adapters.
UI & navigation
3 toolsDeclarative UI toolkit — our default for all new screens. Mind recomposition and hoist state.
Type-safe navigation graph for Compose destinations.
Coroutine-based image loading for Compose; lightweight and Kotlin-first.
Testing
3 toolsUnit tests with a Kotlin-first mocking library; MockK handles coroutines and final classes cleanly.
Testing library for asserting on Flow emissions.
Instrumented UI tests on-device.
Monitoring & observability
6 toolsReal-time crash reporting with stack traces, breadcrumbs, and impact grouping. Our default for crash monitoring.
Crashes, ANRs, performance traces, and release health in one product — the pick when you want app and backend errors under one vendor.
Automatic and custom traces for cold start, network calls, and slow/frozen frames.
Automatic memory-leak detection in debug builds — catches leaked activities, fragments, and view models before they ship. Our default.
A tiny logging façade; plant a release tree that forwards to Crashlytics/Sentry instead of writing to logcat in production.
In-app HTTP(S) inspector for debug builds — every request and response on-device via an OkHttp interceptor.
CI/CD
4 toolsBuild, test, and lint on every push; cache Gradle for fast runs. Our default CI.
Automate builds, code signing, and Play Store uploads. Our default for release automation.
Push signed builds to testers before store rollout.
Run instrumented tests across real devices in CI.
Linting & formatting
4 toolsKotlin linter and formatter with sane defaults — ends style debates. Our default.
Static analysis for Kotlin — code smells, complexity, and potential bugs. Our default.
Built-in checks for Android-specific correctness, performance, and API misuse.
Gradle plugin that enforces (and applies) formatting in CI.
Building on Android?
We ship production Androidwith exactly this stack. Tell us what you're building.
Start a conversation