https://d226lax1qjow5r.cloudfront.net/blog/blogposts/improve-your-software-project-part-one-understanding-a-codebase/making-projects-better_part-one.png

Mejore su proyecto de software - Primera parte: Comprender una base de código

Publicado el November 15, 2022

Tiempo de lectura: 8 minutos

¿Alguna vez te has hecho cargo de una base de código y te has dado cuenta de que no estás contento con cómo está escrito u organizado? Es una historia común, pero que puede causar muchos dolores de cabeza. La deuda técnica puede convertirse en una bola de nieve que dificulte exponencialmente la comprensión del código y la incorporación de nuevas funciones.

En esta serie de tres partes, voy a recorrer algunas de las cosas clave que usted querrá hacer para ser más feliz con su brillante (viejo) proyecto. Para dar algunos ejemplos concretos, voy a atar todo junto explicando cómo he refactorizado y mejorado el código abierto Vonage Python SDKuna biblioteca que realiza llamadas HTTP a las API de Vonage, pero los principios se aplican a cualquier tipo de proyecto de software.

Los ejemplos de este post estarán escritos en Python, pero estos principios se aplican a proyectos en cualquier lenguaje. También hay una práctica lista de comprobación si estás intentando arreglar específicamente un proyecto en Python.

La serie, por secciones

  1. Primera parte: Entender una base de código (este artículo)

  2. Segunda parte: Hacer cambios

  3. Tercera parte: Mejoras de siguiente nivel

¿Qué abarca esta serie?

En esta serie hablaremos de:

  1. Familiarizarse con el código

  2. Adquirir confianza para realizar cambios y abordar la deuda técnica

  3. Generar confianza con su jefe, equipo y clientes/comunidad

  4. Realización de mejoras

  5. Qué hacer cuando llega el momento de entregar el proyecto

Al final de cada artículo, dispondrás de algunas estrategias para enfrentarte tú mismo a esta situación y te sentirás capacitado para hacerlo.

Sin más dilación, sigamos con la primera parte...

Primera parte: Entender el proyecto

¡Leer Cosas!

Lo primero que hay que hacer es intentar entender qué hace el código que has heredado y cómo está organizado.

Empieza por hablar con alguien que conozca el proyecto y leer toda la documentación disponible. En mi caso, había un readme en el repositorio que me sirvió de punto de partida. Proporcionaba una buena instantánea del estado del código cuando se actualizó por última vez.

Image of readme

También había documentación del producto que me ayudó a entender lo que se esperaba que hiciera el código, ya que describía las API a las que tenía que llamar y su comportamiento actual.

Image of main docs page

¡Construye cosas!

Una vez que tengas una idea de lo que hace el código, el siguiente paso es explorarlo construyendo algún tipo de proyecto "Hello, World" - un proyecto sencillo que utilice el código y haga algo pequeño, pero útil. Como mi proyecto llama a APIs, escribí un trozo de código muy sencillo que me envía un SMS.

import vonage


client = vonage.Client(key=MY_KEY, secret=MY_SECRET)

client.sms.send_message({
    "from": "Max",
    "to": MY_NUMBER,
    "text": "Hello, world!",
})

¡Funcionó!

Screenshot of my phone with a new message

Juega con tu código base y construye tu propio "Hola, mundo".

Comprender cómo está estructurado el código

Es importante entender cómo está organizado el código del proyecto para que puedas conceptualizar más fácilmente lo que ocurre cuando se ejecuta tu "Hola, mundo". También es una buena forma de practicar tus habilidades de pensamiento arquitectónico, ya que tendrás que desarrollar un modelo mental de cómo funciona el código. Esta forma de pensar te ayudará a dividir el proyecto en distintas piezas más fáciles de entender, lo que también te ayudará más adelante cuando quieras reducir el acoplamiento de código y otros efectos secundarios.

En el caso de nuestro SDK, el código se organizó en seis archivos separados (Python es un lenguaje muy compacto, ¡la base de código Java equivalente es unas 10 veces mayor!), que tienen este aspecto:

  • Un archivo para inicializar el proyecto y gestionar las importaciones,

  • Un archivo para contener métodos internos,

  • Un archivo que contenía clases de error personalizadas, y

  • Tres archivos que contenían cada uno una clase relativa a una de las API de Vonage

Image showing the different files with the descriptions above applied to them

Las alarmas empezaron a sonar cuando me di cuenta de que había 3 archivos con nombres de APIs de Vonage, pero el README afirmaba que se admitían 12 APIs diferentes.

Me di cuenta de que la mayor parte del código estaba en un archivo (__init__.py) que normalmente se utiliza sólo para las importaciones, en una gran clase que se ocupaba de todo. Como esto no ayudaba para nada con la estructura, decidí mirar la estructura de las pruebas para obtener más información sobre cómo estaba organizado el código.

Las pruebas estaban agrupadas de forma razonable en módulos, lo que me ayudó a comprender los distintos componentes en juego. Yo recomendaría intentar comprender la estructura tanto del código como de las pruebas, ya que ambas pueden ser muy útiles.

Screenshot of the tests folder

Prepárese para el desarrollo y ejecute las pruebas.

Ahora, es el momento de instalar las dependencias del proyecto y ejecutar las pruebas. Si pasan, usando la última versión de tu lenguaje y dependencias, ¡buenas noticias! Las mías no lo hicieron, así que empecé con las versiones exactas de las dependencias que se mencionaban e incrementé las dependencias para averiguar cuáles no funcionaban bien con la última versión de mi lenguaje de programación.

