Liste de contrôle pour la revue de code Kotlin : les bases à connaître

Souhaitez-vous, ainsi que votre équipe, développer de meilleures applications Kotlin ? Une revue de code indépendante vous donnera un avantage certain. Les revues de code vous aideront à détecter les bugs tôt et à améliorer les performances tout en créant des applications que les utilisateurs aimeront et noteront positivement. Cette checklist couvre les bases du développement d’applications Kotlin et simplifiera les revues de code Kotlin.

Portée et objectifs

La préparation préalable à la revue est très importante. Cela signifie vérifier que tout est en place avant le début de l’analyse du code. Premièrement, assurez-vous que la portée et les objectifs sont bien définis pour vous concentrer. De cette façon, vous garantirez que votre revue est ciblée et précise.

Meilleures pratiques clés :

  • Énoncez clairement les objectifs de la revue : concentrez-vous sur la sécurité, les performances, la lisibilité, etc.
  • Définissez précisément la portée : un module, une fonctionnalité spécifique, ou l’application entière
  • Établissez des critères de succès mesurables : nombre de bugs corrigés, benchmarks de performance, pourcentage de couverture du code, etc.
  • Communiquez la portée à votre équipe à l’avance

Configuration de l'environnement de test

L’étape cruciale suivante au stade de la pré-revue est la configuration de votre environnement de test. Un environnement correctement configuré rend le processus de revue futur fluide et efficace. Cela vous évitera également de tout refaire en cas d’erreur.

Meilleures pratiques clés :

  • Vérifiez que l’IDE est correctement configuré avec Kotlin et toutes les dépendances
  • Vérifiez que les plugins nécessaires comme Detekt ou Gradle sont installés et fonctionnent
  • Mettez en place une suite de tests complète, incluant des tests unitaires, d’intégration et de bout en bout
  • Confirmez que la suite de tests s’exécute sans problème et que la base de code est prête pour les tests

Documentation du code

Il est maintenant temps de passer à l’étape critique suivante : vérifier si votre code Kotlin se présente clairement grâce à sa documentation. Un code bien documenté n’est pas seulement une courtoisie, c’est un superpouvoir pour des revues efficaces et la santé à long terme du projet.

Meilleures pratiques clés :

  • Écrivez des commentaires KDoc significatifs pour les classes, les fonctions et les propriétés
  • Vérifiez la présence d’explications claires et concises pour la logique complexe
  • Assurez-vous que les exemples fonctionnent pour les API et les fonctions publiques
  • Vérifiez que la documentation est à jour avec le code
  • Encouragez l’utilisation d’outils de génération de documentation comme Dokka
// Good practice of Code Documentation (KDoc)

/**
 * A class representing a user profile.
 *
 * It holds user's personal information and provides methods to manage their profile.
 *
 * @property userId Unique identifier for the user.
 * @property username The user's chosen username.
 */
class UserProfile(val userId: Int, val username: String) {

   /**
* Validates personal information.
*
* @param firstName The user's first name. Can be null.
* @param lastName The user's last name. Can be null.
* @return `true` if both the first name and last name are non-null and non-blank, `false` otherwise.
*/
fun validatePersonalInfo(firstName: String?, lastName: String?) =
!firstName.isNullOrBlank() && !lastName.isNullOrBlank()
}


// Bad practice of Code Documentation (KDoc)

/**
 * UserProfile class.
 */
class UserProfile(val userId: Int, val username: String) {

    /**
     * Function to get full name.
     */
    fun getFullName(firstName: String, lastName: String): String {
        return "$firstName $lastName"
    }
}

Dépendances à jour

La vérification des dépendances à jour détermine si la base de votre projet est solide. Utiliser les bibliothèques et les outils les plus récents n’est pas seulement une décoration de fonctionnalités dernier cri, c’est une étape essentielle pour la sécurité et la stabilité à long terme.

Meilleures pratiques clés :

  • Examinez les mises à jour de dépendances à l’aide des commandes Gradle ou Maven
  • Vérifiez que toutes les dépendances proviennent de sources fiables et réputées
  • Recherchez les dépendances présentant des vulnérabilités de sécurité connues
  • Vérifiez si les versions des dépendances sont cohérentes dans tout le projet
  • Utilisez des outils de gestion des dépendances pour automatiser les mises à jour et les contrôles de vulnérabilité

Outils d'analyse statique

