Header Ads Widget

Ticker

6/recent/ticker-posts

Escribir microservicios en Go Parte II: cuándo no usar Go

 

microservicios-parte-2-cuando-no-usar-go

Al escribir microservicios en Go pt. 1 , destacamos toda una serie de razones para codificar en Go, el lenguaje novedoso y con visión de futuro desarrollado en Google. Sin embargo, cada moneda tiene dos caras; el beneficio de un lenguaje o sistema a menudo puede ser un inconveniente en otros casos de uso, y una serie de fallas ocultas pueden hacer que lo que alguna vez fue una idea asombrosa sea significativamente menos asombroso.

En este artículo, tomamos otra mirada más crítica a Go e identificamos algunos casos de uso en los que Go puede no ser la mejor opción para un desarrollador de microservicios emprendedor .

Un resumen

Como recordatorio, el lenguaje Go se desarrolló en 2007 como un proyecto interno supervisado por Robert Griesemer, Rob Pike y Kem Thompson , entonces empleados de Google. El lenguaje, al que se hace referencia indistintamente como "Golang", se basa libremente en el lenguaje de codificación C , tomando prestada gran parte de su sintaxis del lenguaje.

La dependencia de las bibliotecas estándar y la sintaxis del sistema más antiguo hace que Golang sea un lenguaje supuestamente simple , que ofrece una ventaja muy necesaria y fácil de usar . Estas cualidades llevaron a que el lenguaje se ingresara oficialmente en el repositorio público en 2009 y desde entonces se ha adoptado para una variedad de proyectos.

Go tiene mucho que ofrecer. Debido a que se desarrolló utilizando una sintaxis de lenguaje más antigua, muchos usuarios que vengan de una perspectiva de desarrollo encontrarán un lenguaje de codificación más simple , pero de alguna manera más poderoso , que es fácil de entender e implementar. Esta mentalidad posicionada en el legado se yuxtapone con una base creciente de conjuntos de características experimentales , lo que hace que el lenguaje sea uno de los más extensibles disponibles.

Sobre el papel, Go parece el idioma perfecto. Sin embargo, detrás de la exageración que tan a menudo impregna el campo del desarrollo, hay algunos problemas graves con Golang que rara vez se abordan y que deberían hacer que muchos se detengan antes de la adopción o aplicación a lo largo del ciclo de vida de la API .

Recorrido por la publicación del blog CTA 5-01

Gestión de dependencias

Una de las mejores cosas de Go es que tiene una enorme biblioteca de dependencias a las que se puede llamar en cualquier momento para ampliar la funcionalidad de su código. Sin embargo, uno de los inconvenientes de esta metodología de alojamiento de control de código fuente en Go es la falta de control de versiones .

Al llamar a cualquier dependencia, los desarrolladores deben considerar un conjunto de variables que aclaren su elección de dependencia:

  • ¿Cuándo se actualizó el código por última vez?
  • ¿Existe alguna vulnerabilidad de seguridad ?
  • ¿Ha habido bifurcaciones importantes para la funcionalidad que podrían admitir mejor mi código?

Todo esto está muy bien, pero es funcionalmente inútil en Golang. No puede seleccionar una versión del código fuente de servicios como Github o Bitbucket. Independientemente de la versión que desee, al seleccionar una dependencia solo se selecciona la disponible actualmente (que suele ser la más reciente, excluyendo las versiones beta especiales y de prueba). En otros idiomas, el esfuerzo requerido para promulgar el control de versiones es tan simple como agregar un número a la solicitud de su paquete, es decir, "obtener ModuleTestVar 1.79 Beta". En Go, no hay soporte en el código base.

Este es un gran problema, un problema tan grande que el equipo de Golang lo abordó en una publicación de la comunidad . La solución dada por el equipo es simplemente crear un archivo de referencia interno, lo cual está bien para proyectos pequeños, pero aumenta la complejidad y el tamaño de una suite o aplicación a medida que crece el proyecto. Agregue a esto que hasta la fecha no hay consenso sobre el formato o diseño de archivo Golang , y rápidamente ingresa a un reino desconocido.

Consulte también: Living in the Cloud Stack: comprensión de SaaS, PaaS e IaaS

El sistema Go Type y los genéricos

El mayor problema con Go es uno que contrasta con la alta extensibilidad percibida del lenguaje: el sistema de tipo Golang y el soporte para los genéricos de programación . Si bien existen muchos enfoques para el desarrollo de API, uno de los más comunes es el desarrollo de código genérico para aplicaciones masivas.

Por ejemplo, si un desarrollador desea crear un sistema para manejar pagos, renderizar imágenes y transmitir medios (actuando como un transmisor de medios como Spotify o iHeartRadio ), primero se debe desarrollar un renderizador de servidor y un transmisor. En lugar de desarrollar un renderizador de medios y un streamer separados para cada tipo de medio, estos tipos pueden ser servidos por un solo renderizador de variable en la misma API, lo que resulta en una base de código más liviana y fácil de mantener.

