Skip to main content

Command Palette

Search for a command to run...

Kotlin Code Smell 20 - Settings/Configs

Settings Spaghetti: When Configurations Go Wild...

Updated
2 min read
Kotlin Code Smell 20 - Settings/Configs
Y

I've started to work as a software engineer at 2014, however, I started to write code at high-school.

My first language was Assembly, but still, I fall in love with the possibilities to make the computer to do as you wish, shortly after that I started to write in C.

Later on I studied a practical engineering in electricity, and during this time discovered that I preferred much more writing code than design electrical components.

As a result of this understanding I decided to switch and study bachelor degree in computer science in Reichman university, where the focus was of the Java language.

Today I'm working at SumUp using Kotlin, SpringBoot & Micronaut, Cassandra and Kafka

Problem

  • Duplicated Code

  • If Pollution

  • Global usage

  • Coupling

  • Testability and the explosion of testing scenarios

  • Complexity

Solution

  • Avoid using settings directly

  • Create polymorphic objects and inject them externally.

Sample Code

Wrong

class VerySpecificAndSmallObjectThatDealWithPersistence {
    fun retrieveData() =
        if (GlobalSettings.instance["I am A Possible Mistyped String"] != null) {
            retrieveDataThisWay()
        } else {
            retrieveDataTheOtherWay()
        }

    private fun retrieveDataThisWay() {
        // ...
    }

    private fun retrieveDataTheOtherWay() {
        // ...
    }
}

class GlobalSettings {
    private val settings = mutableMapOf<String, String>()

    operator fun set(key: String, value: String) {
        settings[key] = value
    }

    operator fun get(key: String) = settings[key]

    companion object {
        val instance = GlobalSettings()
    }
}

Right

class VerySpecificAndSmallObjectThatDealWithPersistence(private val retrieveStrategy: RetrieveStrategy) {
    fun retrieveData() = retrieveStrategy.retrieve()
}

interface RetrieveStrategy {
    fun retrieve(): String
}

class RetrieveFromDatabase : RetrieveStrategy {
    override fun retrieve(): String {
        return "Data from the database"
    }
}

Examples

Exceptions

  • Sometimes, we use Feature toggling as a safeguard mechanism. This is acceptable in a legacy system. However, these toggles should be very short-lived in a CI/CD system.

  • Hyperparameter settings should be managed by configuration objects. You can read these objects from any persistence media and change your system behavior at runtime in an explicit and controlled way.

Conclusion

Setting runtime behavior is great for software systems.

We should configure our objects so they can behave in different ways, and we should achieve it in an explicit way with explicit behavioral objects.

In this way, our code will be more declarative, clean, and testable. It is not as easy as adding an IF Statement. This kind of lazy developer brings lots of coupling and unexpected issues to our systems.

A system with 300 Boolean configurations has more test combinations (2 ^ 300) than the number of atoms in the universe (10 ^ 80).

Credits

Kotlin Code Smells

Part 17 of 36

In this series, we will see several symptoms and situations that make us doubt the quality of our development. We will present possible solutions. Most are just clues. They are no hard rules.

Up next

Kotlin Code Smell 19 - Setters

Code Missteps: When Objects Play Hard to Set

More from this blog

Yonatan Karp-Rudin | kotlin for backend developer skills | java for backend developer skills | SpringBoot | Tutorials

57 posts

Experienced Senior Software Engineer passionate about functional programming & Kotlin. Excels in app development, optimization, and team collaboration. Let's create something amazing!