Consejos para empezar a modular tu antigua app de Android

Compartir esta publicación

Contexto

Trabajamos con un proyecto legacy cuyo desarrollo comenzó hace más de 4 años por otra empresa con otros estándares, prácticas y experiencias.

En este proyecto trabajamos con sprints de 2 semanas, siempre entregando nuevas funcionalidades y corrigiendo errores, con scopes muy cerrados donde añadir tareas técnicas es algo difícil. Hemos ido añadiendo buenas prácticas habituales como testing y arquitectura clean, y también hemos añadido nuevos elementos dentro del entorno de desarrollo Android como Room o Jetpack Compose.

Ahora empezamos a afrontar una necesidad: empezar a modularizar la app de Android para obtener todas sus ventajas.

¿Por qué modularizar tu app de Android?

Los beneficios que podemos obtener al tener al modularizar nuestra app son varios:

  • Builds más rápidas. Gradle acelera el tiempo de compilación de tu proyecto al permitirte hacer tareas en paralelo, y reutilizar todo el código ya compilado sin modificarlo. Cuando empiezas un nuevo proyecto puede que no aprecies el tiempo de compilación, pero cuando tu proyecto crece es habitual que los tiempos de compilación ronden los 5 minutos.Para ajustar algún pequeño detalle de la vista que tengas que compilar entre 3 y 5 veces el proyecto puedes acumular mucho tiempo muerto.
  • Suele simplificar el desarrollo. Al trabajar en pequeños módulos centrados en una sola funcionalidad corregir, mantener y mejorar ese código será mucho más fácil. Solo por el simple hecho que acotas el tamaño del código, revisar o añadir test para empezar un refactor se hace más cómodo.
  • Reusar módulos entre apps. Quizás uno de tus módulos puede convertirse fácilmente en una librería y puedes abrirlo a la comunidad open source con todos los beneficios sin que afecte a toda tu app, tanto a nivel de seguridad como de integración.
  • Facilita añadir más personas al equipo. Este es uno de los puntos más importantes a nuestro entender. Como divides tu código en módulos más pequeños puedes asignar a personas o equipos a ellos directamente sin afectar al resto de la app. Por ejemplo, si tienes una función que es un chat y necesitas ayuda para llegar a un deadline con una funcionalidad, solo tener que entender ese módulo sin tener que entender el resto de la app hace que puedas recibir o dar ayuda mucho más rápido.
  • Mejora la automatización de tests. Al poder testear módulos sin tener que hacer todo el flujo, los tests irán más rápido y no se propagan los errores de los pasos anteriores. Puedes crear normas específicas en tu CI para agilizar tu tiempo de compilación. Por ejemplo, solo activar los test de UI, normalmente los más lentos, al hacer merge a tu rama principal de desarrollo.

¿Cómo podemos empezar a modularizar sin detener el desarrollo o que esto afecte a nuestra capacidad de entrega?

Afrontar un reto tan grande nos puede abrumar, pero el consejo más importante es tener paciencia. Al final encontraremos el primer paso del camino y poco a poco iremos añadiendo módulos hasta tener la app completamente modularizada.

En este artículo, no buscamos dar con una receta perfecta sobre cómo modularizar tu app. Cada proyecto es diferente y abordarlo con unos pasos estrictos puede ser más un problema que una solución. Por ejemplo, si nos dijeran que en nuestro proyecto empezaremos por la parte de networking sería un caos y decaeríamos en el intento al ver que tenemos algunas dependencias en analítica y muestra de errores. Solo crear estos dos módulos que utilizará el networking nos podría tomar un par de semanas en tenerlo todo testado y asegurar que no hemos roto nada. Y volvemos al contexto, no tenemos estas dos semanas para focalizarnos en tareas técnicas. Debemos adaptarnos a nuestra situación y buscar cómo añadir pequeños módulos para que al final, abordar networking sea mucho más trivial y poder realizarlo en los típicos finales de sprint con algún hueco para tareas externas.

  • Analiza tus dependencias. Busca una funcionalidad pequeña que tenga pocas dependencias, o mejor aún, ninguna. En nuestro caso fue toda la parte de programación funcional y todas las extensiones de kotlin que hemos creado a lo largo de estos años. Creamos el módulo, movimos todo el código allí, test incluidos, y compilamos. Eureka! funciono! ¿Fácil? Bueno tuvimos que cambiar todos los imports del proyecto, más de 100 archivos modificados, comprobar que no habíamos roto ningún test y que todas las funcionalidades de la app seguían intactas, pero teníamos nuestro primer módulo.
  • Testear antes de empezar. Una buena práctica que deberíamos tener en nuestros proyectos es tener una buena base de tests, pero a veces llegamos a un proyecto y no es así. Por lo tanto, si encontramos esa pequeña funcionalidad sin dependencias, antes de moverla al nuevo módulo, crearemos unos test que la envuelvan y migraremos todo al nuevo módulo sea, con suerte, cambiar imports y que todo funcione. Nosotros pudimos hacer el primer módulo porque ya teníamos una buena base de test qué cubrían toda esta parte.
  • Usar Gradle para componer dependencias. Uno de los inconvenientes que nos podemos encontrar al modularizar nuestro proyecto, puede ser la gestión de dependencias y versiones. Suele ser habitual el típico error de compilación de conflicto de versiones de la librería X de Android. Para ello podemos sacar provecho de Gradle para componer nuestros archivos build.gradle. En nuestro proyecto tenemos un fichero dependencies.gradle donde definimos primero las versiones de nuestras dependencias y después las dependencias en sí.

