Init.js: una guía sobre el por qué y el cómo de JavaScript de pila completa

Publicado: 2022-03-11

La historia

Entonces, tú y tu cofundador tienen esta gran idea para un negocio, ¿verdad?

Ha estado agregando características en su mente.

Con frecuencia, pides a los clientes potenciales su opinión y a todos les encanta.

Ok, entonces la gente lo quiere. Incluso hay algo de dinero por hacer. Y la única razón por la que no pueden tenerlo es porque aún no lo ha implementado.

Entonces, finalmente, te sientas un día y dices: "¡Hagámoslo!" Pronto, está tratando de descubrir cómo implementar la lógica de negocios de su aplicación, la característica principal que impulsará el producto: tiene una idea de cómo hacerlo y sabe que puede hacerlo.

"¡Hecho! ¡Funciona!" tu dices. ¡Tu prueba de concepto es un éxito! Todo lo que queda es empaquetarlo en una aplicación web.

“Ok, vamos a crear el sitio”, dices.

Y luego, te das cuenta de la verdad: necesitas elegir un lenguaje de programación; debe elegir una plataforma (moderna); debe elegir algunos marcos (modernos); necesita configurar (y comprar) almacenamiento, bases de datos y proveedores de alojamiento; necesita una interfaz de administración; necesitas un sistema de permisos; necesita un administrador de contenido.

Quieres ser esbelto, quieres ser ágil. Desea utilizar tecnologías que lo ayudarán a tener éxito a corto y largo plazo. Y no siempre son fáciles de elegir.

Tiene decenas y decenas de decisiones arquitectónicas que tomar. Y quiere hacer las correctas: quiere usar tecnologías que permitan un desarrollo rápido, iteración constante, eficiencia máxima, velocidad, robustez y más. Quieres ser esbelto, quieres ser ágil. Desea utilizar tecnologías que lo ayudarán a tener éxito a corto y largo plazo. Y no siempre son fáciles de elegir.

“Estoy abrumado”, dices, mientras te sientes abrumado. Tu energía no es la misma de antes. Intenta juntar las piezas, pero es demasiado trabajo.

Su prueba de concepto se marchita y muere lentamente.

La propuesta

Después de abandonar toneladas de ideas de esta manera, decidí diseñar una solución. Lo llamo el proyecto 'Init' (o, init.js).

El núcleo de la idea es tener un solo proyecto para iniciarlos todos, permitir que el desarrollador o el fundador técnico tomen todas estas decisiones esenciales a la vez y reciban una plantilla de inicio adecuada basada en esas decisiones. Sé lo que van a decir los detractores: "Una solución no puede aplicarse a todos los problemas" (los que odian van a odiar). Y pueden tener razón. Pero podemos hacer todo lo posible para crear una solución aproximada, y creo que Init se acerca bastante.

Para lograr mejor este objetivo, debemos tener en cuenta algunas ideas clave. Al desarrollar Init, consideré:

  • Componentes

    La creación de componentes es una característica clave de cualquier sistema, ya que le permite reutilizar componentes de software en diferentes proyectos, que es el objetivo principal de Init. Pero la creación de componentes también viene con un subproducto, la "reemplazabilidad", que será nuestro mejor aliado para atacar varios problemas diferentes con "casi" la misma solución.

  • Facilidad de desarrollo

    Algún problema, en algún lugar tiene una solución mejor escrita en Brainf * ck. Pero implementar esa solución (en Brainfuck) será casi imposible de escribir, y mucho menos de leer. Le costará tiempo y una enorme cantidad de esfuerzo. En general, debe usar lenguajes y plataformas que hagan que el desarrollo sea más fácil, no más difícil para usted (o para cualquier persona que pueda trabajar en ello más adelante).

  • Comunidad

    Independientemente de la plataforma que elija, asegúrese de que tenga una gran comunidad y que pueda ayudarlo con los problemas más comunes y menos comunes. Recuerde: jQuery puede no ser la biblioteca más rápida, limpia o elegante, pero es ganadora solo por su comunidad.