N’hésitez pas à intégrer des outils d’analyse statique dans vos revues de code Kotlin. Ces outils agissent comme vos assistants infatigables, détectant les problèmes potentiels tôt et de manière cohérente, libérant ainsi les réviseurs humains pour des aspects plus nuancés du code.

Meilleures pratiques clés :

  • Ajoutez des outils d’analyse statique au pipeline CI/CD de votre projet
  • Utilisez des linters comme Detekt et Ktlint pour le respect des styles de code Kotlin
  • Configurez les règles d’analyse statique pour qu’elles correspondent aux besoins spécifiques de votre projet et à vos standards de codage
  • Examinez et corrigez les découvertes des rapports d’analyse statique
  • Introduisez de nouvelles règles d’analyse statique auprès des autres développeurs

Données et scénarios de test

Des tests bien menés sont votre filet de sécurité, et leur revue est aussi vitale que la revue du code lui-même. Des données et des scénarios de test solides garantissent la fiabilité de votre code Kotlin, aujourd’hui et à l’avenir.

  • Examinez les données de test pour vérifier leur réalisme et les cas limites
  • Vérifiez la couverture complète des scénarios, y compris les cas positifs et négatifs
  • Confirmez que les tests sont indépendants et ne dépendent pas d’états ou de dépendances externes
  • Recherchez des noms de tests clairs et descriptifs qui expliquent le scénario testé
  • Promouvez les tests basés sur les données pour les scénarios avec plusieurs variations d’entrée

Nommage et commentaires cohérents

Le nommage, le formatage et les commentaires peuvent parfois sembler mineurs, mais ils sont le fondement de la lisibilité et de la maintenabilité de tout projet Kotlin. Ils permettent des revues de code plus fluides et un alignement plus efficace pour tous.

Meilleures pratiques clés :

  • Confirmez que le code respecte les conventions de codage Kotlin et les guides de style
  • Vérifiez la cohérence des conventions de nommage dans toute la base de code
  • Assurez-vous que les commentaires expliquent la logique non évidente ou les sections complexes
  • Recherchez des opportunités d’améliorer la clarté du code par un meilleur nommage ou formatage, plutôt que de simplement ajouter des commentaires
  • Vérifiez que les commentaires sont exacts et à jour avec le code
// Good practice of Naming, Formatting, and Comments

class OrderProcessor {

    fun calculateTotalPrice(orderItems: List, discountPercentage: Double): Double {
        val subtotal = orderItems.sumOf { it.price * it.quantity }
        return subtotal * (1 - (discountPercentage / 100))
    }
}

data class OrderItem(
    val productId: Int,
    val productName: String,
    val quantity: Int,
    val price: Double
)


// Bad practice of Naming, Formatting, and Comments

class order_processor {

    fun calc_price(items: List, disc: Double): Double {
        // calculate sum
        val s = items.sumOf { it.price * it.quantity }

        // discount
        val discount = s * (disc / 100)
        val finalPrice = s - discount

        return finalPrice
    }
}

data class OrderItem(
    val productID: Int,
    val item_name: String,
    val qty: Int,
    val itemPrice: Double
)

Organisation des importations

Garder le code simple et organiser les importations accélérera finalement les revues, réduira les risques d’erreurs et rendra la base de code compréhensible pour tous. Un code facile à saisir et à naviguer est un plaisir à réviser et à maintenir. Il prépare le terrain pour un projet plus sain et plus robuste.

Meilleures pratiques clés :

  • Décomposez les fonctions complexes
  • Réduisez l’imbrication pour améliorer la navigabilité du code
  • Organisez les importations pour clarifier les dépendances
  • Supprimez les importations inutilisées pour réduire l’encombrement
  • Évitez les importations génériques (wildcard)
// Good practice of Import Organization

import com.example.model.User
import com.example.repository.UserRepository
import com.example.service.UserService
import com.example.view.UserView

class UserProcessor {

    private val userRepository = UserRepository()
    private val userService = UserService(userRepository)

    fun displayUserDetails(userId: Int) {
        val user = userService.getUserById(userId) ?: return // Ранній вихід, якщо користувача не знайдено

        val formattedName = user.username.capitalize() // Використання вбудованої функції Kotlin
        val userView = createUserView(user, formattedName)
        userView.display()
    }

    private fun createUserView(user: User, formattedName: String): UserView {
        return UserView(
            name = formattedName,
            email = user.email.orEmpty() // Безпечна обробка null
        )
    }
}


// Bad practice of Import Organization 

