Borg
A powerful, coroutine-based dependency initialization orchestrator for Android applications. Borg ensures your components are initialized in the correct order, with maximum parallelization and bulletproof thread safety.
Install / Use
/learn @kibotu/BorgREADME
Borg 🤖
Resistance is futile - your components will be assimilated in perfect order.
🚀 About Borg is a high-performance Android initialization orchestrator that brings order to your app's startup chaos. It automatically manages complex dependency graphs, parallelizes independent initializations, and provides bulletproof thread safety - all with the elegance of Kotlin coroutines.
Check out the article on Medium

Table of Contents 📑
- Why Borg?
- Key Features
- Installation
- Quick Start
- Advanced Usage
- Best Practices
- Error Handling
- Testing
- Comparison with Alternatives
- Contributing
- License
Why Borg? 🤔
Modern Android apps face complex initialization challenges:
Common Problems
- ❌ Race conditions in component initialization
- ❌ Unclear dependency ordering
- ❌ Blocking main thread during setup
- ❌ Hard to test initialization logic
- ❌ Difficult error recovery
- ❌ Poor performance from sequential initialization
Borg's Solutions
- ✅ Thread-safe, deterministic initialization
- ✅ Automatic dependency resolution
- ✅ Non-blocking coroutine-based setup
- ✅ Easy to test with constructor injection
- ✅ Structured error handling
- ✅ Maximum parallel initialization
Key Features 🌟
- Type-Safe Dependencies: Compile-time verification of dependency graph
- Parallel Initialization: Automatic parallelization of independent components
- Coroutine Support: Native suspend function support for async operations
- Thread Safety: Bulletproof concurrency handling with deadlock prevention
- Error Handling: Rich exception hierarchy with detailed context
- Testing Support: Easy mocking and testing through constructor injection
- Performance: Optimal initialization order with parallel execution
- Flexibility: Generic context type for any initialization needs
Installation 📦
Maven Central
allprojects {
repositories {
mavenCentral()
}
}
dependencies {
implementation 'net.kibotu:Borg:{latest-version}'
}
JitPack (Alternative)
- Add JitPack repository:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
maven { url = uri("https://jitpack.io") }
}
}
- Add the dependency:
// build.gradle.kts
dependencies {
implementation("com.github.kibotu:Borg:latest-version")
}
Quick Start 🚀
1. Define Your Components
Create a drone for each component that needs initialization:
// 1. Simple configuration
class ConfigDrone : BorgDrone<AppConfig, Context> {
override suspend fun assimilate(context: Context, borg: Borg<Context>) =
AppConfig.load(context.assets.open("config.json"))
}
// 2. Database with config dependency
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
override fun requiredDrones() = listOf(ConfigDrone::class.java)
override suspend fun assimilate(context: Context, borg: Borg<Context>): AppDatabase {
val config = borg.requireAssimilated(ConfigDrone::class.java)
return Room.databaseBuilder(context, AppDatabase::class.java, config.dbName)
.build()
}
}
// 3. Repository combining multiple dependencies
class RepositoryDrone : BorgDrone<Repository, Context> {
override fun requiredDrones() = listOf(
DatabaseDrone::class.java,
ApiDrone::class.java
)
override suspend fun assimilate(context: Context, borg: Borg<Context>) = Repository(
database = borg.requireAssimilated(DatabaseDrone::class.java),
api = borg.requireAssimilated(ApiDrone::class.java)
)
}
2. Initialize the Collective
class App : Application() {
private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
override fun onCreate() {
super.onCreate()
lifecycleScope.launch {
try {
// Create and initialize the collective
val borg = Borg(setOf(
ConfigDrone(),
DatabaseDrone(),
ApiDrone(),
RepositoryDrone()
))
// Assimilate all components
borg.assimilate(applicationContext)
// Store initialized components
appContainer.repository = borg.requireAssimilated(RepositoryDrone::class.java)
} catch (e: BorgException) {
handleInitializationError(e)
}
}
}
}
Advanced Usage 🔧
Parallel Initialization
Borg automatically parallelizes initialization of independent components:
val drones = setOf(
AnalyticsDrone(), // No dependencies - Parallel
ConfigDrone(), // No dependencies - Parallel
DatabaseDrone(), // Depends on Config - Waits for Config
ApiDrone() // Depends on Config - Waits for Config
)
// Visualization of parallel execution:
// Time →
// Analytics ▓▓▓▓▓▓▓
// Config ▓▓▓▓
// Database ▓▓▓▓ (starts after Config)
// Api ▓▓▓▓ (starts after Config)
Handling Optional Dependencies
Use getAssimilated() for optional dependencies:
class AnalyticsDrone : BorgDrone<Analytics, Context> {
override fun requiredDrones() = listOf(UserDrone::class.java)
override suspend fun assimilate(context: Context, borg: Borg<Context>): Analytics {
val analytics = FirebaseAnalytics.getInstance(context)
// Optional user identification
borg.getAssimilated(UserDrone::class.java)?.let { user ->
analytics.setUserId(user.id)
}
return analytics
}
}
Fallback Handling
Implement graceful degradation:
class RemoteConfigDrone : BorgDrone<Config, Context> {
override suspend fun assimilate(context: Context, borg: Borg<Context>): Config {
return try {
// Try remote config first
FirebaseRemoteConfig.getInstance()
.fetchAndActivate()
.await()
.let { RemoteConfig() }
} catch (e: Exception) {
// Fall back to local config
LocalConfig.fromAssets(context)
}
}
}
Enabling Logging 📝
To enable logging in Borg, you can use the enableLogging parameter in the Borg constructor. This will automatically set up logging for all Borg operations.
Example:
val borg = Borg(
drones = setOf(
ConfigDrone(),
DatabaseDrone(),
ApiDrone(),
RepositoryDrone()
),
enableLogging = true
)
By setting enableLogging to true, Borg will log important lifecycle events and errors, helping you to monitor and debug the initialization process effectively.
You can still configure the logging level and customize the logger as needed, but the enableLogging parameter provides a quick and easy way to get started with logging in Borg.
Best Practices 💡
1. Keep Drones Focused
// ❌ Bad: Too many responsibilities
class MonolithicDrone : BorgDrone<AppServices, Context> {
override suspend fun assimilate(context: Context, borg: Borg<Context>): AppServices {
val db = Room.databaseBuilder(/*...*/).build()
val api = Retrofit.Builder().build()
val analytics = FirebaseAnalytics.getInstance(context)
return AppServices(db, api, analytics)
}
}
// ✅ Good: Single responsibility
class DatabaseDrone : BorgDrone<AppDatabase, Context> {
override suspend fun assimilate(context: Context, borg: Borg<Context>) =
Room.databaseBuilder(context, AppDatabase::class.java, "app.db").build()
}
2. Handle Errors Gracefully
class ApiDrone : BorgDrone<ApiClient, Context> {
override fun requiredDrones() = listOf(ConfigDrone::class.java)
override suspend fun assimilate(context: Context, borg: Borg<Context>): ApiClient {
try {
val config = borg.requireAssimilated(ConfigDrone::class.java)
// Validate configuration
require(config.apiUrl.isNotBlank()) { "API URL is required" }
return ApiClient(config.apiUrl)
.also { client ->
// Verify connectivity
client.ping()
.await()
.also { response ->
require(response.isSuccessful) {
"API not reachable: ${response.code()}"
}
