This is the code showed during my presentation about Feature Flags.
The most "archaic" way of using a feature flag, comment or uncomment the code to get the expected result.
fun getCardToDisplay() {
// var isVodaCampaign = true // uncomment to test
val isVodaCampaign = false
val cardToDisplay = if (isVodaCampaign) {
PromoCard(
text = R.string.voda_title,
imageUrl = VODA_IMG,
ctaText = R.string.voda_cta
)
} else {
null
}
}
A basic example of a feature flag, where we keep the string and the remote config in the ViewModel.
fun getCardToDisplay() {
val cardToDisplay = if (remoteCampaignFlag == "voda_campaign_2021") {
PromoCard(
text = R.string.voda_title,
imageUrl = VODA_IMG,
ctaText = R.string.voda_cta
)
} else {
null
}
}
Extracted the logic from the ViewModel to a separate class that handles the remote value and the possible scenarios. The construction the card is still done in the ViewModel.
fun getCardToDisplay() {
val cardToDisplay = when (gatekeeper.getCampaign()) {
AvailableCampaigns.VODA -> {
PromoCard(
text = R.string.voda_title,
imageUrl = VODA_IMG,
ctaText = R.string.voda_cta
)
}
AvailableCampaigns.INFOGRAPHIC -> {
PromoCard(
text = R.string.infographic_title,
imageUrl = INFOG_IMG,
ctaText = R.string.infographic_cta
)
}
else -> {
null
}
}
}
Example using Flow while getting the config from a remote host using sockets so you have real time enabling or disabling of feature flags.
fun getCardToDisplay() {
viewModelScope.launch {
gatekeeper.getCampaignFlow().collect { campaign ->
getCampaignCard(campaign)?.let { cardToDisplay ->
...
}
}
}
}
Using Inversion of Control so that you keep the ViewModel as clean as possible. Gatekeeper decides what to show.
fun getCardToDisplay() {
displayedCard?.getCard()?.let { cardToDisplay ->
viewModelScope.launch {
_activeCampaign.emit(cardToDisplay)
}
}
}