“Do’s” in modern Android/Kotlin Development (Tips & Good Practices) — Chapter II

A Poplawski
4 min readNov 11, 2023
Photo by John Schnobrich on Unsplash

This series of articles will discuss variety of practices, actions and tips that are likely to improve your: code quality, product stability, relationships with fellow devs and happiness of your PM/client. I will refer to them as “Do’s”.

Following “Do’s” I developed throughout my 4 years journey as Android developer. They will concern various different topics; coding best practices, design patterns that might benefit code quality, but also processes and actions, that should improve workflow and maintainability of the project.

While some of the “Do’s” might be an overkill for a small, hobby project, they can really shine in medium and large commercial projects, where complexity can really become an issue and quality is a requirement.

Part 1: https://medium.com/@a.poplawski96/dos-in-modern-android-kotlin-development-tips-good-practices-chapter-i-5dfdd276dd6b

I also write on “Don’ts”, bad practices & anti-pattenrs:
Part 1: https://medium.com/@a.poplawski96/donts-in-modern-android-kotlin-development-bad-practices-anti-patterns-chapter-i-d38cba2f5f7d
Part 2: https://medium.com/@a.poplawski96/donts-in-modern-android-kotlin-development-bad-practices-anti-patterns-chapter-ii-984b501aed4b

4. Use abstractions instead of concrete implementations

Consider following example. We’re working on an application. It’s a real commercial app, and we track analytics using Firebase.

@Composable
fun ExampleScreen(
viewModel: ExampleViewModel,
firebaseAnalytics: FirebaseAnalytics,
) {

LaunchedEffect(null) {
firebaseAnalytics.logEvent("screen_view", bundleOf("screen_name" to "example_screen"))
}


Button(
onClick = {
viewModel.handleClick()
firebaseAnalytics.logEvent("button_click", bundleOf("screen_name" to "example_screen"))
}
) {
Text(text = "Click me")
}

// ...some more code.
}

Let’s assume we have 15, 30 or more screens in our application.
Let’s assume we track proper analytics throughout the whole application (5, 10 or more events per screen).

Now, let’s assume that requirements changed (shocking, I know).
We have to migrate out of current analytics solution. Either Firebase got too expensive, there is a better solution, or we want to use our new in-house analytics.

If we write code like on the example above, i.e. depending on concrete implementations (Firebase Analytics in this case), then we end up with dozens, few dozens or even hundreds line of to modify (depending on the project size), if we want to switch to new analytics solution.

And that’s not good. We can do better.

Here’s what should be done instead.

class AnalyticsLogger(private val firebaseAnalytics: FirebaseAnalytics) {

fun log(event: String, params: Bundle) {
firebaseAnalytics.logEvent(event, params)
}
}

Or…

interface AnalyticsLogger {

fun log(event: String, params: Bundle)
}

class FirebaseAnalyticsLogger(private val firebaseAnalytics: FirebaseAnalytics) : AnalyticsLogger {

override fun log(event: String, params: Bundle) {
firebaseAnalytics.logEvent(event, params)
}
}

Then…

@Composable
fun ExampleScreen(
viewModel: ExampleViewModel,
analyticsLogger: AnalyticsLogger,
) {

LaunchedEffect(null) {
analyticsLogger.logEvent("screen_view", bundleOf("screen_name" to "example_screen"))
}

Button(
onClick = {
viewModel.handleClick()
analyticsLogger.logEvent("button_click", bundleOf("screen_name" to "example_screen"))
}
) {
Text(text = "Click me")
}

// ...some more code.
}

We used AnalyticsLogger abstraction, instead of FirebaseAnalytics concrete implementation. Now, if we need to switch libraries, we just do it in one place. Much better than dozens or hundreds, isn’t it?

5. Care about naming

TLDR; I wrote a separate article about issues of imprecise naming, it’s here: https://medium.com/@a.poplawski96/why-precise-naming-matters-in-programming-real-life-example-0d87006c4f41

“How do you care about code quality?”, “What best practices do you use in programming?”. You can often hear this, or similar questions in your job interview. “Testability, design patterns, SOLID principles, loose coupling, learning language idioms” are some of the great answers for this. These subjects are technical, difficult and require understanding.

But there is one more thing that I would always add to the list. It’s not technical, it does not require deep understanding. It’s the easiest one, yet often overlooked.

It’s precise naming.

What I mean by naming — how you name your classes, functions, variables and files in your project.

And when I say precise, I mean really precise.

Apart from the obvious advantage of making the code more clear and easier to understand, caring about precise naming can save you from mistakes and bugs in your software.

I’ll give you a real life example of mine, where variable that was not named precise enough led me to making a mistake in code.

I wrote a separate article about issues of imprecise naming, you can find it here: https://medium.com/@a.poplawski96/why-precise-naming-matters-in-programming-real-life-example-0d87006c4f41

Thanks for reading my friend! I write more about Android, Kotlin and programming itself, so if you’re interested in this topic, consider following and seeing my other articles.

And also, let’s connect!
https://www.linkedin.com/in/apoplawski96/

--

--

A Poplawski

Creating Android apps since 2019 - Android, Android TV & Kotlin Multiplatform. Trying to share useful information in a digestible manner.