Durante su más de una década de vida, el lenguaje de programación Go de Google, también conocido como Golang (con la versión 1.18 a partir de marzo de 2022), ha pasado de ser una curiosidad para los frikis a ser el lenguaje de programación probado en batalla detrás de algunos de los proyectos centrados en la nube más importantes del mundo.
¿Por qué eligieron Go los desarrolladores de proyectos como Docker y Kubernetes? ¿Cuáles son las características que definen a Go, en qué se diferencia de otros lenguajes de programación y para qué tipo de proyectos es más adecuado? En este artículo, exploraremos el conjunto de características de Go, los casos de uso óptimos, las omisiones y limitaciones del lenguaje y hacia dónde puede ir en el futuro.
El lenguaje de programación Go es pequeño y sencillo
Go, o Golang, como se le suele llamar, fue desarrollado por empleados de Google (principalmente el veterano gurú de Unix e ingeniero distinguido de Google Rob Pike), pero no es estrictamente un “proyecto de Google“. Más bien, Go se desarrolla como un proyecto de código abierto dirigido por la comunidad, encabezado por un liderazgo que tiene fuertes opiniones sobre cómo debe usarse este lenguaje y la dirección que debe tomar.
El lenguaje de programación Go está pensado para que sea fácil de aprender, sencillo de trabajar y fácil de leer por otros desarrolladores. No tiene un gran conjunto de características, especialmente cuando se compara con lenguajes como C++. Recuerda a C en su sintaxis, por lo que es relativamente fácil de aprender para los desarrolladores de C de toda la vida. Dicho esto, muchas características de Go, especialmente sus características de concurrencia y programación funcional, recuerdan a lenguajes como Erlang.
Como lenguaje similar a C para construir y mantener aplicaciones empresariales multiplataforma de todo tipo, Go tiene mucho en común con Java. Y como medio para permitir el desarrollo rápido de código que pueda ejecutarse en cualquier lugar, se podría establecer un paralelismo entre Go y Python, aunque las diferencias son mucho mayores que las similitudes.
El lenguaje Go tiene algo para todos
La documentación de Go lo describe como “un lenguaje rápido, codificado estáticamente y compilado, que se comporta como un lenguaje interpretado y codificado de forma dinámica”. Incluso un programa grande de Go se compila en cuestión de segundos. Además, Go evita gran parte de la sobrecarga de los archivos de inclusión y las bibliotecas de estilo C.
Go facilita la vida del desarrollador de varias maneras.
Es cómodo
Go ha sido comparado con lenguajes de scripting como Python en su capacidad para satisfacer muchas necesidades comunes de programación. Parte de esta funcionalidad está incorporada en el propio lenguaje, como las “goroutines” para la concurrencia y el comportamiento similar a los hilos, mientras que las capacidades adicionales están disponibles en los paquetes de la biblioteca estándar de Go, como el paquete http de Go. Al igual que Python, Go proporciona capacidades de gestión automática de la memoria, incluyendo la recolección de basura.
A diferencia de los lenguajes de scripting como Python, el código Go se compila en un binario nativo de rápida ejecución. Y a diferencia de C o C++, Go se compila extremadamente rápido, lo suficiente como para que trabajar con Go sea más parecido a trabajar con un lenguaje de scripting que con un lenguaje compilado. Además, el sistema de compilación de Go es menos complejo que el de otros lenguajes compilados. Se necesitan pocos pasos y poca contabilidad para construir y ejecutar un proyecto Go.
Es rápido
Los binarios de Go se ejecutan más lentamente que sus homólogos de C, pero la diferencia de velocidad es insignificante para la mayoría de las aplicaciones. El rendimiento de Go es tan bueno como el de C para la gran mayoría de los trabajos, y en general mucho más rápido que otros lenguajes conocidos por su velocidad de desarrollo (por ejemplo, JavaScript, Python y Ruby).
Es portátil
Los ejecutables creados con la cadena de herramientas Go pueden ser independientes, sin dependencias externas por defecto. La cadena de herramientas de Go está disponible para una amplia variedad de sistemas operativos y plataformas de hardware, y puede utilizarse para compilar binarios en distintas plataformas.
Es interoperable
Go ofrece todo lo anterior sin sacrificar el acceso al sistema subyacente. Los programas Go pueden hablar con bibliotecas C externas o hacer llamadas al sistema nativo. En Docker, por ejemplo, Go interactúa con las funciones de bajo nivel de Linux, los cgroups y los espacios de nombres, para hacer la magia de los contenedores.
Está ampliamente soportado
La cadena de herramientas Go está disponible gratuitamente como binario de Linux, MacOS o Windows o como contenedor Docker. Go está incluido por defecto en muchas distribuciones populares de Linux, como Red Hat Enterprise Linux y Fedora, lo que facilita la implementación del código fuente de Go en esas plataformas. El soporte para Go también es fuerte en muchos entornos de desarrollo de terceros, desde Microsoft Visual Studio Code hasta Komodo IDE de ActiveState.
Dónde funciona mejor el lenguaje Go
Ningún lenguaje es adecuado para todos los trabajos, pero algunos son más idóneos que otros. Go brilla más para desarrollar los siguientes tipos de aplicaciones.
Desarrollo nativo en la nube
Las características de concurrencia y de red de Go, así como su alto grado de portabilidad, lo hacen muy adecuado para construir aplicaciones nativas de la nube. De hecho, Go se utilizó para crear varias piedras angulares de la computación nativa en la nube, como Docker, Kubernetes e Istio.
Servicios de red distribuidos
Las aplicaciones de red viven y mueren por la concurrencia, y las características de concurrencia nativas de Go (motores y canales, principalmente) son muy adecuadas para este tipo de trabajo. En consecuencia, muchos proyectos de Go son para redes, funciones distribuidas y servicios en la nube: API, servidores web, frameworks mínimos para aplicaciones web, y similares.
Utilidades y herramientas independientes
Los programas Go compilan en binarios con mínimas dependencias externas. Esto los hace ideales para crear utilidades y otras herramientas, porque se lanzan rápidamente y pueden ser fácilmente empaquetados para su redistribución. Un ejemplo es un servidor de acceso llamado Teleport (para SSH, entre otras cosas). Teleport puede desplegarse en servidores de forma rápida y sencilla compilándolo desde el código fuente o descargando un binario precompilado.
Limitaciones del lenguaje Go
El conjunto de características de Go ha suscitado tanto elogios como críticas. Go está diseñado para pecar de ser pequeño y fácil de entender, omitiendo deliberadamente algunas características. El resultado es que algunas características que son comunes en otros lenguajes no están disponibles en Go a propósito.
Una de las quejas más antiguas era la falta de funciones genéricas, que permiten que una función acepte muchos tipos diferentes de variables. Durante muchos años, el equipo de desarrollo de Go se resistió a añadir funciones genéricas al lenguaje, alegando que querían una sintaxis y un conjunto de comportamientos que complementaran el resto de Go. Pero a partir de Go 1.18, publicada a principios de 2022, el lenguaje incluye ahora una sintaxis para genéricos. La lección que hay que extraer es que Go añade características importantes raramente y solo después de haberlo considerado mucho, para preservar una amplia compatibilidad entre versiones.
Otro posible inconveniente de Go es el tamaño de los binarios generados. Los binarios de Go se compilan estáticamente por defecto, lo que significa que todo lo que se necesita en tiempo de ejecución está incluido en la imagen binaria. Este enfoque simplifica el proceso de construcción y despliegue, pero a costa de un simple “¡Hola, mundo!” que pesa alrededor de 1,5 MB en Windows de 64 bits. El equipo de Go ha estado trabajando para reducir el tamaño de esos binarios con cada versión sucesiva. También es posible reducir los binarios de Go mediante la compresión o la eliminación de la información de depuración de Go. Esta última opción puede funcionar mejor para aplicaciones distribuidas autónomas que para servicios en la nube o en red, donde tener información de depuración es útil si un servicio falla en el lugar.
Otra característica de Go, la gestión automática de la memoria, puede ser vista como una desventaja, ya que la recolección de basura requiere una cierta cantidad de sobrecarga de procesamiento. Por diseño, Go no proporciona una gestión manual de la memoria, y la recolección de basura en Go ha sido criticada por no lidiar bien con los tipos de cargas de memoria que aparecen en las aplicaciones empresariales.
Dicho esto, cada nueva versión de Go parece mejorar las características de gestión de la memoria. Por ejemplo, Go 1.8 trajo tiempos de retraso significativamente más cortos para la recolección de basura. Los desarrolladores de Go tienen la posibilidad de utilizar la asignación manual de memoria en una extensión de C, o por medio de una biblioteca de gestión de memoria manual de terceros, pero la mayoría de los desarrolladores de Go prefieren soluciones nativas para esos problemas.
La cultura del software en torno a la construcción de ricas interfaces gráficas de usuario para aplicaciones Go, como las de las aplicaciones de escritorio, todavía está dispersa.
La mayoría de las aplicaciones Go son herramientas de línea de comandos o servicios de red. Dicho esto, varios proyectos están trabajando para ofrecer interfaces gráficas ricas para las aplicaciones Go. Hay bindings para los frameworks GTK y GTK3. Otro proyecto pretende ofrecer interfaces de usuario nativas de la plataforma, aunque éstas se basan en enlaces de C y no están escritas en Go puro. Y los usuarios de Windows pueden probar Go. Pero no ha surgido ningún ganador claro ni una apuesta segura a largo plazo en este espacio, y algunos proyectos, como el intento de Google de crear una biblioteca de interfaz gráfica de usuario multiplataforma, se han quedado en el camino. Además, dado que Go es independiente de la plataforma por su diseño, es poco probable que alguno de ellos pase a formar parte del conjunto de paquetes estándar.
Aunque Go puede hablar con funciones nativas del sistema, no fue diseñado para crear componentes de sistema de bajo nivel, como kernels o controladores de dispositivos, o sistemas embebidos. Después de todo, el tiempo de ejecución de Go y el recolector de basura de las aplicaciones Go dependen del sistema operativo subyacente. (Los desarrolladores interesados en un lenguaje de vanguardia para ese tipo de trabajo podrían buscar en el lenguaje Rust).
El futuro del lenguaje Go
El desarrollo futuro de Go se está orientando más hacia los deseos y necesidades de su base de desarrolladores, y los responsables de Go están cambiando el lenguaje para acomodarlo mejor a este público, en lugar de predicar con el ejemplo. Un ejemplo de ello son los genéricos, que finalmente se han añadido al lenguaje tras muchas deliberaciones sobre la mejor manera de hacerlo.
La Encuesta de Desarrolladores de Go de 2021 reveló que los usuarios de Go estaban en general satisfechos con lo que ofrece el lenguaje, pero también citaron un amplio margen de mejora. Las principales áreas en las que los usuarios de Go querían mejoras eran la gestión de dependencias (un reto constante en Go), el diagnóstico de errores y la fiabilidad, mientras que cuestiones como la memoria, el uso de la CPU, el tamaño de los binarios y los tiempos de compilación ocupaban un lugar mucho menor.
La mayoría de los lenguajes gravitan sobre un conjunto básico de casos de uso. En la década que lleva Go, su nicho se ha convertido en los servicios de red, donde es probable que siga expandiendo su dominio. En general, el principal caso de uso citado para el lenguaje fue la creación de API o servicios RPC (49%), seguido del procesamiento de datos (10%), los servicios web (10%) y las aplicaciones CLI (8%).
Otra señal del creciente atractivo del lenguaje Go es el número de desarrolladores que se decantan por él tras evaluarlo. El 75% de los encuestados que consideraron usar Go para un proyecto eligieron el lenguaje. De los que no eligieron Go, Rust (25%), Python (17%) y Java (12%) fueron las principales alternativas. Cada uno de estos lenguajes ha encontrado, o está encontrando, otros nichos: Rust para la programación de sistemas segura y rápida; Python para la creación de prototipos, la automatización y el código de cola; y Java para las aplicaciones empresariales de larga duración.
Queda por ver hasta qué punto la velocidad y la simplicidad de desarrollo de Go lo llevarán a otros casos de uso, o hasta qué punto Go penetrará en el desarrollo empresarial. Pero el futuro de Go como lenguaje de programación importante ya está asegurado, sin duda en la nube, donde la velocidad y la simplicidad de Go facilitan el desarrollo de una infraestructura escalable que pueda mantenerse a largo plazo.