import com.example.utils.*
import com.example.view.UserView
import com.example.service.UserService
import com.example.model.User
import com.example.repository.UserRepository

class UserProcessor {

    private val userRepository = UserRepository()
    private val userService = UserService(userRepository)

    fun displayUserDetails(userId: Int) {
        val user = userService.getUserById(userId)
        if (user != null) {
            val name = user.username
            val formattedName = StringUtils.capitalize(name) // Utility function somewhere
            val email = if (user.email != null) user.email else "" // Null check inline

            val view = UserView(formattedName, email)
            view.display()

        }
    }
}

Regroupement logique et longueur des fonctions

Un code bien organisé est fondamentalement plus simple à comprendre, tester et faire évoluer. En regroupant logiquement les fonctions et en les maintenant concises, nous créons une base de code que les examinateurs peuvent rapidement assimiler. Les modifications sont moins sujettes aux erreurs, et le projet dans son ensemble devient plus maintenable.

Meilleures pratiques clés :

  • Regroupez le code lié en unités logiques comme des classes ou des packages
  • Respectez le principe de responsabilité unique (SRP)
  • Limitez la longueur des fonctions
  • Refactorisez les fonctions trop longues ou mal groupées

Utilisation appropriée des fonctionnalités

Écrire du bon code Kotlin va au-delà d’un code fonctionnel. Il s’agit d’utiliser les outils de Kotlin pour rendre les applications plus robustes, lisibles et rapides. Lorsque les fonctionnalités de Kotlin sont correctement utilisées, les revues de code deviennent beaucoup plus fluides. Les examinateurs peuvent rapidement comprendre l’essentiel : comment l’application fonctionne et est construite, au lieu de se perdre dans un code complexe ou inhabituel.

Meilleures pratiques clés :

  • Tirez parti des fonctionnalités de sécurité des null de Kotlin (par exemple, ?, !!, let, run, also, apply) pour minimiser les NullPointerExceptions et simplifier les revues de code
  • Utilisez les classes de données Kotlin pour les entités centrées sur les données
  • Utilisez les fonctions d’extension Kotlin, les fonctions d’ordre supérieur et les lambdas
  • Vérifiez l’utilisation idiomatique de Kotlin, en vous éloignant des schémas verbeux de style Java
// Good practice of Feature Utilization (Idiomatic Kotlin)

import java.util.Locale

data class Address(val street: String?, val city: String, val zipCode: String?)

fun formatAddress(address: Address?) = address?.run {
    """${street?.capitalizeFirst() ?: "Unknown Street"}, ${city.uppercase()}, ${zipCode ?: "N/A"}"""
} ?: "No Address Provided"

fun String.capitalizeFirst(): String =
    replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }

fun main() {
    val validAddress = Address("Main Street", "Kyiv", "01001")
    val partialAddress = Address(null, "Lviv", null)
    val noAddress: Address? = null

    println(formatAddress(validAddress))
    println(formatAddress(partialAddress))
    println(formatAddress(noAddress))
}

// Bad practice of Feature Utilization (More Java-style/Verbose)

class Address {
    var street: String? = null
    var city: String = ""
    var zipCode: String? = null

    constructor(street: String?, city: String, zipCode: String?) {
        this.street = street
        this.city = city
        this.zipCode = zipCode
    }
}

fun getFormattedAddress(address: Address?): String {
    if (address != null) {
        var streetName = address.street
        if (streetName == null) {
            streetName = "Unknown Street"
        } else {
            streetName = streetName.capitalize()
        }

        var zip = address.zipCode
        if (zip == null) {
            zip = "N/A"
        }

        return streetName + ", " + address.city.toUpperCase() + ", " + zip
    } else {
        return "No Address Provided"
    }
}

fun main() {
    val validAddress = Address("Main Street", "Kyiv", "01001")
    val partialAddress = Address(null, "Lviv", null)
    val noAddress: Address? = null

    println(getFormattedAddress(validAddress))
    println(getFormattedAddress(partialAddress))
    println(getFormattedAddress(noAddress))
}

Gestion sûre des nulls

Les exceptions de pointeur nul sont le fléau de l’existence de nombreux développeurs, et Kotlin fournit des outils puissants pour les éviter. En examinant précisément la manière dont les nulls sont gérés dans le code Kotlin, nous réduisons le risque de plantages, améliorons la stabilité de l’application et rendons la base de code plus simple à maintenir.

