Lista de comprobación para la revisión de código Kotlin: Lo básico que hay que saber

¿Tú y tu equipo queréis desarrollar mejores aplicaciones Kotlin? Superar una revisión de código independiente te dará una gran ventaja. Las revisiones de código te ayudarán a detectar errores a tiempo y a mejorar el rendimiento, al tiempo que creas aplicaciones que los usuarios adorarán y valorarán positivamente. Esta lista de comprobación cubre los aspectos básicos de la creación de aplicaciones Kotlin y simplificará las revisiones de código de Kotlin.

Alcance y objetivos

La configuración previa a la revisión es muy importante. Significa comprobar que todo está en su sitio antes de que comience el análisis del código. En primer lugar, asegúrese de que hay un alcance y unos objetivos bien definidos en los que centrarse. De este modo, se asegurará de que su revisión esté bien orientada y sea precisa.

Mejores prácticas clave:

  • Establezca claramente los objetivos de la revisión: céntrese en la seguridad, el rendimiento, la legibilidad, etc.
  • Defina el alcance con precisión: un módulo, una función específica o toda la aplicación
  • Establezca criterios de éxito mensurables: número de errores corregidos, parámetros de rendimiento, porcentaje de cobertura del código, etc.
  • Comunique el alcance a su equipo de antemano

Configuración del entorno de pruebas

El siguiente paso crucial en la fase previa a la revisión es la configuración del entorno de pruebas. Un entorno correctamente configurado hace que el futuro proceso de revisión sea fluido y eficaz. También le evitará tener que rehacerlo todo en caso de error.

Buenas prácticas clave:

  • Comprueba si un IDE está configurado adecuadamente con Kotlin y todas las dependencias
  • Verificar que los plugins necesarios como Detekt o Gradle están instalados y funcionando
  • Configurar un conjunto completo de pruebas, incluyendo la integración de la unidad y pruebas de extremo a extremo
  • Confirmar que el conjunto de pruebas se ejecuta sin problemas y que el código base está listo para las pruebas

Documentación del código

Ahora es el momento de la siguiente etapa crítica, comprobar si tu código Kotlin habla claramente por sí mismo a través de la documentación. Un código bien documentado no es sólo una cortesía, es una superpotencia para las revisiones eficientes y la salud del proyecto a largo plazo.

Mejores prácticas clave:

  • Escribir comentarios KDoc significativos para clases, funciones y propiedades
  • Compruebe que la lógica compleja se explica de forma clara y concisa
  • Asegúrese de que los ejemplos funcionan para las API y funciones públicas
  • Compruebe que la documentación está actualizada con el código
  • Fomentar herramientas de generación de documentación como 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"
    }
}

Dependencias actualizadas

La comprobación de las dependencias actualizadas determina si la base de tu proyecto es sólida. Utilizar las bibliotecas y herramientas más actualizadas no es sólo un elogio por tener las últimas funciones: es un paso vital para la seguridad y la estabilidad a largo plazo.

Mejores prácticas clave:

  • Revisar las actualizaciones de dependencias mediante los comandos Gradle o Maven
  • Compruebe que todas las dependencias proceden de fuentes fiables y acreditadas
  • Busque dependencias con vulnerabilidades de seguridad conocidas
  • Comprobar si las versiones de las dependencias son coherentes en todo el proyecto
  • Utilizar herramientas de gestión de dependencias para automatizar las actualizaciones y las comprobaciones de vulnerabilidades

Herramientas de análisis estático

No dudes en recurrir a los ojos automatizados de las herramientas de análisis estático para tus revisiones de código Kotlin. Estas herramientas actúan como sus incansables asistentes, detectando problemas potenciales de forma temprana y consistente, liberando a los revisores humanos para aspectos más matizados del código.

Mejores prácticas clave:

  • Añada herramientas de análisis estático a la canalización CI/CD de su proyecto
  • Utilice linters como Detekt y Ktlint para la aplicación del estilo de código Kotlin
  • Configure las reglas de análisis estático para que coincidan con las necesidades específicas y los estándares de codificación de su proyecto
  • Revisar y abordar los descubrimientos de los informes de análisis estático
  • Introducir nuevas reglas de análisis estático a otros desarrolladores

Datos y escenarios de prueba

