Lista de verificación para la revisión del código de Android: Arquitectura para la publicación

Un error detectado durante la revisión de código cuesta minutos. El mismo error detectado después del lanzamiento en Google Play Store afecta la tasa de usuarios sin fallos, el posicionamiento en la tienda y, a veces, incluso el fin de semana. Google reduce la visibilidad de cualquier aplicación que supere una tasa de fallos percibida por el usuario del 1,09 %, lo que convierte la revisión en el último punto de control discreto antes de que la exposición se haga pública.

Esta lista de verificación para la revisión de código de Android es la que utilizan nuestros revisores cuando un cliente abre una solicitud de extracción en un código base de Android. Codifica las mejores prácticas de revisión de código que nuestro equipo ha perfeccionado en auditorías de aplicaciones en producción. Una lista de verificación sólida para la revisión de código comienza con principios universales como la arquitectura en capas, la nomenclatura y la cobertura de pruebas. En Android, estos principios se ajustan a un conjunto específico de modos de fallo relacionados con el ciclo de vida, los subprocesos y la ruta de publicación en Google Play Store.

Antes de que comience la reseña

Una revisión productiva del código de Android comienza antes de que nadie abra el código. El revisor necesita contexto, una compilación limpia y un alcance manejable. Confirmamos tres cosas de antemano:

  • Alcance y objetivo. Una corrección de errores, una rama de desarrollo de nuevas funcionalidades, una refactorización y una auditoría de una versión candidata merecen un análisis diferente. Le preguntamos al autor qué cambios se produjeron, qué riesgos conllevan y qué priorizar.
  • La integración continua (CI) está en verde. Lint, Detekt, Ktlint y las pruebas unitarias deben pasar antes de que un humano toque el código.
  • El conjunto de cambios es revisable. El estudio clásico de Cisco sobre la revisión de código entre pares demostró que la detección de defectos disminuye drásticamente por encima de las 400 líneas por sesión. Una revisión de 3000 líneas se convierte en una mera formalidad.

También leemos primero el ticket y los mensajes de confirmación. La mitad del ruido en la mayoría de las revisiones proviene de revisores que preguntan “¿por qué?” cuando la respuesta estaba en Jira.

Lista de verificación para la revisión del código de Android: Arquitectura para la publicación

Revista de Arquitectura

La arquitectura es la capa donde los pequeños errores se convierten en reescrituras que duran varios trimestres. La mayoría de los problemas que encontramos en las auditorías de clientes se remontan a uno de estos tres patrones: la lógica de negocio se filtra en la interfaz de usuario, se omite la inyección de dependencias o un “ViewModel” que se ha convertido silenciosamente en un objeto todopoderoso.

Separación de capas

La lógica de dominio pertenece a los casos de uso o a los interactores. Activity, Fragment y las funciones componibles deben mantenerse al margen de las reglas de negocio. Los repositorios gestionan las fuentes de datos y los modelos de vista coordinan el estado. Una Activity que llama directamente a Retrofit siempre se convierte en candidata a refactorización.

Inyección de dependencias

Hilt o Koin deberían encargarse de la configuración del grafo. La instanciación manual de dependencias dentro de las clases de producción es una señal de alerta, especialmente para cualquier cosa que necesite probarse de forma aislada. La inyección de dependencias centralizada también abarata la modernización del código heredado posteriormente, ya que el cambio de implementaciones se convierte en una modificación a nivel de módulo en lugar de una búsqueda exhaustiva en todo el código.

Señales de alerta de la modularización

Los módulos de características deben depender de :core y :domain. Las dependencias entre características se transmiten a través de una capa compartida. Las dependencias circulares deben provocar un fallo en la compilación. También señalamos los antipatrones arquitectónicos que observamos con mayor frecuencia en las revisiones de código con IA: componentes componibles que compilan pero rompen el contrato de estabilidad de Compose, repositorios que eluden la arquitectura en capas y ViewModels que absorben lógica que debería pertenecer a la capa de casos de uso.