Teniendo estos objetivos en mente, a continuación les mostraré cómo tomé mis propias decisiones al crear Init.

En esencia, Init aprovecha el paradigma de 'JavaScript de pila completa' (algunas personas se refieren a él, o a un subconjunto de él, como MEAN Stack). Al trabajar con una pila de este tipo, Init puede usar un solo idioma mientras crea un entorno increíblemente flexible y con todas las funciones para desarrollar aplicaciones web. En resumen, Init le permite usar JavaScript no solo para el desarrollo de clientes y servidores, sino también para compilar, probar, crear plantillas y más.

Pero reduzcamos la velocidad por un momento y preguntémonos: ¿es realmente una buena idea usar JavaScript?

Por qué elegí JavaScript

He sido desarrollador web desde 1998. En ese entonces usábamos Perl para la mayor parte de nuestro desarrollo del lado del servidor, pero incluso desde entonces hemos tenido JavaScript en el lado del cliente. Las tecnologías de servidores web han cambiado enormemente desde entonces: pasamos ola tras ola de lenguajes y tecnologías como PHP, AP, JSP, .NET, Ruby, Python, solo por nombrar algunos. Los desarrolladores comenzaron a darse cuenta de que usar dos idiomas diferentes para los entornos de cliente y servidor complicaba las cosas. Los intentos iniciales de unificación bajo un solo idioma intentaron crear componentes de cliente en el servidor y compilarlos en JavaScript. Esto no funcionó como se esperaba y la mayoría de esos proyectos fallaron (por ejemplo: ASP MVC reemplazó los formularios web de ASP.NET y GWT podría ser reemplazado en un futuro cercano por Polymer). Pero fue una gran idea, en esencia: un solo lenguaje en el cliente y el servidor, permitiéndonos reutilizar componentes y recursos (esta es la palabra clave: recursos ).

La respuesta fue simple: poner JavaScript en el servidor.

JavaScript realmente nació con JavaScript Server Side en Netscape Enterprise Server, pero el lenguaje simplemente no estaba listo en ese momento. Después de años de prueba y error, finalmente surgió Node.js, que no solo puso JavaScript en el servidor, sino que también promovió la idea de la programación sin bloqueo, cambiando la forma en que escribimos un "fread" (E/S) para siempre (lea aquí para más).

En una frase: la programación sin bloqueo tiene como objetivo dejar de lado las tareas que consumen mucho tiempo, generalmente especificando qué se debe hacer cuando se completan estas tareas y permitiendo que el procesador maneje otras solicitudes mientras tanto.

Pero esas ideas no eran nuevas, entonces, ¿por qué se volvieron tan populares con Node.js? La programación simple y sin bloqueo se puede lograr de varias maneras. Quizás lo más fácil es usar devoluciones de llamada y un bucle de eventos. En la mayoría de los idiomas, esa no es una tarea fácil: mientras que las 'devoluciones de llamada' son una característica común en algunos otros idiomas, un bucle de eventos no lo es, y a menudo te encuentras lidiando con bibliotecas externas (por ejemplo: Python, con Tornado). Pero en JavaScript, las devoluciones de llamadas están integradas en el lenguaje, al igual que el bucle de eventos, y casi todos los programadores que han incursionado en JavaScript están familiarizados con ellos (o al menos los han usado, incluso si no entienden bien qué significa el evento). bucle es). De repente, cada startup en la Tierra podría reutilizar desarrolladores (es decir, recursos) tanto en el lado del cliente como en el del servidor, resolviendo el problema del puesto de trabajo "Python Guru Needed".

De repente, cada startup en la Tierra podría reutilizar desarrolladores (es decir, recursos) tanto en el lado del cliente como en el del servidor, resolviendo el problema del puesto de trabajo "Python Guru Needed".

