¿Qué pasos han tomado los desarrolladores de software en las startups con un “Monorail” (una aplicación grande y monolítica de Ruby on Rails) para dividirlo con éxito en los servicios?

Esto es en realidad dos preguntas. El primero es “¿Cómo puedo romper una aplicación monolítica?” y el segundo es “¿Qué pasa si esa aplicación está en Rails?” El primero es más difícil, así que comenzaré allí. Pero para hacerlo hablemos realmente sobre los problemas reales con una aplicación monolítica.

Parte 1: ¿Por qué un monolito es duro?

Cantidad de contribuyentes
Su aplicación probablemente tuvo mucho sentido cuando menos de una docena de desarrolladores contribuyeron. A medida que su plantilla aumenta, también lo hace el deseo de ingenieros individuales de trabajar fuera de los supuestos compartidos globalmente de una única base de código.

Líneas de código
Cada archivo adicional reduce el porcentaje del proyecto que un ingeniero individual entiende. No nos gusta trabajar en cosas que no entendemos, por lo que, a menos que hayamos construido todo, un proyecto grande simplemente se siente mal.

Crecimiento de datos
Si su aplicación asume que cada pieza de datos se puede unir relacionalmente con cualquier otra pieza de datos, entonces está limitado a almacenar solo la cantidad de datos que quepa en un solo disco duro. Al momento de escribir, la unidad más grande y más rápida que puede obtener es una unidad de 6TB de Fusion-IO. No es barato Una vez que supera que tiene que dividir sus datos en partes y puede ser más fácil dividir su aplicación que enseñarle a conectarse a múltiples almacenes de datos.

Velocidad
Cualquier aplicación grande tendrá algún código útil que se use con suficiente frecuencia como para que se convierta en parte del marco. Esto puede ser que cada solicitud web busque la ubicación física del usuario a través de geo-ip o descifre las cookies encriptadas, etc. Cuando tenga una función que debe ser súper rápida, deseará que fuera por sí sola los supuestos de la aplicación no se aplicaban a ella.

Actualización de dependencias
En una aplicación enorme y bien envejecida, es un dolor enorme actualizar las dependencias (como pasar a una versión Rails más moderna). Los sistemas más pequeños se pueden actualizar más fácilmente ya que hay menos líneas de código para modificar y otros ingenieros realizarán menos cambios al mismo tiempo. Sin embargo, esto es una mentira porque un monolito bien diseñado tiene dependencias que se abstraen de manera tal que la dificultad de actualización es la misma.

El problema subyacente
Detrás de las 4 cosas anteriores se encuentra el verdadero problema: la deuda tecnológica se escala mal. Puede salirse con la suya cuando su aplicación tenga el 99% del marco Rails y el 1% su código. Una vez que es 15-25% su código, entonces usted es dueño de los errores y el mal diseño. Y, a menos que no tenga ingenieros que contribuyan o usuarios que utilicen la aplicación, hay un diseño inerte detrás del mal diseño actual.

Parte 2: Cómo romper tu monolito

Extrae lo más importante
En AirB & B, Yammer y Square, las primeras partes extraídas del monolito fueron las partes de alto rendimiento o críticas. Cualquier comportamiento que su aplicación tenga que hacer de manera rápida, correcta y constante que esté directamente relacionado con su ventaja competitiva como empresa es lo primero que extrae. Esto le permite reconstruir esta función correctamente y alivia el estrés en el monolito. Una vez que tenga, digamos, que los pagos con tarjeta de crédito no se ejecutan directamente a través de su monolito, puede respirar un poco más fácilmente.

Sin embargo, es probable que después de esta primera extracción haya más código en su monolito porque ahora está coordinando los datos entre las dos aplicaciones. Todavía hay características en el monolito que dependen de los datos en el servicio extraído (o viceversa), por lo que debe puentearlos de alguna manera. Esto no ha resuelto tu problema.

Extrae más cosas
Especialmente si su problema es el crecimiento de los datos, es probable que pueda comprarse alguna pista al encontrar datos que se vean o huelan como un registro de actividad y lo trasladen a un servicio de registro o análisis adecuado. Sin embargo, esto todavía no resuelve su problema.

