Si buscáis qué significa “zen” en Google os encontraréis con muchos resultados que explican el origen de dicho término así como su significado desde múltiples puntos de vista. Sin embargo, nuestra idea para el artículo de hoy no es para nada filosófica ni mucho menos religiosa. Tampoco es que hayamos decidido usar un título clickbait, sino que hoy os queremos presentar un conjunto de buenas prácticas que el bueno de Dave Cheney ha nombrado así: el Zen de Go.
¿Quién es Dave Cheney?
Es altamente probable que ya sepáis quién es Dave Cheney, así que, si ese es vuestro caso, podéis omitir esta sección. Sin embargo, si no sabéis de quién estamos hablando, creemos que merece la pena que lo sepáis para así poder seguirle.
Bien, pues Dave Cheney es uno de los personajes más populares en la comunidad Go, especialmente por sus contribuciones. Los artículos de su blog son una de esas contribuciones, pero también podemos verle en muchas conferencias. Además, también es conocido por dos de sus documentos más extensos en lo que a buenas prácticas se refiere:
-
Por un lado, el workshop de High Performance Go, en el que nos da varios recursos para desarrollar aplicaciones de alto rendimiento.
-
Y por otro lado, su Practical Go, en el que nos brinda un extenso conjunto de buenas prácticas para nuestro día a día con Go.
Finalmente, también es el creador de la organización pkg en GitHub, bajo cuyo paraguas residen
paquetes archiconocidos como el pkg/errors
, del que formamos parte entre los maintainers.
Lo dicho, si aún no le seguís, creemos que es especialmente recomendable que lo hagáis por el contenido que genera y comparte.
El Zen de Go
¡Y ahora sí! Es hora de revisar ese conjunto de buenas prácticas que Dave ha bautizado como el Zen de Go y que presentó durante la última edición de la GopherCon Israel. Como veréis a continuación, es probable que la mayoría de estos consejos no sean ni mucho menos grandes descubrimientos, sin embargo, es importante que, a medida que pasa el tiempo, nos vayamos ejercitando para mantenerlos presentes en nuestro día a día.
Each package fulfils a single purpose
Este primer punto viene a decir algo similar a lo que nos dice el primero de los [principios SOLID] (https://es.wikipedia.org/wiki/SOLID) (el [principio de responsabilidad única] (https://es.wikipedia.org/wiki/Principio_de_responsabilidad_%C3%BAnica)). En esta ocasión adaptado a Go y a su sistema de paquetes. Para ello, hace uso del concepto de elevator pitch, para decirnos que esa debería ser la filosofía que usáramos a la hora de nombrar a nuestros paquetes, en este caso, reducido a una sola palabra en lugar del popular discurso de un minuto.
Handle errors explicitly
El segundo punto hace referencia a uno de los temas tabú de Go: la gestión de los errores y el popular if err != nil { return err }
.
Para ello, su intención no es otra que reincidir en que esa es precisamente la oportunidad que nos brinda el lenguaje para
gestionar los errores de forma explícita. Y que el comportamiento específico de los panic
no son casualidad, sino parte
de esa misma filosofía.
Return early rather than nesting deeply
Quién no ha visto un código similar a (o a una variante del mismo):
if precondition A failed {
return errors.New("whatever")
} else {
if precondition B failed {
return errors.New("whatever")
} else {
if precondition C failed {
return errors.New("whatever")
} else {
// success (happy path)
}
}
}
Pues básicamente, lo que nos viene a recordar este punto es que hagamos uso de las tan preciadas [cláusulas de guarda] (https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html), lo que nos va a permitir hacer un código mucho más fácil de leer. Por ejemplo, obteniendo un código similar a este:
if precondition A failed {
return errors.New("whatever")
}
if precondition B failed {
return errors.New("whatever")
if precondition C failed {
return errors.New("whatever")
}
// success (happy path)
dónde claramente podemos identificar todas las precondiciones que se tienen que cumplir para que el método funcione como se espera y cuál es el código que se corresponde con el happy path del mismo.
Es decir, a diferencia de otros lenguajes, en Go se premia la legibilidad en forma de múltiples puntos de retorno.
Leave concurrency to the caller
El cuarto punto hace referencia al uso de la concurrencia y sobre cómo deberíamos definir la API de nuestros paquetes en base a la misma. En ese sentido, lo que se nos recomienda es que siempre le demos la opción al consumidor de escoger si quiere hacer uso (o no) de la concurrencia. Y, por lo tanto, que si nuestro paquete usa concurrencia internamente, esta sea transparente para el consumidor. Dicho de otro modo, nuestros consumidores solo deberían de preocuparse por la asincronía cuándo esta se haya explicitado, y siempre intentando darle ambas opciones.
Before you launch a goroutine, know when it will stop
Un problema habitual en las aplicaciones Go es tener leaks de gorrutinas, es decir, tener gorrutinas consumiendo recursos (CPU, memoria, etc) cuyo control hemos perdido, de forma que somos incapaces de recuperar esos recursos. Por esa razón, este quinto punto nos recomienda que siempre que vayamos a lanzar una nueva gorrutina seamos capaces de predecir hasta cuándo va a durar su ejecución y qué puede impedir que esta finalice de forma controlada.
Adicionalmente, podemos hacer uso de alguna de las siguientes dos herramientas para intentar identificar aquellos casos en los que hayamos incurrido en ese error:
Avoid package level state
El sexto punto vendría a ser algo así como el habitual “no deberías depender de un estado global”. En este caso adaptado a Go y a su visibilidad a nivel de paquete. Es decir, en lugar de usar variables globales en nuestros paquetes, lo más recomendable es permitir a nuestros consumidores que sean capaces de inyectarnos todas las dependencias necesarias y que sean ellos mismos los que gestionan el estado.
Simplicity matters
El séptimo punto es “marca de la casa” Cheney, recordándonos que una solución sofisticada no tiene porqué dejar de ser simple, es decir, fácil de leer y por consiguiente fácil de mantener. Quizás se trata de un punto un tanto ambiguo, pero siempre merece la pena recordar que, aunque el lenguaje nos de herramientas para hacer soluciones sofisticadas (gorrutinas, canales, etc) no debemos olvidar que ese código se va a tener que mantener, que ese mantenimiento tendrá un coste, y que dicho coste es casi tan importante (o más) como la implementación en sí.
Write tests to lock in the behaviour of your package’s API
El octavo punto hace referencia a los tests y, de hecho, busca ir un poco más allá de las recomendaciones habituales en esta materia para centrarse en lo que de verdad importa y en lo que muchas veces olvidamos.
En ese sentido, poco importa si guías tu desarrollo con los tests (TDD) o no, o si tu coverage es del 100% o no, lo que de verdad importa es que tus tests garanticen el contrato de tus paquetes. Es decir, que cualquier cambio de comportamiento que sufra tu paquete rompa los tests. Ese es el único método de garantizar que los nuevos desarrollos no van a romper lo que ya funcionaba hasta ese momento, y, por lo tanto, el único modo de que los tests nos aporten valor.
Finalmente, los tres últimos puntos están estrechamente relaciondos con el séptimo, es decir, con esa especial preocupación de Dave (y que todos deberíamos compartir) sobre la mantenibilidad de nuestros desarrollos.
If you think it’s slow, first prove it with a benchmark
El primero lo podríamos asimilar con el popular principio YAGNI, por las siglas en inglés de “No vas a necesitarlo”. En este caso focalizado al rendimiento de nuestras aplicaciones. Pues es cierto que, muchas veces intentamos optimizar nuestro código pagando con la mantenibilidad del mismo, sin que esa mejora de rendimiento sea realmente necesaria. O lo que es incluso peor, dejando una deuda mayor (en términos de mantenibilidad) que lo que nos ahorraremos en términos computacionales (costes de infraestructura) con esa optimización.
Moderation is a virtue
El segundo es una simple llamada a la moderación a la hora de usar los recursos que nos proporciona el lenguaje (gorrutinas, canales, mutex, etc), pues conviene que la sobreingeniería de nuestros proyectos se mantenga tan mínima como sea posible.
Maintainability counts
Finalmente, el tercer y último punto no es más que otro recordatorio en pro de la mantenibilidad de nuestros desarrollos. Para ello nos recuerda que la mantenibilidad está formada por aspectos como la claridad, la legibilidad o la simplicidad. Y que en nuestro día a día deberíamos hacernos preguntas como: ¿que puedo hacer para que quién me sustituya el día de mañana lo tenga más sencillo?
Y hasta aquí esta nueva ración de consejos y buenas prácticas recomendadas por Dave Cheney.
¿Qué os han parecido? ¿Ya las conocíais? ¿Ya las estábais aplicando?
Como siempre, estaremos muy agradecidos de que nos dejéis vuestras experiencias personales, dudas o sugerencias en los comentarios o en nuestra cuenta de Twitter.