Modismos de Kotlin en código Android

Kotlin ofrece a los revisores herramientas que Java no tenía. La mayoría de las bases de código modernas de Android combinan ambos lenguajes, especialmente después de una migración. Para la parte Java de una base de código mixta, nuestra lista de verificación de revisión de código Java cubre las comprobaciones específicas del lenguaje. A continuación, se abordan los patrones de Kotlin que se relacionan con el ciclo de vida y la gestión de subprocesos de Android.

Buscamos estos modismos:

  • val por defecto, var solo cuando el estado realmente muta
  • Seguridad nula sin !!. Las llamadas seguras, el operador Elvis y requireNotNull con un mensaje significativo son los valores predeterminados
  • Clases selladas o interfaces selladas para el estado de la interfaz de usuario. Una data class con siete campos que admiten valores nulos es candidata a refactorización
  • Las funciones de alcance se utilizan para aclarar la intención en lugar de anidar hasta cuatro niveles de profundidad
  • Colecciones elegidas para el patrón de acceso. Un escaneo lineal de una List dentro de un bucle caliente debe ser un Set

Un ejemplo preciso del patrón que queremos observar:

sealed interface OrderListUiState {
    data object Loading : OrderListUiState
    data class Ready(val orders: List) : OrderListUiState
    data class Error(val message: String) : OrderListUiState
}

class OrderListViewModel(
    private val repository: OrderRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(OrderListUiState.Loading)
    val uiState: StateFlow = _uiState.asStateFlow()

    fun refresh() {
        viewModelScope.launch {
            _uiState.value = runCatching { repository.getOrders() }
                .fold(
                    onSuccess = { OrderListUiState.Ready(it) },
                    onFailure = { OrderListUiState.Error(it.message.orEmpty()) }
                )
        }
    }
}

El equivalente heredado normalmente utiliza LiveData<Order>?, una cadena de error que puede ser nula, y un !! en el value. El trabajo del revisor es detectar ese patrón antes de que se fusione.

Reseña de Jetpack Compose

Compose cambió lo que busca un revisor. Los patrones XML y Fragment siguen siendo relevantes para las pantallas antiguas, pero el nuevo código pone de relieve el costo de la recomposición, la elevación del estado y el manejo de efectos secundarios.

Para el nuevo código Compose, comprobamos lo siguiente:

  • El estado se eleva al nivel correcto. Un TextField no debería tener su propio estado si la pantalla principal lo necesita
  • No hay lógica de negocio dentro de las funciones @Composable. Los Composables describen el estado de la interfaz de usuario. No llaman a repositorios ni realizan operaciones de entrada/salida
  • LaunchedEffect, DisposableEffect y SideEffect se utilizan correctamente. Una corrutina lanzada en un cuerpo componible sin LaunchedEffect presenta fugas en la siguiente recomposición
  • remember y derivedStateOf protege los cálculos costosos de volver a calcularlos
  • LazyColumn y LazyRow usan claves estables. Una LazyColumn sin key = { it.id } reorganiza los elementos componibles en cada cambio de datos
  • Existen vistas previas para los estados de carga, éxito y error

Para las pantallas basadas en XML, la regla no cambia: ViewBinding en lugar de findViewById, diseños planos, sin lógica de negocio dentro de onCreateView.

Revisión de trabajo asíncrono

Los errores asíncronos son el tipo de defecto de Android más costoso que encontramos en las auditorías. Son intermitentes en control de calidad, deterministas en producción y, con frecuencia, invisibles en los registros. La labor del revisor consiste en detectarlos durante la revisión.

Lo que buscamos:

  • No se debe usar GlobalScope en el código de producción. viewModelScope, lifecycleScope o un ámbito inyectado vinculado a un componente Hilt son las opciones correctas
  • Los despachadores hacen coincidir el trabajo y se inyectan para que las pruebas puedan sustituirlos
  • Se respeta la cancelación. Los trabajos de larga duración utilizan API de suspensión cancelables o comprueban isActive en bucles
  • No hay runBlocking en el hilo principal. Nunca
  • StateFlow para el estado de la interfaz de usuario, SharedFlow para eventos puntuales, Flow frío para lecturas del repositorio
  • Las excepciones cruzan los límites de las corrutinas de forma segura. CoroutineExceptionHandler y supervisorScope son elecciones deliberadas que el revisor debería ver utilizadas a propósito