Las pruebas bien realizadas son su red de seguridad, y revisarlas es tan vital como revisar el propio código. Unos datos y escenarios de prueba sólidos garantizan la fiabilidad de tu código Kotlin, tanto ahora como en el futuro.

  • Revisar los datos de las pruebas para comprobar el realismo y los casos extremos
  • Compruebe que se cubren todos los escenarios, incluidos los casos positivos y negativos
  • Confirme que las pruebas son independientes y no dependen de estados o dependencias externas
  • Busque nombres de pruebas claros y descriptivos que expliquen el escenario que se está probando
  • Promover pruebas basadas en datos para escenarios con múltiples variaciones de entrada

Nombres y comentarios consistentes

Nombrar, formatear y comentar a veces puede parecer menor, pero son la base de la legibilidad y mantenibilidad en cualquier proyecto Kotlin. Permiten revisiones de código más fluidas y una alineación más efectiva para todos.

Mejores prácticas clave:

  • Confirmar que el código se adhiere a las convenciones de codificación y guías de estilo de Kotlin
  • Comprobar la coherencia de las convenciones de nomenclatura en todo el código base
  • Asegúrese de que los comentarios explican la lógica no evidente o las secciones complejas
  • Busque oportunidades para mejorar la claridad del código mediante una mejor nomenclatura o formato en lugar de limitarse a añadir comentarios
  • Comprobar que los comentarios son precisos y están actualizados con el código
// 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
)

Organización de las importaciones

Mantener el código simple y las importaciones organizadas acelerará las revisiones, reducirá las posibilidades de que se produzcan errores y hará que el código base sea comprensible para todos. El código que es fácil de entender y navegar es una alegría para revisar y mantener. Y sienta las bases para un proyecto más sano y sólido.

Buenas prácticas clave:

  • Descomponer las funciones complejas
  • Reducir el anidamiento para mejorar la navegabilidad del código
  • Organizar las importaciones para aclarar las dependencias
  • Elimine las importaciones no utilizadas para reducir el desorden
  • Evitar las importaciones comodín
// 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()

        }
    }
}

Agrupación lógica y longitud de las funciones

El código bien organizado es fundamentalmente más fácil de entender, probar y evolucionar. Al agrupar lógicamente las funciones y mantenerlas concisas, creamos una base de código que los revisores pueden comprender rápidamente. Los cambios son menos propensos a errores y el proyecto en general es más fácil de mantener.

Mejores prácticas clave:

  • Agrupar el código relacionado en unidades lógicas como clases o paquetes
  • Cíñase al principio de responsabilidad única (SRP)
  • Limitar la longitud de las funciones
  • Refractar funciones demasiado largas o código mal agrupado

Utilización adecuada de funciones

Escribir buen código Kotlin es más que tener código funcional. Se trata de utilizar las herramientas de Kotlin para hacer aplicaciones más fuertes, legibles y rápidas. Cuando las características de Kotlin se utilizan correctamente, las revisiones de código son mucho más fluidas. Los revisores pueden entender rápidamente lo importante -cómo funciona y se construye la aplicación- en lugar de perderse en código complicado o inusual.

Mejores prácticas clave:

  • Aprovechar las características de seguridad de nulos de Kotlin (por ejemplo, ?, !!, let, run, also, apply) para minimizar las NullPointerExceptions y simplificar las revisiones de código
  • Utilizar clases de datos de Kotlin para entidades centradas en datos
  • Utilizar funciones de extensión Kotlin, funciones de orden superior y lambdas
  • Comprobar el uso idiomático de Kotlin, alejándose de patrones farragosos al estilo 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))
}

Manejando Nulos con Seguridad

Las excepciones de puntero nulo son la pesadilla de la existencia de muchos desarrolladores, y Kotlin proporciona potentes herramientas para evitarlas. Revisando con precisión cómo se manejan los nulos en el código Kotlin, reducimos el riesgo de cuelgues, mejoramos la estabilidad de la aplicación y hacemos que la base de código sea más fácil de mantener.

Mejores prácticas clave:

  • Utilizar tipos no nulos (?) por defecto
  • Utilizar tipos anulables (?) cuando null es válido y esperado
  • Promover la llamada segura (?.) y el operador elvis (?:) para un manejo de nulos conciso y seguro
  • No emplee el operador de aserción not-null (!!) a menos que sea inevitable
  • Pruebe los escenarios de manejo de nulos, especialmente para tipos anulables e interacciones de datos externos

Gestión de excepciones

Ningún código es perfecto, y es inevitable que las cosas salgan mal. Lo importante es que el código sepa cómo reaccionar cuando ocurren estos accidentes y pueda indicar claramente qué ha fallado. La comprobación de una buena gestión de errores en las revisiones del código garantiza que las aplicaciones sean sólidas y no se rompan con facilidad. Además, los mensajes de error claros proporcionan una guía útil para que los desarrolladores encuentren y solucionen los problemas rápidamente.