La mejor solución para esto (por el momento) son los genéricos de tipo restringido, es decir, un fragmento de código genérico que se puede aplicar a una variedad de tipos establecidos como una variable. En Haskell , una base de código alternativa, esto se puede resolver usando funciones genéricas:

id :: t -> t  
id a = a

En pocas palabras, este código genera una función genérica con el nombre "id", y luego genera un retorno basado en esa función. Esta función puede ser literalmente de cualquier tipo, debido al hecho de que Haskell conserva el tipo que se pasa a través de ella, junto con cualquier variable agregada a los datos. Luego, podemos usar estos datos para crear una función que devuelva modificaciones en ese flujo de datos:

modifyvar :: Num t => t -> t-> t -> t
modifyvar x y z = x + y + z

Observe que no hubo una configuración de variables ni una restricción detallada de los datos; el compilador interpreta esta función como una restricción simple del tipo "t", donde "t" debe ser un valor "Num".

Go no funciona de esta manera. Debido a que Golang no tiene un sistema de tipos que funcione bien, los desarrolladores tienen que usar el sistema de interfaz, que genera un código que es mucho más largo de lo necesario. Tomemos, por ejemplo, esta comparación entre nuestra restricción de función de identificación genérica.

En Haskell:

id :: t -> t  
id a = a

Y el equivalente de Golang:

type Hashable interface {
Hash() []byte } func printHash(item Hashable) { fmt.Println(item.Hash())
}

Si bien es cierto que este es un tipo genérico válido, el tipo "valor" de la variable "interfaz" es un "tipo superior", lo que significa que todas las variaciones de ese tipo son en realidad un subtipo de la variable "interfaz". Si bien Golang técnicamente no admite subtipos, en la práctica esto es cierto porque las variables modificadas desde "interfaz" dependen directamente de la primera definición de "interfaz", lo que significa que el tipo superior debe ser monolítico e incluir todas las variables posibles. En otras palabras, la "variable" en Golang es en realidad un conjunto estático, sin aprendizaje, mientras que en otros lenguajes, como Haskell, el aprendizaje es intrínseco al lenguaje y al manejo de tipos genéricos.

Además, esto se desglosa desde el tipo superior, lo que significa que cualquier variable derivada no se compilará correctamente, bloqueando su API tan pronto como el código intente reducir una variable no declarada explícitamente o incluida en el tipo superior. En pocas palabras, no hay un tipo de variable dinámica en Golang , lo que hace que el desarrollo de marcos de API estilo "Swiss Army Knife" sea completamente imposible sin hackear el lenguaje mucho más de lo que es posible en la implementación predeterminada de otros lenguajes. La funcionalidad es importante, pero debe equilibrarse con la utilidad y la accesibilidad .

Leer más: Showdown: REST vs SOAP vs Apache Thrift, y por qué es importante

Extensible, pero no realmente

Curiosamente, uno de los grandes puntos de venta de Golang es, a su vez, uno de sus mayores aspectos negativos: la extensibilidad. Si bien es cierto que Go es muy extensible y, a través de una variedad de marcos, se puede adaptar a una gran cantidad de aplicaciones variables, el problema del tipo de antes aparece una y otra vez, lo que hace que esta extensibilidad tenga un precio: la complejidad . Y, dado que la simplicidad es un punto de venta de Golang, agregar complejidad parece contradictorio.

Tomemos, por ejemplo, un problema matemático muy simple: a (b) + 5 * b (c). Para representar eso en Golang, debe definir cada variable como un modificador de la otra. En otras palabras, su código termina luciendo así:

a.Mul (b) .Add(big.NewInt (5) .Mul(b) .Mul(c))

Para hacer esto en Haskell, simplemente cree la siguiente línea:

a*b - 5 * b * c

A medida que se expande la complejidad de una API, es importante recordar que no solo importa la experiencia del usuario, sino también la experiencia del desarrollador , y como desarrollador, ¿preferiría ver el ejemplo A o el ejemplo B? Go es muy poderoso, pero con una expansión tan masiva de funciones que de otro modo serían simples, lo que podría tomar 50 o 60 líneas en Haskell rápidamente termina tomando 200 o 250 líneas para manejar. Permitir la manipulación de datos es de vital importancia, que dificultarlo es simplemente un enfoque deficiente.

* Desde la publicación de este artículo, varios lectores han señalado un defecto percibido en el problema matemático presentado. El problema anterior tal como está escrito es específicamente un problema BigInt, y no un problema int64.

Alternativas

Está bien decir que algo está mal, pero brindar soluciones, eso es importante. Afortunadamente, hay algunas cosas que un desarrollador puede hacer para alejarse de Go y sus problemas nativos.

Modificar Ir