versions = [
"koin_version"              	: "3.1.4",
]

libs = [
    "koin"     	 : "io.insert-koin:koin-android:$versions.koin_version",
    "koinCore"   	 : "io.insert-koin:koin-core:$versions.koin_version",
    "koinJavaCompat" : "io.insert-koin:koin-android-compat:$versions.koin_version",
    "koinCompose"  : "io.insert-koin:koin-androidx-compose:$versions.koin_version",
]

Como podéis ver, al utilizar koin tenemos varias dependencias que comparten versión y si tuviéramos que actualizarla para solventar algún error de ella solo tenemos que venir aquí y no buscar en todos los ficheros Gradle que tenemos en cada módulo del proyecto.

Pero, si tenemos muchos módulos, tendremos muchos ficheros build.gradle y podemos sacar ventaja de la composición para ahorrarnos mucho código repetido. Si definimos un fichero con la configuración básica de todos los módulos de Android, lo único que tendremos que añadir en cada fichero es una línea de código y las dependencias particulares de ese.

Android_lib.gradle
apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'kotlin-kapt'
apply from: "$rootDir/buildsystem/dependencies.gradle"

android {
  compileSdkVersion configs.compileSdk

  defaultConfig {
	targetSdkVersion configs.targetSdk
	minSdkVersion configs.minSdk

	testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
	consumerProguardFiles "consumer-rules.pro"

  }

  buildTypes {

	debug {
  	debuggable = true
	}

	release {
  	minifyEnabled false
  	proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
	}
  }
  compileOptions {
	sourceCompatibility JavaVersion.VERSION_1_8
	targetCompatibility JavaVersion.VERSION_1_8
  }
  kotlinOptions {
	jvmTarget = '1.8'
  }
  sourceSets {
	main.java.srcDirs += 'src/main/kotlin'
	test.java.srcDirs += 'src/test/kotlin'
	androidTest.java.srcDirs += 'src/androidTest/kotlin'
  }
  buildFeatures {
	compose true
  }
  composeOptions {
	kotlinCompilerExtensionVersion '1.0.1'
  }
}



Con esta definición podemos componer nuestras dependencias en los nuevos módulos.

module.gradle


plugins {
  id 'com.android.library'
  id 'org.jetbrains.kotlin.android'
  id 'kotlin-kapt'
}

apply from: "$rootDir/buildsystem/android_lib.gradle"

android {
  buildFeatures {
	compose true
  }

  composeOptions {
	kotlinCompilerExtensionVersion '1.0.1'
  }
}

dependencies {

  implementation libs.core
  implementation libs.appcompat
  implementation libs.material

  //COMPOSE
  implementation libs.composeActivities
  implementation libs.composeMaterial
  implementation libs.composeConstraintLayout
  implementation libs.composeTooling
  implementation libs.composeRuntime
  implementation libs.composeViewModel

  //Room
  implementation libs.roomRuntime
  implementation libs.roomKtx
  kapt libs.roomCompiler

  //Koin
  implementation libs.koin
  implementation libs.koinJavaCompat
  implementation libs.koinCompose
  implementation libs.koinCore

  //TEST
  testImplementation testLibs.junit
  testImplementation testLibs.coroutinesTest
}

Incluso podemos beneficiarnos de gradle para componer aún más las dependencias y no tener que añadir o actualizar tantos ficheros cuando añadamos una nueva que compartan varios módulos.

  • Usa el Readme como base de conocimiento. Dentro de nuestros repositorios el archivo Readme.md suele contener pasos para compilar el proyecto, es lo más habitual en proyectos cerrados. La documentación más elaborada suele estar en soluciones externas como Confluence, Notion, etc. Una opción interesante es tener en cada módulo un archivo Readme que contenga una pincelada de información básica. Esta información puede ser una descripción de la funcionalidad del módulo, sus dependencias de otros módulos (con su link relativo para facilitar la navegabilidad), dependencias externas relevantes, posibles mejoras a realizar, etc. Esto facilita la entrada de nuevos miembros al equipo, solo con darle acceso al repositorio pueden navegar por una documentación simple sin tener que navegar por el código que es mucho más confuso e intimidante. Una consideración es que Android studio, a fecha de publicación de este artículo, no tiene un buen soporte para ficheros MD. Una buena alternativa es Visual Studio Code, dónde puedes previsualizar como se vería tu fichero en el repositorio.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Suscríbete a nuestro boletín de noticias

Recibe actualizaciones de los últimos descubrimientos tecnológicos

Acerca de Apiumhub

Apiumhub reúne a una comunidad de desarrolladores y arquitectos de software para ayudarte a transformar tu idea en un producto potente y escalable. Nuestro Tech Hub se especializa en Arquitectura de Software, Desarrollo Web & Desarrollo de Aplicaciones Móviles. Aquí compartimos con usted consejos de la industria & mejores prácticas, basadas en nuestra experiencia.

¿Tienes un proyecto desafiante?

Podemos trabajar juntos

Secured By miniOrange