Construir nuevas funciones en nuevos servicios
Has alcanzado un punto de inflexión cuando tienes el soporte de la plataforma para crear un nuevo servicio para nuevas funciones en lugar de tener que construir las funciones primero en el monolito. Esto retrasará el crecimiento del monolito al darle una salida para un nuevo desarrollo. También significará que tiene un pequeño equipo dedicado a tiempo completo para coordinar servicios y construir interfaces síncronas y por lotes entre ellos. Esto realmente ha empeorado su problema un poco.

Si sigues este camino el tiempo suficiente y con suficientes ingenieros, ya no tienes un monolito. Tienes varios. Existe el antiguo (ahora un poco domesticado) y probablemente al menos uno nuevo que, si publicara la fuente, haría que la gente se sorprendiera por su tamaño y expansión.

Parte 2 de verdad: cómo romper tu monolito

Necesita diseñar sus sistemas.
Cuando tu startup es pequeña y tu mayor amenaza es que cierres antes de que alguien te escuche, entonces solo estás construyendo lo que funcione. Probablemente esté escribiendo un buen código, pero probablemente no esté escribiendo buenos sistemas. Esto está bien al principio, pero una vez que sobrevivas y trates de contratar muchos más ingenieros, tendrás que repensar cómo organizas todo.

Comience con datos
Lo más importante en su empresa son sus datos, no su código. Por lo tanto, cree un sistema que se adapte al tamaño y la forma de sus datos. Si realiza un trabajo financiero, ¿el esquema de su base de datos utiliza conceptos de dominio que un CPA reconocería? ¿Qué almacén de datos secundario necesitará para analizar sus datos (para evitar arruinar el esquema y la escalabilidad de la base de datos donde se creó)? ¿Existe una forma natural de fragmentar a sus clientes? ¿Qué partes de sus datos son derivables de otras? ¿Qué puede ser finalmente consistente y qué debe ser consistente en todo momento?

Un error que he cometido a menudo es tratar de centralizar los datos sobre una sola entidad en un solo lugar. De hecho, siempre y cuando cada entidad (por ejemplo, un usuario) tenga un token único generado, cada servicio y aplicación puede y debe mantener sus propios datos privados sobre la entidad y comunicarse entre sí mediante el token único. Siempre puede generar una síntesis completa de datos extrayendo información de múltiples servicios. No necesita centralizar la mayoría de los datos más de lo que necesita centralizar la mayoría del código.

Piense en el perfil de uso de sus servicios.
Una vez que extrae su primer servicio, es posible que se sorprenda al notar que, ahora que tiene usuarios, algunas partes de su sistema tienen órdenes de magnitud más de uso que otras. Las personas pueden estar favoreciendo / compartiendo cosas 10,000 veces por segundo, pero solo estás viendo suscripciones 5 veces por segundo. Si este es el caso, puede optimizar el código de registro para que sea amigable con sus diseñadores y garantizar una experiencia de desarrollo optimizada para la iteración rápida y las pruebas A / B al bajo costo de unos pocos servidores adicionales. Y tal vez necesite reescribir su servicio de favorecer / compartir en Clojure desde cero solo para buscar cada bit de rendimiento.

Habrá partes de su sistema que requieren velocidad, otras que requieren una disponibilidad ultra alta y otras que son meras características de prueba donde la calidad importante es que puede enviarlas y desecharlas rápidamente. Cada uno de estos requiere un diseño de aplicación diferente y, francamente, probablemente diferentes lenguajes de programación.

Obtenga sus interfaces correctas
Cuando todo su código está en una aplicación, sabe que está usando otro código correctamente porque obtendrá un ArgumentError si invoca incorrectamente un método o un NoMethodError si intenta llamar a un método con un nombre tipográfico. Esto no funciona a través de los límites del servicio.

Tiene dos opciones para la comunicación entre servicios:

  1. Escriba documentación para que los humanos la lean y cree una API CURL-able en cada servicio para que puedan verificar que hicieron las cosas correctamente. Ore por el servicio y los clientes nunca pierdan una actualización de API.
  2. Utilice algún contrato de API que pueda analizarse por máquina entre el cliente y el servidor. Sus opciones aquí son Thrift, esquema JSON, protobuffers de Google, msgpack, capnproto, Blink y probablemente algunos otros. Esto le permitirá enviar su definición de API a cualquier cliente y les permitirá validar el esquema de cualquier mensaje entre servicios antes de enviarlo.