Un revisor que pueda leer viewModelScope.launch(Dispatchers.IO) { repository.fetch() } y detectar los cuatro errores que contiene merece ser mantenido en cada lanzamiento.

Revisión de memoria y rendimiento

Los problemas de rendimiento se ocultan en pequeños errores. Las fugas de memoria se esconden en partes que, aisladas, parecen correctas. Un revisor sénior lee el código con un analizador de rendimiento en la cabeza.

Fugas del ciclo de vida

Los objetos de larga duración, como los singletons, los repositorios con ámbito de aplicación o los campos estáticos, nunca deben mantener una referencia a una Activity, Fragment o View. Las clases internas dentro de los propietarios del ciclo de vida suelen infringir este principio. Todo lo que se registre en una función de devolución de llamada del ciclo de vida también debe anularse cuando finalice dicho ciclo.

Anulación de ViewBinding

En los Fragmentos, establecer _binding = null en onDestroyView es obligatorio. Este sencillo patrón evita la fuga de memoria más común en el código Android moderno. El revisor debería verlo en cada Fragmento que utilice ViewBinding, sin excepción.

También revisamos el resto de la capa de rendimiento en prosa. La carga de imágenes debe usar una biblioteca real (Coil, Glide, Picasso) en lugar de un decodificador manual que bloquea el hilo principal. Los adaptadores de RecyclerView deben usar DiffUtil o ListAdapter en lugar de notifyDataSetChanged(). LeakCanary debe estar presente en cada compilación de depuración, y cualquier fuga que detecte se corrige antes de que se implemente el cambio.

Revisión de seguridad

La seguridad móvil es un área donde un solo descuido puede costar más que un año de trabajo minucioso. Los informes de Verizon de 2025 sobre seguridad móvil y filtraciones revelaron que el 85 % de las organizaciones reportaron un aumento en los ataques a dispositivos móviles, y que el 88 % de las filtraciones de aplicaciones se realizaron mediante credenciales robadas. El manejo de secretos y la definición del alcance de los permisos son aspectos imprescindibles en cualquier lista de verificación de revisión de código para Android.

Manifiesto y permisos

android:exported debe establecerse explícitamente en cada Activity, Service, Receiver y Provider con un filtro de intención. Android 12 y versiones posteriores lo requieren, y el valor predeterminado seguro es false. Cada <uses-permission> debe corresponder a una función que la aplicación utilice realmente. networkSecurityConfig no debe permitir el tráfico en texto plano en las versiones de producción.

Secretos y almacenamiento de datos

Las claves API, las claves de firma, los secretos de cliente OAuth y las claves de servidor de Firebase deben almacenarse en almacenes de secretos de CI. El archivo gradle.properties incluido en Git es una vía de fuga que solemos detectar durante los servicios de revisión de código. Los datos confidenciales en tiempo de ejecución utilizan EncryptedSharedPreferences para las credenciales, Android Keystore para las claves criptográficas y almacenamiento interno para los archivos privados de la aplicación. Se requiere TLS 1.2 como mínimo.

Se debe habilitar R8 para las compilaciones de lanzamiento con reducción y ofuscación activadas. Es necesario revisar las reglas de ProGuard para evitar directivas -keep demasiado amplias, y el código exclusivo de depuración debe estar restringido por variante de compilación para que nunca llegue a la versión final.

Preparación para el lanzamiento

Algunas de las preocupaciones de la revisión solo se aplican a los cambios que afectan la ruta de lanzamiento. Les damos una oportunidad aparte porque el costo de un error es alto y los equipos generalmente los detectan tarde.