Buenas prácticas clave:

  • Utilizar bloques try-catch de Kotlin para gestionar posibles excepciones y evitar fallos
  • Generar tipos de excepción específicos y personalizados para proporcionar un contexto de error más rico
  • Incluir mensajes de error informativos en las excepciones
  • No capturar Exception genéricas a menos que sea necesario
  • Revisar las prácticas de registro de errores
// 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
    }
}

Colecciones eficientes

Revisar el código es vital para hacer aplicaciones más rápidas y ligeras. Puedes asegurarte de que no se crean demasiadas cosas y de que utilizas las mejores formas de almacenar datos. Este tipo de aplicaciones son más rápidas, responden mejor y consumen menos. Prestar atención a estos detalles en las revisiones del código marca una diferencia significativa para los usuarios de las aplicaciones.

Mejores prácticas clave:

  • Minimizar la creación de objetos, especialmente en bucles o funciones llamadas con frecuencia
  • Utilizar adecuadamente colecciones Kotlin eficientes como List, Set y Map
  • Utilizar colecciones inmutables siempre que sea posible
  • Utilizar operaciones de colección como map, filter, reduce y fold
  • Revisar el código para detectar posibles sobrecargas de boxing/unboxing al utilizar tipos primitivos y colecciones, especialmente en secciones críticas para el rendimiento
// 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]
}

Sus servicios de desarrollo y auditoría de Kotlin

La oportunidad de nuestra era móvil moderna es que una audiencia global de 5.000 millones de usuarios de teléfonos inteligentes está esperando que su aplicación resuelva sus tareas. Aproveche al máximo esta bendición utilizando los 20 años de experiencia de Redwerk en el desarrollo de aplicaciones móviles. Pondremos en práctica su visión para crear una aplicación nativa para Android de primera categoría, una aplicación nativa para iOS impecable o incluso una aplicación híbrida multiplataforma totalmente funcional.

Olvídese del estrés de los proyectos y de los plazos agotados. Los expertos desarrolladores, diseñadores, ingenieros de control de calidad y gestores de proyectos de Redwerk prosperan ante los retos, transformándolos en victorias. Le ayudaremos a alcanzar cada objetivo empresarial en su camino hacia el éxito: eche un vistazo:

  • De la idea a la solución. Creamos soluciones de software personalizadas desde cero. Vea cómo desarrollamos un conjunto de herramientas de pruebas para Android (Kotlin) eficaz e intuitivo que pueden utilizar ingenieros de control de calidad, desarrolladores, especialistas en productos y creativos con la misma eficacia.
  • Modernización de código heredado. La refactorización no consiste únicamente en poner orden, sino en insuflar nueva vida al sistema existente. Nuestros especialistas en refactorización de código inyectan las mejores prácticas modernas en el ADN de su proyecto. Con nuestra experiencia en el desarrollo de más de 20 aplicaciones Android en 7 países, sabemos lo que se necesita para mejorar el rendimiento y la gestión de su sistema.
  • Aumentamos el atractivo de su producto. Nuestros desarrolladores no se limitan a añadir funciones, sino que construyen dentro del contexto de sus objetivos, permitiendo que su solución crezca y se adapte sin esfuerzo. Descubra cómo desarrollamos un sistema de aparcamiento de bicicletas conectado a una aplicación en la nube, una aplicación nativa para Android y aplicaciones para iOS para My Bike Valet. Diseñamos su arquitectura MVP, que proporcionó una base sólida y condujo a una expansión sencilla.
  • ¿Nativo o multiplataforma? Descubra la estrategia móvil ideal. En Redwerk, dominamos tanto las aplicaciones nativas como las multiplataforma. El desarrollo nativo es la mejor opción para obtener el máximo rendimiento y las funciones más avanzadas, mientras que el desarrollo multiplataforma ofrece precios más bajos y un mayor alcance. No tome esta decisión solo: los expertos de Redwerk le ayudarán a elegir el camino correcto.

Tanto si se enfrenta a una revisión crítica del código, como si necesita la orientación de expertos en Kotlin, si desea un desarrollo de ciclo completo o cualquier otra cosa intermedia, los expertos en Kotlin de Redwerk están a su disposición. Estamos ansiosos por conocer su visión y saber cómo podemos ayudarle. Póngase en contacto con nuestro equipo cuando lo desee.