Los lenguajes de desarrollo se benefician del hecho de que, si hay un problema, es probable que haya una solución. Go puede ser modificado por cualquier cantidad de marcos para que funcione de la manera que queremos, y muchos de los mayores problemas inherentes al lenguaje Golang se resuelven con estos marcos:

  • Revel : permite comodines en URL y parámetros coincidentes utilizando "map [string] string", lo que permite tipos y valores de datos heredados más complejos a través de la reflexión y la inyección.
  • Gorilla : proporciona una metodología de enrutamiento más sólida y poderosa que el código base de Go. También permite gran parte de la misma herencia de "id" basada en inyección a través de "map [string] string" que Revel, pero no requiere una dependencia exterior para hacerlo.
  • Gocraft / web : admite tanto comodines de URL como expresiones regulares de URL (regex), como “/: id: [0–9] +”. Utiliza rutas en un árbol en lugar de una lista, y admite la funcionalidad "map [string] string" mencionada anteriormente a través de la implementación nativa de las llamadas "http.ResponseWriter" y "http.Request" y el formato de tipo "web.Request".

Sin embargo, el problema con la modificación de Go es que está modificando funcionalmente un sistema en lugar de utilizar una aplicación base. Esto significa exceso de código , complejidad y dificultad para mantener no solo su propio código, sino también los sistemas derivados utilizados para hacer que el código funcione. Esta complejidad adicional plantea la pregunta: si está usando Go para simplificar, y da como resultado una complejidad adicional, ¿por qué está usando Go?

Otros idiomas

Por supuesto, existe la opción de alejarse de Go. Se han diseñado varios lenguajes junto con Go o como una alternativa a Go, creados o modificados específicamente para resolver problemas inherentes tanto en Go como en otros lenguajes como C o Java, al tiempo que permiten el uso de una amplia gama de arquitecturas y enfoques API .

  • Lua : Una base de código muy poderosa y liviana con muchos de los mismos enfoques hacia problemas comunes que Go. A diferencia de Go, Lua se escribe dinámicamente , pero al igual que Go, admite la gestión y recolección de basura incrementales . Hay, por supuesto, salvedades con Lua; La declaración de variables deja mucho que desear, la iteración sobre declaraciones más antiguas es difícil y Lua utiliza expresiones regulares que varían de las expresiones regulares estándar.
  • Dart : Un idioma interesante en gran parte porque es el hermano pequeño de Golang. Presentado en 2011 por Google, el lenguaje fue diseñado para reemplazar JavaScript. Es liviano, pero hereda problemas en la sintaxis de JavaScript en el manejo de la compilación en navegadores no compatibles y en el exceso de código. Durante los primeros días de Dart, se compiló un programa "Hello World" de nueve líneas en un programa JavaScript de 17.259 líneas utilizando el compilador incorporado .
  • Scala : muy potente y ligero, pero tiene sus propios problemas. Es complicado y requiere conocimientos de programación orientada a objetos para hacer cualquier cosa de manera efectiva. Si Scala es utilizado por alguien que comprende la sutileza de la programación orientada a objetos, puede ser muy poderoso, pero seguro que espera mucho de un usuario para un uso básico.

Conclusión

Desafortunadamente, la conclusión de este artículo es probablemente una que haya escuchado antes: "depende". El desarrollo de API es un tema tan extenso que ninguna pieza lo convencerá de adoptar un lenguaje con el que no está familiarizado o de abandonar un lenguaje que ama desesperadamente.

El problema se reduce a esto: ¿puedes vivir sin versiones? ¿Puede funcionar su código sin tipos genéricos? ¿Está dispuesto a implementar un marco sobre un lenguaje para que funcione al mismo nivel que otros lenguajes listos para usar? Debido a que Go aumenta en complejidad a medida que aumentan los requisitos de API, diseñar una API genérica con múltiples funciones y aplicaciones se vuelve mucho más difícil de lo que debería ser. Combine esto con la adición de la falta de soporte de versiones y el exceso de código, y es posible que el uso de Golang se convierta en una propuesta desalentadora.

Pero, ¿y si puede vivir sin versiones? ¿Qué pasa si no necesita tipos genéricos? ¿Qué pasa si se siente cómodo con marcos adicionales y una solución rápida o dos? Al desarrollar una función simple, única o baja APIs basadas en estrategias Lean , Golang es una opción maravillosa y puede reducir la complejidad del desarrollo de manera espectacular en comparación con otros lenguajes igualmente detallados.

De hecho, al desarrollar un sencillo API , esas debilidades a menudo se convierten en fortalezas: el control de versiones se convierte en un problema menor en una API que utiliza pocas dependencias, las funciones de API genéricas no son importantes para tareas específicas y el exceso de código es un problema mínimo con un código base pequeño. .

Cuota

¿Utiliza Go para su proyecto? ¿Tiene una opinión de cualquier manera? No dude en compartir sus pensamientos o experiencias a continuación, o comuníquese con nosotros en Twitter .

Publicar un comentario

0 Comentarios