Code review in Kotlin: what to check and how to give feedback for beginners
Code review is one of the key processes in modern development. For beginners in Kotlin, it often seems intimidating: how to find errors? What exactly should you look at? How not to offend the code author? In this article, we will break down what to pay attention to during code review of Kotlin projects, and how to give feedback that is helpful rather than hurtful. This material is suitable both for those who are just learning to review and for those who want to improve their skills.
Why is code review needed in Kotlin?
Kotlin is a modern, concise, and safe language, but it gives the developer a lot of freedom. Without code review, it's easy to make mistakes related to null-safety, incorrect use of coroutines, or excessive complexity. Review helps:
- Find bugs at early stages.
- Improve code readability and maintainability.
- Spread knowledge within the team.
- Maintain a consistent code style.
For beginners, it's also a great way to learn from others' examples. Let's move on to the specifics.
What to check during code review of Kotlin code
Here is a checklist of key points to check in every pull request. Let's divide them into logical blocks.
1. Null-safety and types
Kotlin prides itself on its null-safety, but it can be violated. Check:
- Are nullable types (e.g.,
String?) used only where truly necessary? - Are there any unnecessary
!!(non-null assertion)? Ideally, there should be none at all in production code. - Are safe calls (
?.let { ... }) and the Elvis operator (?:) used instead ofif (x != null)?
Bad example:
val name: String? = getName()val length = name!!.length // Dangerous: will cause NPE if name == nullGood example:
val name: String? = getName()val length = name?.length ?: 0 // Safe: if null, then 02. Functions and their signatures
Kotlin supports named arguments and default values. Check:
- Is the function too long (more than 20–30 lines)? It might need to be broken up.
- Are default parameters used to simplify calls?
- Are there functions with more than 3–4 parameters? This complicates reading.
- Is the use of extension functions appropriate? They should not change the object's state.
Example:
// Instead of this:fun processUserData(name: String, age: Int, email: String, city: String, phone: String) { ... }
// Better like this:data class UserData(val name: String, val age: Int, val email: String, val city: String, val phone: String)fun processUserData(user: UserData) { ... }3. Working with coroutines
Coroutines are a powerful Kotlin tool, but they are easy to misuse. Pay attention to:
- Is the thread blocked inside a coroutine (calling
Thread.sleep()or blocking I/O)? - Is the correct dispatcher used (Dispatchers.IO for network requests, Dispatchers.Main for UI)?
- Are there any coroutine leaks (e.g., launched in an Activity without cancellation)?
- Is
withContextused to switch dispatchers, rather thanlaunchinsidelaunch?
Bad example:
fun fetchData() { GlobalScope.launch { // Leak: not cancelled when component is destroyed Thread.sleep(1000) // Blocks the thread! }}Good example:
fun fetchData(scope: CoroutineScope) { scope.launch { withContext(Dispatchers