Así que ahora tenemos una plataforma increíblemente rápida (gracias a la programación sin bloqueo) con un lenguaje de programación que es increíblemente fácil de usar (gracias a JavaScript). ¿Pero es suficiente? ¿Durará? Estoy seguro de que JavaScript tendrá un lugar importante en el futuro. Déjame decirte por qué:

  • Programación funcional

    JavaScript fue el primer lenguaje de programación en llevar el paradigma funcional a las masas (por supuesto, Lisp fue el primero, pero la mayoría de los programadores nunca han creado una aplicación lista para producción usando Lisp). Lisp y Self, las principales influencias de Javascript, están llenas de ideas innovadoras. Esas ideas podrían liberar nuestras mentes para explorar nuevas técnicas, patrones y paradigmas. Y todos se trasladan a JavaScript. Eche un vistazo a las mónadas, los números de la iglesia o incluso (para un ejemplo más práctico) las funciones de colecciones de Underscore.js, que pueden ahorrarle líneas y líneas de código.

  • Objetos dinámicos y herencia de prototipos

    La programación orientada a objetos sin clases (y sin jerarquías interminables de clases) permite un desarrollo rápido (crear objetos, agregar métodos y usarlos) pero, lo que es más importante, reduce el tiempo de refactorización durante las tareas de mantenimiento al permitir que el programador modifique las instancias de los objetos. de clases Esta velocidad y flexibilidad allanan el camino para un rápido desarrollo.

  • JavaScript es Internet

    JavaScript fue diseñado para Internet, ha estado aquí desde el principio y no va a desaparecer. Todos los intentos de destruirlo han fallado: vea, por ejemplo, la caída de Java Applets, el reemplazo de VBScript por TypeScript de Microsoft (que compila JavaScript) y la desaparición de Flash a manos del mercado móvil y HTML5. Es imposible reemplazar Javascript sin romper millones de páginas web, por lo que nuestro objetivo en el futuro debería ser mejorarlo. Y no hay nadie mejor para el trabajo que el Comité Técnico 39 de ECMA.

    Ok, las alternativas a JavaScript nacen todos los días, como CoffeeScript, TypeScript y los millones de lenguajes que compilan en JavaScript. Estas alternativas pueden ser útiles para las etapas de desarrollo (a través de mapas de origen), pero no podrán suplantar a JavaScript a largo plazo por dos razones: sus comunidades nunca serán más grandes y sus mejores características serán adoptadas por el script ECMA (es decir, JavaScript ). JavaScript no es un lenguaje ensamblador: es un lenguaje de programación de alto nivel con un código fuente que puede comprender, por lo que debe comprenderlo.

Ahora gracias al proyecto Esprima, puedes crear tus propias herramientas para jugar con el código fuente, modificándolo, cambiando su estilo, añadiendo comentarios, instrumentando, y todo tipo de cosas que puedas imaginar jugando con el Abstract Syntax Tree de tu programa. como si estuvieras trabajando con un árbol DOM.

JavaScript de extremo a extremo: Node.js y MongoDB

Entonces, esas son las razones para usar JavaScript. Ahora, usaré JavaScript como motivo para usar Node.js y MongoDB.

  • Nodo.js

    Node.js es una plataforma para crear aplicaciones de red rápidas y escalables; eso es más o menos lo que dice el sitio de Node.js. Pero Node.js es más que eso: es el entorno de tiempo de ejecución preferido para cualquier aplicación de JavaScript con acceso de E/S. Incluso si no planea escribir su aplicación de servidor principal con Node.js, puede usar herramientas creadas sobre Node.js para mejorar su proceso de desarrollo. Por ejemplo: Mocha.js para pruebas unitarias, Grunt.js para tareas de compilación automatizadas o incluso Brackets para la edición de código de texto completo.

    Entonces, si va a escribir aplicaciones JavaScript para el servidor o el cliente, debería echar un vistazo a algunos ejemplos de Node.js, porque lo necesitará y lo usará a diario. Hay algunas alternativas interesantes, pero ninguna de ellas está ni siquiera en el 10% de la comunidad de Node.js.

  • MongoDB

    MongoDB es una base de datos basada en documentos NoSQL que utiliza JavaScript como lenguaje de consulta, lo que me permite completar la plataforma de JavaScript de un extremo a otro. Pero esa ni siquiera es la razón principal para elegir esta base de datos.

    MongoDB es una base de datos sin esquema que le permite conservar sus objetos de manera flexible y, por lo tanto, adaptarse más rápido a los cambios en los requisitos. Además, es altamente escalable y está basado en reducción de mapas, lo que lo hace adecuado para aplicaciones de big data. MongoDB es tan flexible que se puede usar como una base de datos de documentos sin esquema, un almacén de datos relacional (aunque carece de transacciones) o incluso como un almacén de clave-valor para almacenar respuestas en caché.