Meilleures pratiques clés :

  • Utilisez les types non nuls (?) par défaut
  • Utilisez les types nuls (?) lorsqu’un nul est valide et attendu
  • Promouvez l’appel sûr (?.) et l’opérateur Elvis (?:) pour une gestion des nulls concise et sûre
  • N’utilisez pas l’opérateur d’assertion non nul (!!) à moins que cela ne soit inévitable
  • Testez les scénarios de gestion des nulls, en particulier pour les types nuls et les interactions de données externes

Gestion des exceptions

Aucun code n’est parfait, et les choses vont inévitablement mal tourner. Ce qui est important, c’est que votre code sache comment réagir lorsque ces accidents se produisent et puisse clairement indiquer ce qui s’est mal passé. Vérifier une bonne gestion des erreurs lors des revues de code garantit que les applications sont robustes et ne se bloquent pas facilement. De plus, des messages d’erreur clairs fournissent un guide utile aux développeurs pour trouver et corriger rapidement les problèmes.

Meilleures pratiques clés :

  • Utilisez les blocs try-catch de Kotlin pour gérer les exceptions potentielles et prévenir les plantages
  • Générez des types d’exception spécifiques et personnalisés pour fournir un contexte d’erreur plus riche
  • Incluez des messages d’erreur informatifs dans les exceptions
  • Ne rattrapez pas les Exception génériques sauf si nécessaire
  • Examinez les pratiques de journalisation des erreurs
// Good practice of Exception Management

import java.io.File
import java.io.IOException

class FileProcessor {

    fun readFileContent(filePath: String): String = try {
        File(filePath).readText()
    } catch (e: IOException) {
        throw FileProcessingException("Error reading file at path: $filePath", e)
    } catch (e: SecurityException) {
        throw FileProcessingException("Insufficient permissions to read file: $filePath", e)
    }
}

class FileProcessingException(message: String, cause: Throwable? = null) : Exception(message, cause)

fun main() {
    val processor = FileProcessor()
    val filePath = "data.txt"
    try {
        val content = processor.readFileContent(filePath)
        println("File content:n$content")
    } catch (e: FileProcessingException) {
        println("Error processing file: ${e.message}")
        e.cause?.message?.let { println("Caused by: $it") } // Simplified cause message printing
    }
}

// Bad practice of Exception Management

import java.io.File
import java.io.IOException

class FileProcessor {

    fun readFileContent(filePath: String): String {
        try {
            return File(filePath).readText()
        } catch (e: Exception) { // Catching generic Exception
            throw Exception("File error") // Generic error message
        }
    }
}


fun main() {
    val processor = FileProcessor()
    val filePath = "data.txt"
    try {
        val content = processor.readFileContent(filePath)
        println("File content:n$content")
    } catch (e: Exception) {
        println("Error: Something went wrong") // Uninformative error message
    }
}

Collections efficaces

Examiner le code est essentiel pour rendre les applications plus rapides et plus légères. Vous pouvez vous assurer qu’il n’y a pas trop d’objets créés et que vous utilisez les meilleures méthodes pour stocker les données. De telles applications sont plus rapides et plus réactives, et elles consomment moins de batterie. Porter attention à ces détails lors des revues de code fait une différence significative pour les utilisateurs de l’application.

Meilleures pratiques clés :

  • Minimisez la création d’objets, en particulier dans les boucles ou les fonctions fréquemment appelées
  • Utilisez efficacement les collections Kotlin telles que List, Set et Map de manière appropriée
  • Utilisez des collections immuables lorsque cela est possible
  • Utilisez des opérations sur les collections telles que map, filter, reduce et fold
  • Examinez le code pour les surcoûts potentiels de boxing/unboxing lors de l’utilisation de types primitifs et de collections, en particulier dans les sections critiques en termes de performances
// Good practice of Object Creation and Collection Usage

object StringCache { // Using object declaration for singleton
    private val cache = hashSetOf() // Efficient HashSet for lookups

    fun isCached(text: String) = cache.contains(text)

    fun addToCache(text: String) {
        cache.add(text) // HashSet add() already handles duplicates efficiently
    }
}

fun List.processUniqueToUpperCase() = this
    .filterNot { StringCache.isCached(it) } // Using filterNot for better readability
    .map { it.uppercase() }