En esta situación, actualice las dependencias de forma incremental y los problemas deberían aparecer por sí solos. Una de tus dependencias probablemente tuvo una versión con un cambio de ruptura desde la última vez que se utilizó la base de código. (En mi caso, una dependencia cambió la forma en que devolvía datos entre versiones, así que tuve que reescribir un par de pruebas para manejar los datos correctamente).

Herramientas que pueden ayudarle

En esta sección se mencionan algunas formas de utilizar herramientas para ponerse al día en un proyecto. El uso de herramientas de análisis de código puede ser extremadamente útil porque su uso puede automatizarse, lo que significa que puedes ejecutar las herramientas tan a menudo como quieras y realizar un seguimiento de tu progreso a medida que empiezas a mejorar el código base.

Las herramientas también son muy útiles a la hora de planificar el trabajo, ya que la información que proporcionan te dirá dónde se encuentran la deuda técnica y los puntos conflictivos del código, y te sugerirán cómo priorizar tu tiempo.

Herramientas de análisis

El análisis estático es una forma de analizar automáticamente el código fuente sin tener que ejecutarlo. Puede dar una idea de la estructura de una base de código, poner de relieve la duplicación y otros objetivos de refactorización, así como advertirle de cualquier vulnerabilidad potencial. Hay herramientas gratuitas en línea para la mayoría de los lenguajes, y muchos proveedores que cobran por el servicio tienen un nivel gratuito para proyectos no comerciales o de código abierto, por ejemplo sonarcloud.

El análisis del comportamiento se refiere a aprender sobre un proyecto basándose en el historial de commits. Esto le puede decir quién trabajó en el proyecto y cuándo, qué se cambió, y qué componentes se cambian juntos con frecuencia, así como una serie de otras ideas. Es un método muy útil para proyectos grandes con muchos committers. CodeScene tiene un nivel gratuito para proyectos de código abierto y funciona bien.

Cobertura de las pruebas

La cobertura de las pruebas (el porcentaje de sentencias de código cubiertas por las pruebas) es útil para saber exactamente qué prueban las pruebas unitarias. También puede poner de relieve las áreas de un código que no se prueban. Existen herramientas para la mayoría de los lenguajes; para Python, recomiendo cobertura.

Image of test coverage outputs

Puntuación de la mutación

La cobertura de las pruebas puede decirle qué parte de su código está cubierta por pruebas, pero esto no le dice lo buenas que son sus pruebas a la hora de comprobar realmente el comportamiento de su código. Las pruebas de mutación proporcionan más información sobre hasta qué punto puedes confiar en que tus pruebas hagan su trabajo y se aseguren de que tu código funciona según lo previsto.

Funciona tomando sentencias de tu código y cambiándolas ligeramente, por ejemplo, cambiando una cadena/cambiando un más por un menos, etc. para producir muchas versiones "mutantes" de tu código. A continuación, el conjunto de pruebas se ejecuta con cada una de estas versiones mutantes del código. Como las cosas han cambiado, esperamos que las pruebas fallen. Pero si las pruebas pasan a pesar de los cambios, el mutante se ha escapado y estos pequeños cambios podrían haber llegado a producción. Por tanto, la puntuación de la mutación (relación entre los mutantes detectados y el número total de mutantes creados) nos indica el grado de confianza que debemos tener en nuestras pruebas.

Existen versiones en muchos idiomas, entre ellas Stryker que tiene soporte para Javascript, Node.js y C#. En Python, recomiendo probar mutmutque es simple y eficaz.

Image of mutation score output

Comprenda la pila de llamadas perfilando su muestra.

Yo recomendaría intentar comprender la pila de llamadas que se produce cuando se ejecuta tu "Hola, mundo". Existen herramientas en la mayoría de los lenguajes para hacerlo.

En Python, me gusta recomendar una herramienta llamada Snakeviz para mostrar tu pila de llamadas visualmente. Pon tu código "Hola, Mundo" en una función y perfílala, así:

with cProfile.Profile() as pr:
    send_sms() # This function is where the Hello, World code lives
stats = pstats.Stats(pr)
stats.dump_stats(filename='send_sms.prof')

Esto genera un archivo llamado send_sms.prof. Si ejecutas esto en la línea de comandos con Snakeviz...

python -m pip install snakeviz snakeviz send_sms.prof

...generará un gráfico de carámbano interactivo que le mostrará todas las funciones a las que llama su función, todas las funciones que y así sucesivamente. Esto puede ser útil para ayudarle a seguir el camino de la computadora a través del código, y puede arrojar luz sobre cómo funciona.

Image of an icicle plot of my profiled function

¿Y ahora qué?

Si sigues las sugerencias de este artículo, estarás en una posición inmejorable para empezar a generar confianza, abordar la deuda técnica y realizar cambios en tu código base. Vuelve pronto para leer la segunda parte, en la que hablaremos de todo esto en detalle.

Mientras tanto, puedes ponerte en contacto con nosotros en nuestro Slack de la comunidad de Vonage o enviarnos un mensaje en Twitter.

Compartir:

https://a.storyblok.com/f/270183/400x400/92109caf6a/max-kahan.png
Max KahanVonage Antiguo miembro del equipo

Max es un defensor de los desarrolladores de Python e ingeniero de software interesado en las API de comunicaciones, el aprendizaje automático, la experiencia de los desarrolladores y el baile. Su formación es en Física, pero ahora trabaja en proyectos de código abierto y hace cosas para mejorar la vida de los desarrolladores.