Elimina todo lo que puedas
Si has llegado hasta aquí, entonces probablemente tengas más de 20 servicios y hay una disminución en los ingresos por agregar cualquier servicio nuevo: el sistema no se está volviendo más simple. Entonces, el siguiente paso para dividir su monolito en servicios es negrita: elimine las funciones y el código antiguos. En algún momento, cada compañía necesita revisar cuidadosamente todas las características que han escrito para ver si pueden eliminarse o reemplazarse por algo más simple. Por ejemplo, mi equipo recientemente eliminó 16 archivos grandes y complejos que operaban un trabajo de procesamiento por lotes inestable porque el servicio al que estábamos enviando archivos por lotes había construido una API mejor en los últimos dos años.

Realmente, cualquier cosa para reducir la cantidad de código y / o datos que pasan por su sistema (en cualquier servicio) mitigará el dolor que sintió cuando comenzó a romper este monolito.

Parte 3: hacer esto con una aplicación Rails

Primero las buenas noticias: las aplicaciones Ruby on Rails tienen una estructura organizativa estandarizada y años de mejores prácticas de la comunidad que lo ayudan a mantener las cosas en su lugar mientras crece. Esto significa que un monorraíl (monolito de Rails) puede crecer muchísimo más que una aplicación en la mayoría de los idiomas y seguir funcionando. Siempre que tenga buenas pruebas, su aplicación Rails / ActiveRecord de 200 controladores será mucho más agradable para trabajar que una aplicación Java / Hibernate de 200 controladores.

Ahora, la mala noticia: Ruby tiene herramientas muy pobres para las dependencias en proceso. Puede solicitar cualquier cosa en la ruta de carga en cualquier momento y las dependencias circulares son totalmente normales siempre que esté dispuesto a realizar algún tipo de inicialización retrasada. Esto significa que las dependencias de nivel de clase de su monorraíl no serían, si se muestran en un gráfico, una estructura de árbol, serían un gráfico cíclico no dirigido. Este patrón también se llama patrón de software de “gran bola de lodo”.

Entonces, a menos que tenga SÚPER suerte y sus desarrolladores lean el libro de Sandi Metz (busque ‘POODR’) no habrán agregado declaraciones explícitas de ‘requerir’ en la parte superior de los archivos de código fuente y en su lugar simplemente asumirán que todo se carga en la memoria todo el tiempo.

Por lo tanto, su mayor desafío al diseccionar un monorraíl es enderezar las dependencias para que sepa qué se refiere a qué. Una vez que sepa que la característica X puede vivir en una base de datos (o servicio) separada y nada intentará unir sus consultas SQL a través de las tablas de la característica X, entonces la extracción es relativamente sencilla. Pero llegar allí es difícil porque actualmente no conozco ninguna herramienta para analizar el tipo de consultas que realiza en producción y desarrollar un gráfico de dependencia de modelos. Construye esto y lo usaré totalmente.

“He trabajado con muchos proveedores, y algunos de ellos han sido excelentes, pero pondría al Servicio de Desarrollo de Aplicaciones en los primeros puestos de ese grupo. Fueron muy profesionales, muy organizados”.

More Interesting

Si su lista de contactos contiene nombres de contacto en inglés y tailandés, ¿qué algoritmo utiliza normalmente para ordenar la lista de contactos?

Como desarrollador de software, ¿alguna vez has sido culpable de 'jugar' las pruebas de software?

¿Cómo obtiene un desarrollador externo los derechos sobre el código fuente de un software abandonado, si el desarrollador original está muerto?

¿Es posible que un estudiante de CS en Europa encuentre trabajo en una de las principales empresas de EE. UU.?

¿Cuáles son los mejores sitios web que debe visitar un probador de software?

Como desarrollador de software, ¿es un problema si veo la programación como un trabajo más?

Como hay muchos tipos de desarrolladores de software, ¿podría contarme sobre su área de especialización?

¿Qué es el software y cómo se crea?

¿Cómo puede un joven de 22 años sin conocimiento de la industria asumir un rol como desarrollador de software?

¿Qué desarrolladores necesito para abrir una empresa de realidad virtual?

¿Cuál es la diferencia entre un desarrollador web y un ingeniero de software? Si conozco JavaScript, Python y SQL, ¿me convierte también en ingeniero de software?

Ingenieros de software: ¿Cómo deshacerse de la visión del túnel cuando se programa?

¿Por qué los programadores se enojan cuando haces una sugerencia sobre su producto / software?

¿Cómo empiezo a contribuir en proyectos de código abierto?

¿Cuáles son las preocupaciones de los desarrolladores de software sobre la privacidad?