fun main() {
    StringCache.addToCache("apple")
    StringCache.addToCache("banana")

    val inputList = listOf("apple", "orange", "banana", "grape")
    val uniqueUpperCaseStrings = inputList.processUniqueToUpperCase() // Using the extension function
    println("Unique Uppercase Strings: $uniqueUpperCaseStrings") // Output: [ORANGE, GRAPE] - Corrected output
}

// Bad practice of Object Creation and Collection Usage

class StringCache { // Class, not object - new instance created every time
    private val cache = ArrayList() // Inefficient ArrayList for lookups

    fun isCached(text: String): Boolean {
        for (item in cache) { // Manual loop for checking existence - slow in ArrayList
            if (item == text) {
                return true
            }
        }
        return false
    }

    fun addToCache(text: String) {
        if (!isCached(text)) {
            cache.add(text)
        }
    }
}

fun processStrings(stringList: List): List {
    val resultList = ArrayList() // Creating a mutable list
    for (str in stringList) { // Manual loop for filtering and transforming
        if (!StringCache().isCached(str)) { // New StringCache instance created unnecessarily!
            resultList.add(str.toUpperCase())
        }
    }
    return resultList
}

fun main() {
    val cache = StringCache() // Instance creation
    cache.addToCache("apple")
    cache.addToCache("banana")

    val inputList = listOf("apple", "orange", "banana", "grape")
    val uniqueUpperCaseStrings = processStrings(inputList)
    println("Unique Uppercase Strings: $uniqueUpperCaseStrings") // Output: [ORANGE, GRAPE]
}

Vos services d'audit et de développement Kotlin

L’opportunité de notre ère mobile moderne est que près de 5 milliards d’utilisateurs de smartphones dans le monde attendent votre application pour résoudre leurs tâches ! Tirez le meilleur parti de cette aubaine en utilisant les 20 ans d’expérience de Redwerk dans le développement d’applications mobiles. Nous mettrons en œuvre votre vision pour créer une application Android native classée parmi les meilleures, une application iOS native sans faille, ou même une application hybride multiplateforme entièrement fonctionnelle.

Oubliez le stress du projet et les délais dépassés. Les développeurs, designers, ingénieurs QA et chefs de projet experts de Redwerk excellent face aux défis, les transformant en succès. Nous vous aiderons à atteindre tous vos objectifs commerciaux sur la voie du succès — jetez un œil :

  • De l’idée à la solution. Nous construisons des solutions logicielles personnalisées à partir de zéro. Découvrez comment nous avons développé une boîte à outils de test Android (Kotlin) performante et intuitive qui peut être utilisée avec une efficacité égale par les ingénieurs QA, les développeurs, les spécialistes produits et les créatifs.
  • Modernisation du code hérité. La refactorisation ne consiste pas seulement à ranger — il s’agit de redonner vie à votre système existant. Nos spécialistes en refactorisation de code injectent les meilleures pratiques modernes dans l’ADN de votre projet. Forts de notre expérience dans le développement de plus de 20 applications Android dans 7 pays, nous savons ce qu’il faut pour améliorer les performances et la gestion de votre système.
  • Booster l’attrait de votre produit. Nos développeurs n’ajoutent pas seulement des fonctionnalités — ils construisent dans le contexte de vos objectifs, permettant à votre solution de croître et de s’adapter sans effort. Lisez comment nous avons développé un système de stationnement de vélos connecté à une application cloud, une application Android native et des applications iOS pour My Bike Valet. Nous avons conçu leur architecture MVP, qui a fourni une base solide et a conduit à une expansion simple.
  • Natif ou multiplateforme ? Débloquez la stratégie mobile idéale. Chez Redwerk, nous maîtrisons à la fois le natif et le multiplateforme. Le natif est le choix ultime pour des performances de pointe et des fonctionnalités de pointe, tandis que le développement multiplateforme offre des prix plus bas et une portée plus large. Ne naviguez pas seul dans cette décision — les experts de Redwerk vous aideront à choisir la bonne voie.

Que vous soyez confronté à une revue de code critique, ayez besoin d’une expertise Kotlin, souhaitiez un développement complet, ou tout autre chose, les experts Kotlin de Redwerk sont là pour vous. Nous sommes impatients d’entendre parler de votre vision et de la manière dont nous pouvons vous aider. Contactez notre équipe quand vous serez prêt.

Découvrez comment nous avons développé un planificateur de ressources basé sur Kotlin pour Mass Movement, augmentant l'efficacité de la chaîne d'approvisionnement de 30 %

Veuillez saisir votre adresse courriel professionnelle n'est pas un courriel professionnel