Saltar al contenido
Development

Arreglar el load path de History, y sorprenderme comentando de más el arreglo

Por Victor Da Luz
iosswiftperformancedev-logdeep-cut-atlas

Esta app luego se renombró a Deep Cut Atlas. Aquí abajo se la llama “Discoverer” en todo momento, porque así se llamaba el día que pasó esto.

Tercera pasada de rendimiento esta semana en Discoverer, esta vez en la pestaña History. La misma revisión que detectó los problemas de la pestaña Discover señaló tres problemas más aquí también, todos en el código que carga y pagina las canciones reproducidas recientemente.

El primero ya resultaba familiar: tres fetches que deberían haber sido independientes se ejecutaban uno tras otro. El contenido de la playlist, luego un escaneo completo de las claves de álbumes de la biblioteca, y después la primera página de canciones reproducidas recientemente. Todo secuencial, todo bloqueando que la pantalla mostrara algo. La sorpresa esta vez fue que uno de esos tres fetches, el escaneo de claves de biblioteca, ni siquiera se necesitaba para renderizar la lista. Solo importaba después, para habilitar un botón en una hoja de detalle que se abre al tocar una fila. Se cargaba al principio por pura costumbre, no porque la pantalla lo necesitara.

El arreglo tuvo dos partes. Primero, lanzar el fetch de la playlist y el de la primera página al mismo tiempo usando una tarea simple sin estructurar, el mismo workaround de hace unos días, ya que la sintaxis más limpia async let de Swift no funciona contra este tipo de servicio de main-actor. Segundo, hacer que el escaneo de claves de biblioteca corra de forma perezosa, una vez por sesión, después de que la pantalla ya esté mostrando algo. Ya existía un ejemplo funcional de este mismo patrón en otra parte de la app, en la pestaña Playlist, así que copié su forma en vez de inventar una nueva.

Los otros dos problemas eran más chicos pero mostraban la misma forma de error: hacer trabajo repetido que podría haberse hecho una sola vez. Un punto recalculaba una lista filtrada cada vez que una fila entraba en la vista al hacer scroll, en vez de una sola vez por dibujo de pantalla. Otro recalculaba un conteo acumulado desde cero en cada página de resultados en vez de simplemente mantener un total corriente. Ninguno de los dos es dramático en una lista pequeña. Ambos se convierten en una demora real y perceptible a medida que la lista crece, porque el costo escala con el cuadrado del tamaño de la lista en vez de linealmente.

La parte que no esperaba terminar escribiendo

A mitad de camino, mis arreglos de una línea habían acumulado un pequeño ensayo de comentarios de “por qué” pegado a cada uno, explicando el razonamiento detrás de cada cambio. En el momento se sintió responsable. Después el archivo activó una regla del linter sobre cantidad de líneas y tuve que volver atrás y recortar casi todo lo que acababa de escribir, función por función, solo para que el archivo volviera a estar bajo el límite. Ahí fue cuando cayó la ficha: no estaba escribiendo comentarios porque el código los necesitara. Los estaba escribiendo porque acababa de terminar de razonar algo complicado, y escribirlo se sentía como prueba del trabajo hecho. El código no se volvió más difícil de leer sin ellos. Si acaso, algunas de las versiones recortadas se leían más limpias, porque un comentario que solo repite “hacemos X porque Y” a menudo significa que X e Y deberían haber tenido un mejor nombre desde el principio.

Terminé escribiéndome una regla más estricta al respecto antes de seguir: antes de agregar cualquier comentario, verificar si quitarlo realmente pierde información que quien lee no puede obtener de otra forma, si cubre una excepción genuina en vez de lógica de rutina, y si un mejor nombre lo hubiera vuelto innecesario. Si no pasa las tres pruebas, no se escribe. Algo pequeño, pero ya está cambiando cómo se ve el próximo diff.

Lecturas relacionadas