Flagship ← Back to Documentation

Flagship Logo

🔄 Migration Guide

Guide for migrating from other solutions to Flagship.


📋 Table of Contents

1. Migration from Firebase Remote Config

2. Migration from LaunchDarkly

3. Migration from Split.io

4. Migration from Custom Solution


Migration from Firebase Remote Config

Before (Pure Firebase)

// Initialization
val remoteConfig = Firebase.remoteConfig
remoteConfig.setConfigSettingsAsync(
    remoteConfigSettings {
        minimumFetchIntervalInSeconds = 3600
    }
)

// Get values
val newFeatureEnabled = remoteConfig.getBoolean("new_feature")
val apiTimeout = remoteConfig.getLong("api_timeout").toInt()

// Fetch
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
    if (task.isSuccessful) {
        // Updated
    }
}

After (Flagship + Firebase Provider)

// Initialization
val config = FlagsConfig(
    appKey = "my-app",
    environment = "production",
    providers = listOf(
        FirebaseRemoteConfigProvider(
            remoteConfig = Firebase.remoteConfig,
            fetchIntervalSeconds = 3600
        )
    ),
    cache = PersistentCache(FlagsSerializer())
)
Flags.configure(config)

// Get values (suspend functions - use in coroutine scope)
val manager = Flags.manager()
lifecycleScope.launch {
    val newFeatureEnabled = manager.isEnabled("new_feature")
    val apiTimeout: Int = manager.value("api_timeout", default = 5000)
}

// Fetch (automatic with coroutines)
lifecycleScope.launch {
    manager.refresh()
}

Benefits

Type-safe API - no manual type casting

Kotlin Coroutines - instead of callbacks

Offline-first - automatic caching

Multi-provider - add REST fallback

A/B Testing - built-in experiment support


Migration from LaunchDarkly

Before (LaunchDarkly)

val client = LDClient.init(application, ldConfig, ldUser)

// Feature flag
val showNewUI = client.boolVariation("show-new-ui", false)

// Experiment
val buttonColor = client.stringVariation("button-color-test", "blue")

After (Flagship)

// Initialization
val config = FlagsConfig(
    appKey = "my-app",
    environment = "production",
    providers = listOf(
        RestFlagsProvider(httpClient, "https://your-backend.com/flags")
    ),
    cache = PersistentCache(FlagsSerializer())
)
Flags.configure(config)

// Feature flag
val manager = Flags.manager()
// Note: All flag access methods are suspend functions
lifecycleScope.launch {
    val showNewUI = manager.isEnabled("show-new-ui")
    
    // Experiment
    val assignment = manager.assign("button-color-test")
    val buttonColor = assignment?.payload["color"]?.jsonPrimitive?.content ?: "blue"
}

Migration from Split.io

Before (Split.io)

val factory = SplitFactoryBuilder.build("YOUR_SDK_KEY", applicationContext)
val client = factory.client()

// Feature flag
val treatment = client.getTreatment("user123", "new_checkout")
if (treatment == "on") {
    // Show new checkout
}

After (Flagship)

val config = FlagsConfig(
    appKey = "my-app",
    environment = "production",
    providers = listOf(
        RestFlagsProvider(httpClient, "https://your-backend.com/flags")
    ),
    cache = PersistentCache(FlagsSerializer())
)
Flags.configure(config)

val manager = Flags.manager()
val ctx = EvalContext(userId = "user123")

// Feature flag
// Note: isEnabled is a suspend function
lifecycleScope.launch {
    if (manager.isEnabled("new_checkout")) {
        // Show new checkout
    }
}

Migration from Custom Solution

Before (Custom SharedPreferences)

val prefs = context.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)

fun isFeatureEnabled(key: String): Boolean {
    return prefs.getBoolean(key, false)
}

fun updateFlags() {
    // Manual API call
    apiService.getFlags().enqueue(object : Callback<FlagsResponse> {
        override fun onResponse(call: Call<FlagsResponse>, response: Response<FlagsResponse>) {
            response.body()?.let { flags ->
                prefs.edit {
                    flags.features.forEach { (key, value) ->
                        putBoolean(key, value)
                    }
                }
            }
        }
        
        override fun onFailure(call: Call<FlagsResponse>, t: Throwable) {
            // Handle error
        }
    })
}

After (Flagship)

// One-time setup
val config = FlagsConfig(
    appKey = "my-app",
    environment = "production",
    providers = listOf(
        RestFlagsProvider(httpClient, "https://your-backend.com/flags")
    ),
    cache = PersistentCache(FlagsSerializer()) // Automatic cache
)
Flags.configure(config)

// Use everywhere
val manager = Flags.manager()

// Note: isEnabled is a suspend function - make the function suspend too
suspend fun isFeatureEnabled(key: String): Boolean {
    return manager.isEnabled(key)
}

// Update is automatic based on TTL, or manually:
suspend fun updateFlags() {
    manager.refresh()
}

Benefits

Less code - no manual SharedPreferences management

Type-safe - automatic type checking

Coroutines - modern async approach

TTL - automatic scheduled updates

Multi-provider - easy to add Firebase/other sources

A/B Testing - built-in experiment support


Step-by-Step Migration

Step 1: Add Flagship in Parallel

Don't remove the old solution immediately! Use both in parallel:

// Old
val oldFlag = remoteConfig.getBoolean("new_feature")

// New (Flagship)
val newFlag = Flags.manager().isEnabled("new_feature")

// Use new flag, fallback to old if needed
val featureEnabled = if (flagshipInitialized) newFlag else oldFlag

Step 2: Switch Flags Gradually

// Week 1: 10% users
if (userId.hashCode() % 100 < 10) {
    useFlagship = true
}

// Week 2: 50% users
if (userId.hashCode() % 100 < 50) {
    useFlagship = true
}

// Week 3: 100% users
useFlagship = true

Step 3: Monitor Errors

val config = FlagsConfig(
    // ...
    logger = object : FlagsLogger {
        override fun log(level: LogLevel, message: String, error: Throwable?) {
            // Send to Crashlytics/Sentry
            if (level == LogLevel.ERROR) {
                Crashlytics.getInstance().recordException(error ?: Exception(message))
            }
        }
    }
)

Step 4: Remove Old Code

After successful migration (2-4 weeks):

1. Remove old SDK dependencies

2. Remove old solution code

3. Update documentation


Need migration help? Create an Issue on GitHub!