#ModoFoco: Reduciendo el Tamaño de PedidosYa Android

Jossue Diaz Perez
PeYa Tech
Published in
5 min readMar 12, 2024

En el mundo de las aplicaciones móviles, donde cada segundo cuenta y la experiencia del usuario es fundamental, el mantra de “menos es más” resuena más que nunca. La búsqueda de eficiencia toma el centro del escenario y uno de los aspectos en el que pusimos la lupa fue en el tamaño de nuestra aplicación.

Acompañanos en esta entrada de blog mientras exploramos los distintos enfoques que aplicamos para lograr una reducción de tamaño de más del 60%.

Como todo buen storytelling, empecemos seteando el contexto:

La Era Del Híper Crecimiento En PedidosYa

El tamaño de la aplicación comenzó a aumentar significativamente entre 2020 y 2022; años en los que se sumaron gran cantidad de funcionalidades para brindar la mejor experiencia en los 15 países en los que tenemos presencia actualmente.

Fueron años de mucha experimentación y rediseño, en donde la velocidad de entrega de valor primó por sobre el foco de optimizar el tamaño de nuestra aplicación.

Si bien ya habíamos adoptado hacía tiempo el formato de publicación app bundle, validábamos el peso de SDKs de terceros y teníamos métricas para conocer su evolución, nos debíamos un deep-dive para asegurar que no sobrara nada en nuestro compilado final.

Resolución de año nuevo — Bajar de Peso

El año 2023, trajo consigo la decisión de reducir el tamaño de la aplicación y lo haríamos en dos etapas.

Primera Etapa:

  • Eliminar código y librerías obsoletas
  • Optimizar los recursos y librerías que utilizamos
  • Implementar un proceso formal de actualización de librerías, relevando cómo afecta el tamaño de la aplicación entre otras métricas al sumar nuevos SDKs

Segunda Etapa:

  • Implementar R8

Consideramos que la primera etapa fue clave porque los equipos de ingeniería revisaron con sumo detalle sus módulos y pudieron eliminar sin riesgos código muerto perteneciente a funcionalidades deprecadas o que habían formado parte de un experimento A/B y no habían sido eliminadas finalizado dicho experimento.

Este esfuerzo mayoritariamente manual nos trajo una reducción importante como podemos observar (alrededor de un 22%) pero consideramos que no era suficiente.

Comparación después de aplicar los cambios de la primera etapa.

Implementando R8

Aunque ya teníamos identificado implementar R8, era clave una correcta planificación y trazabilidad de los cambios para mitigar posibles riesgos en cuanto a la estabilidad de nuestra aplicación.

Pero, ¿qué es R8?

R8 es una potente herramienta de compilación que convierte el código de bytes de java a código .dex. Tiene muchas características de Proguard pero a diferencia de este, R8 está enfocado en la plataforma Android.

Los procesos que R8 realiza son

  • Reducción de código: Detecta y elimina de forma segura clases, métodos, atributos, anotaciones, entre otros que no son referenciados o identificados como entry point en la aplicación y en librerías. Por ejemplo, si sólo se utiliza una API de una librería, R8 la identifica y elimina el resto de código que no se usa.
  • Reducción de recursos: Elimina los recursos no utilizados en la aplicación y en librerías. Durante la reducción de código es posible también que queden recursos no referenciados por lo que esta etapa trabaja en conjunto con la etapa anterior.
  • Ofuscación: Acorta los nombres de las clases y sus miembros siempre que sea posible, por lo que contribuye a la seguridad de la aplicación pues hace más difícil que se le aplique ingeniería inversa.
  • Optimización: Inspecciona y reescribe el código para reducir aún más. Por ejemplo, si detecta que existe una rama else {} para una condición determinada, pero esta rama nunca se ejecuta, R8 elimina este código. También utiliza reglas de optimización avanzada, por ejemplo si detecta que un método solo se usa desde un lugar incluye el bloque del método dentro de ese lugar. Esta mejora se centra en mejorar la eficiencia y reducir la complejidad del código, lo que resulta en un impacto positivo en la velocidad de ejecución de la aplicación.

Como podemos deducir R8 es bastante invasivo con nuestro código. R8 no es capaz de identificar con certeza las referencias a clases o métodos que utilizan reflexión. Este defecto puede causar errores en tiempo de ejecución mayormente.

Reflexión: es una característica del lenguaje que permite realizar operaciones con clases y objetos en tiempo de ejecución sin que exista una referencia al código de la clase u objeto.

Para prevenir estos errores, se pueden aplicar reglas personalizadas con el objetivo de conservar clases, miembros, nombres, definir modificadores, aplicar comodines, filtros y otros patrones similares a Proguard. Por la complejidad técnica de estos temas no vamos a cubrirlos en este post, pero pueden pedirlas en los comentarios 🙂.

Otro tipo de errores suceden durante la ejecución de métodos desde el lado de nativo a través de JNI, ya que R8 no está diseñado para trabajar con código nativo y es necesario indicarle que conserve estas clases. No obstante no hemos tenido errores de este estilo.

Para llevar a cabo la implementación de R8 diseñamos un plan en el que pudiéramos iterar las reglas con mejoras, respetando el principio de crear reglas que solucionen un problema y que al mismo tiempo no limitara que ciertas clases o miembros se pudieran ver optimizados.

Un dato de color: Cuando empezamos con R8 lo primero que hicimos fue crear un archivo que no tenía reglas personalizadas, solo las que vienen por defecto del SDK para dejar que R8 hiciera la mayor cantidad de optimizaciones, y la aplicación directamente no abría 🙂. Poco a poco fuimos descubriendo y corrigiendo estos errores.

Inicialmente generamos compilaciones y las distribuimos internamente en nuestro equipo. Luego ampliamos la población y aplicamos las reglas a nuestro Nightly build.

Cada etapa fue iterativa con mejoras incrementales, correcciones y ajustes hasta que finalmente salimos a producción en diciembre teniendo el siguiente resultado: una reducción de un 51%🎉

En esta imagen podemos ver el efecto que tuvo aplicar R8 con sus reglas en el release de producción.
En esta imagen podemos ver el efecto que tuvo aplicar R8 con sus reglas en el lanzamiento de producción.

Cuando aceptamos este apasionante desafío, la aplicación pesaba aproximadamente 55 MB, y finalmente logramos reducir y mantener estable un peso aproximado de 20 MB.

Algo que no podemos dejar de mencionar son los beneficios de la optimización de código que realiza R8, los cuales han mejorado el rendimiento de procesamiento interno de la aplicación entre un 10% y 20%.

Todas las bondades de R8 se incluyen automáticamente con la adición de nuevas funcionalidades, sin necesidad de realizar acciones manuales.

Este hito sólo fue posible con la colaboración de todos los equipos de ingeniería de PeYa, el cual estuvo atento a validar sus funcionalidades a medida que fuimos ajustando las reglas de R8 🦾🦾.

¡Nos encantaría conocer tu opinión, los/las leemos!

--

--