Componentización del servidor con Express.js

La creación de componentes del lado del servidor nunca es fácil. Pero con Express.js (y Connect.js) surgió la idea del 'middleware'. En mi opinión, el middleware es la mejor forma de definir componentes en el servidor. Si desea compararlo con un patrón conocido, se parece bastante a las tuberías y los filtros.

La idea básica es que su componente es parte de una canalización. La canalización procesa una solicitud (entrada) y genera una respuesta (salida), pero su componente no es responsable de la respuesta completa. En cambio, solo modifica lo que necesita y luego delega a la siguiente parte de la canalización. Cuando la última parte de la canalización termina de procesarse, la respuesta se envía de vuelta al cliente.

Nos referimos a estas 'piezas de la canalización' como 'middleware'. Claramente, podemos crear dos tipos de middleware:

  • Intermediarios : Aquellos que procesan la solicitud y la respuesta, pero no son totalmente responsables de la respuesta en sí, por lo que delegan al siguiente middleware.

  • Finales : Los que tienen plena responsabilidad sobre la respuesta final. Procesan y modifican la solicitud y la respuesta, pero no necesitan delegar al siguiente middleware. En la práctica, se recomienda que delegue a un próximo middleware de todos modos para permitir la flexibilidad arquitectónica (es decir, agregar más middleware más tarde), incluso si ese middleware no existe (en cuyo caso, la respuesta irá directamente al cliente).

Como ejemplo concreto, considere un componente de 'administrador de usuarios' en el servidor. En términos de middleware, tendríamos tanto finales como intermedios. Para nuestros exámenes finales, tendríamos características tales como crear un usuario y enumerar usuarios. Pero antes de que podamos realizar esas acciones, necesitamos nuestros intermediarios para la autenticación (ya que no queremos que entren solicitudes no autenticadas y creen usuarios). Una vez que hayamos creado estos intermediarios de autenticación, podemos conectarlos en cualquier lugar que queramos para convertir una característica previamente no autenticada en una característica autenticada.

Aplicaciones de una sola página

El proyecto Init se centra en la creación de aplicaciones de una sola página (SPA). La mayoría de los desarrolladores web han tenido la tentación más de una vez de probar suerte en los SPA. He creado varias (principalmente propietarias) y puedo decir con confianza que son simplemente el futuro de las aplicaciones web. ¿Alguna vez ha comparado un SPA con una aplicación web normal en una conexión móvil? La diferencia en la capacidad de respuesta es del orden de decenas de segundos.

¿Alguna vez ha comparado un SPA con una aplicación web normal en una conexión móvil? La diferencia en la capacidad de respuesta es del orden de decenas de segundos.

Los SPA son el futuro de la web, entonces, ¿por qué construiría su producto en una forma heredada? Un argumento común que escucho es que la gente está preocupada por el SEO. Pero si maneja las cosas correctamente, esto no debería ser un problema: el mismo Google tiene un tutorial muy bueno sobre cómo hacerlo, y también hay algunos buenos comentarios aquí.

MV del lado del cliente* con Backbone.js, Marionette.js y Twitter Bootstrap

Mucho se ha dicho sobre los marcos MVC* para SPA. Es una elección difícil, pero diría que los tres primeros son Backbone.js, Ember.js y Angular.js.

Los tres están muy bien considerados. ¿Pero cuál es mejor para ti?