Verificamos lo siguiente:

  • minSdkVersion, targetSdkVersion y compileSdkVersion cumplen con los requisitos actuales de Play Store. Google aumenta los mínimos de targetSdkVersion anualmente, y no cumplir con el límite impide nuevas cargas
  • El artefacto de lanzamiento es un paquete de aplicación de Android (.aab), el formato de carga requerido para las nuevas aplicaciones
  • R8 reduce visiblemente el tamaño del paquete en CI. Un aumento de 20 MB por un pequeño cambio merece cinco minutos de investigación
  • Los perfiles de referencia se regeneran cuando se producen cambios en el código crítico para el inicio
  • Crashlytics, Firebase Performance Monitoring y los informes previos al lanzamiento de Play Console están integrados en la variante de lanzamiento
  • Las cadenas de texto se traducen para cada idioma en el que se distribuye la aplicación. La regla de lint para traducciones faltantes debería provocar fallos en las compilaciones de lanzamiento en lugar de emitir una advertencia que el equipo aprenda a ignorar

Herramientas de análisis estático

Las herramientas automatizadas detectan los problemas a los que los humanos no deberían dedicar tiempo. Cuatro de ellas son imprescindibles en cualquier canalización de integración continua (CI) de Android. Se superponen, se complementan entre sí y ninguna reemplaza a las demás. Las directrices de revisión de código funcionan mejor cuando estas herramientas se encargan de las comprobaciones técnicas y los revisores se centran en la arquitectura y la intención.

Herramienta
Lo que hace
Donde brilla
Herramienta

Lint de Android

Lo que hace

Comprobador nativo de plugins Gradle para Android. Analiza Kotlin, Java, XML y recursos en busca de problemas a nivel de plataforma.

Donde brilla

Permisos, API obsoletas, problemas de diseño, traducciones faltantes, problemas con el manifiesto.

Herramienta

Detekt

Lo que hace

Análisis estático para Kotlin. Analiza el AST de Kotlin, por lo que comprende las corrutinas, las clases selladas y las características modernas del lenguaje.

Donde brilla

Mal olor del código, complejidad ciclomática, convenciones de nomenclatura, conjuntos de reglas personalizadas.

Herramienta

Ktlint

Lo que hace

Formateador y verificador de estilo con opiniones propias para Kotlin. Configuración mínima, valores predeterminados sensatos.

Donde brilla

Consistencia de formato, orden de importación, longitud de línea. Se complementa con Detekt en lugar de reemplazarlo.

Herramienta

R8

Lo que hace

Optimizador, reductor y ofuscador de código de Google. Sustituye a ProGuard.

Donde brilla

Reducción del tamaño de la compilación de lanzamiento, eliminación de código muerto, ofuscación de identificadores.

Si se combinan estas funciones con las inspecciones integradas de Android Studio, las advertencias aparecerán directamente en la revisión, donde los revisores las verán.

Por qué es importante una perspectiva externa

La revisión interna de código es una cultura. La revisión externa de código es un servicio. La lista de verificación anterior funciona mejor cuando la aplica alguien ajeno al equipo que escribió el código, porque los revisores internos normalizan patrones con los que el código base ha convivido durante meses.

Tres momentos en los que contratar a un revisor externo justifica la inversión:

  • Antes de un lanzamiento importante, cuando el equipo está demasiado cerca del código como para darse cuenta de aquello a lo que se han acostumbrado.
  • Tras heredar un código fuente de un proveedor anterior, un profesional independiente o una adquisición.
  • Cuando el código asistido por IA se ha integrado rápidamente y el equipo necesita una revisión exhaustiva antes de que comience el tráfico de producción.

Si tienes un código fuente de Android que quieras que revise un experto, ponte en contacto con nosotros y elaboraremos una revisión basándonos en esta lista de verificación.

Vea cómo auditamos una aplicación de mapeo de redes multiplataforma antes de su lanzamiento y ayudamos a lograr un 90 % de mantenibilidad del código

Este campo es obligatorio. no es un correo electrónico comercial