Desafortunadamente, debo admitir que tengo una experiencia muy limitada con Angular.js, por lo que lo dejaré fuera de esta discusión (para obtener más información al respecto, consulte el tutorial de Angular.js). Ahora, Ember.js y Backbone.js representan dos formas diferentes de atacar el mismo problema.

Backbone.js es mínimo, simple y le ofrece lo suficiente para crear un SPA simple. Ember.js, por otro lado, es un marco completo y profesional para crear SPA. Tiene más campanas y silbatos, pero también una mayor curva de aprendizaje.

Dependiendo del tamaño de su aplicación, la decisión puede ser tan fácil como observar la proporción de características utilizadas/características disponibles, que le dará una gran pista.

En el caso de Init, quería cubrir la mayoría de los escenarios, por lo que elegí Backbone.js para una fácil creación de SPA, con Backbone.Marionette.View para la creación de componentes. De esta forma, cada componente es una aplicación simple, y la aplicación final puede ser tan compleja como queramos.

El diseño también es un desafío, pero nuevamente podemos contar con marcos para rescatarnos. Para CSS, no hay nada mejor que Twitter Bootstrap, que ofrece un conjunto completo de estilos listos para usar y fáciles de personalizar.

Bootstrap se creó utilizando el lenguaje LESS y es de código abierto, por lo que podemos modificarlo si es necesario. Viene con una tonelada de controles UX que están bien documentados en el sitio de Bootstrap. Además, hay un modelo de personalización que te permite crear el tuyo propio. Definitivamente es el hombre para el trabajo.

Prácticas recomendadas: Grunt.js, Mocha.js, Chai.js, RequireJS y CoverJS

Finalmente, debemos definir algunas de nuestras mejores prácticas y ver cómo Init puede ayudarlo a implementarlas y mantenerlas. Nuestra solución se centra en varias herramientas, que se basan en Node.js.

  • Mocha.js y Chai.js :

    Estas herramientas le permiten mejorar su proceso de desarrollo aplicando TDD o BDD, proporcionando la infraestructura para organizar sus pruebas unitarias y un corredor para ejecutarlas automáticamente.

    Hay miles de marcos de pruebas unitarias para JavaScript. Entonces, ¿por qué usar Mocha.js? La respuesta corta: es flexible y completo.

    La respuesta larga: tiene dos características importantes (interfaces, reporteros) y una ausencia significativa (afirmaciones). Dejame explicar.

    • Interfaces : tal vez esté acostumbrado a los conceptos TDD de conjuntos y pruebas unitarias, o tal vez prefiera las ideas BDD de especificaciones de comportamiento con "describir" y "debería". Mocha.js le permite usar ambos enfoques.

    • Reporteros : ejecutar su prueba generará informes de los resultados, y puede formatear estos resultados usando varios reporteros. Por ejemplo, si necesita alimentar un servidor de integración continua, puede encontrar un reportero para hacer precisamente eso.

    • Falta de una biblioteca de aserciones : lejos de ser un problema, Mocha.js fue diseñado para permitirle usar la biblioteca de aserciones de su elección, brindándole aún más flexibilidad. Hay muchas opciones, pero aquí es donde entra en juego Chai.js.

    Chai.js es una biblioteca de aserciones flexible que le permite usar cualquiera de los tres principales estilos de aserciones:

    • Afirmar : estilo clásico de aserción de la vieja escuela TDD. P.ej:

       assert.equal(variable, ”value”);
    • Expect : estilo de aserción encadenable, más comúnmente utilizado en BDD. P.ej:

       expect(variable).to.equal(“value”);
    • Debería : También se usa en BDD, pero prefiero Expect porque Debería suena repetitivo con la especificación de comportamiento 'eso ("debería hacer algo...")'. P.ej:

       variable.should.equal(“value”);

    Chai.js combina a la perfección con Mocha.js. Usando solo estas dos bibliotecas, puede escribir sus pruebas en TDD, BDD o cualquier estilo imaginable.

  • Grunt.js :

    Grunt.js le permite automatizar tareas de compilación, desde copiar y pegar y concatenar archivos, hasta precompilación de plantillas, compilación de lenguaje de estilo (es decir, SASS y LESS), pruebas unitarias (con mocha.js), linting y minificación de código (por ejemplo, con UglifyJS o Closure Compiler). Puede agregar su propia tarea automatizada a Grunt, o buscar en el registro de Grunt, donde hay cientos y cientos de complementos disponibles (nuevamente, vale la pena usar herramientas con grandes comunidades detrás). Grunt también puede monitorear sus archivos y desencadenar acciones cuando se modifican.

  • RequerirJS :

    RequireJS puede sonar como otra forma de cargar módulos con AMD, pero puedo asegurarle que es mucho más que eso. Para entender por qué, primero debemos mencionar la idea del espacio de nombres de los módulos (p. ej., demo.views.hello), que evita contaminar el espacio de nombres global al envolver cada módulo en su propio espacio de nombres. El problema es que estos módulos no son reutilizables: si modifica el espacio de nombres de una 'instancia', está modificando el espacio de nombres de todas las 'instancias'. Por el contrario, RequireJS le permite definir módulos reutilizables desde el principio. (Además, lo ayudará a adoptar la inyección de dependencia para evitar que sus módulos accedan a variables globales).

  • PortadaJS :

    La cobertura de código es una métrica para evaluar sus pruebas. Como su nombre lo indica, le dice cuánto de su código está cubierto por su conjunto de pruebas actual. CoverJS mide la cobertura de código de sus pruebas instrumentando declaraciones (en lugar de líneas de código como JSCoverage) en su código y generando una versión instrumentada de su código. También puede generar informes para alimentar su servidor de integración continua.

Uso de ramas para alternar funciones

Cuando comencé con Init, necesitaba una forma para que los usuarios activaran y desactivaran varias funciones que pudieran querer en su proyecto. Decidí adoptar un enfoque radical del sistema de sucursales de git para implementar esta funcionalidad.

En esencia, cada rama representa una característica o funcionalidad que un usuario puede querer incluir. Si está comenzando un proyecto desde cero, comience en la rama mínima que necesitaría y luego agregue otras tecnologías fusionándolas con las ramas deseadas. Por ejemplo, supongamos que desea comenzar su proyecto con Backbone.js y Marionette.js. Bueno, puede comenzar en la rama Backbone.js y fusionarla con la rama Marionette, continuando hacia adelante para cada parte de la funcionalidad que desee agregar.

init.js y Javascript

Por ahora, esta idea de fusionar para agregar funcionalidad solo se puede usar para plantillas de tecnología (por ejemplo, Backbone, Node, Express). Pero en el futuro, podrá cambiar entre implementaciones de back-end (por ejemplo, de MongoDB a Postgres) y de cliente.

Comience un proyecto con Init e implemente en Heroku hoy

Nunca ha habido una manera más fácil de iniciar un proyecto. Simplemente diríjase al repositorio de GitHub, busque la rama con las últimas confirmaciones (ahora mismo es administrador de usuarios, aunque esto podría cambiar en el futuro) y luego:

  1. Cree el directorio para su proyecto (o use uno existente).
  2. Cree su repositorio con "git init" (o use el repositorio existente).
  3. Agregar un control remoto con init

     git remote add init git://github.com/picanteverde/init.git
  4. Consigue la sucursal que quieras

     git pull init usermanager
  5. Obtener el archivo de proceso de Heroku

     git pull init heroku-webprocess
  6. Con Heroku Toolbelt instalado, cree una aplicación Heroku

     heroku create
  7. Empuje su rama maestra a Heroku

     git push heroku master
  8. ¡Visita tu aplicación, funcionando en Heroku!

Ahora puede comenzar a desarrollar su excelente función con solo unas pocas líneas de código. No solo eso, sino que desarrollará con las tecnologías más recientes y eficientes en una suite de desarrollo tan automatizada como puede ser.

Espero que puedas usar Init para poner en marcha tu próxima gran idea. Recuerde consultar el repositorio de Init para ver si hay nuevas correcciones y funciones: su desarrollo está muy activo y espero escuchar sus comentarios.