Capítulo 4. Poner orden

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

En los dos capítulos anteriores, sólo experimentamos: nos sumergimos en las aguas, por así decirlo. Antes de pasar a funciones más complejas, vamos a hacer algunas tareas domésticas e incorporar algunos buenos hábitos a nuestro trabajo.

En este capítulo, comenzaremos en serio nuestro proyecto Meadowlark Travel. Pero antes de empezar a construir el sitio web propiamente dicho, vamos a asegurarnos de que disponemos de las herramientas necesarias para elaborar un producto de alta calidad.

Consejo

El ejemplo práctico de este libro no es necesariamente el que tienes que seguir. Si estás ansioso por construir tu propio sitio web, podríasseguir el marco del ejemplo en funcionamiento, pero modificándolo en consecuencia, de modo que cuando termines este libro, ¡podrías tener un sitio web terminado !

Estructura de archivos y directorios

La estructuración de las aplicaciones ha dado lugar a muchos debates religiosos, y no hay una única forma correcta de hacerlo. Sin embargo, hay algunas convenciones comunes que conviene conocer.

Es típico intentar restringir el número de archivos en la raíz de tu proyecto. Normalmente, encontrarás archivos de configuración (como package.json), un archivoREADME.md y un montón de directorios. La mayor parte del código fuente va en un directorio a menudo llamado src. En aras de la brevedad, no utilizaremos esa convención en este libro (sorprendentemente, la aplicación de andamiaje Express tampoco lo hace). Para los proyectos del mundo real, probablemente con el tiempo te darás cuenta de que la raíz de tu proyecto se desordena si vas colocando allí el código fuente, y querrás reunir esos archivos en un directorio como src.

También he mencionado que prefiero nombrar mi archivo de aplicación principal (a veces llamado punto de entrada) con el nombre del propio proyecto(meadowlark.js) en lugar de algo genérico como index.js,app.js o server.js.

Depende en gran medida de ti cómo estructurar tu aplicación, y te recomiendo que proporciones una hoja de ruta de tu estructura en el archivo README.md (o un readme vinculado a él).

Como mínimo, te recomiendo que tengas siempre los dos archivos siguientes en la raíz de tu proyecto: package.json y README.md. El resto depende de tu imaginación.

Buenas prácticas

La frase "buenas prácticas" se oye mucho últimamente, y significa que debes "hacer las cosas bien" y no escatimar en gastos (hablaremos de lo que esto significa concretamente dentro de un momento). Sin duda habrás oído el adagio de ingeniería de que tus opciones son "rápido", "barato" y "bueno", y puedes elegir cualquiera de las dos. Lo que siempre me ha molestado de este modelo es que no tiene en cuenta elvalor acumulado de hacer las cosas correctamente. La primera vez que haces algo correctamente, puede que tardes cinco veces más en hacerlo que si lo hicieras rápido y sucio. La segunda vez, sin embargo, tardarás sólo tres veces más. Cuando lo hayas hecho correctamente una docena de veces, lo harás casi tan rápido como de la forma rápida y sucia.

Tuve un entrenador de esgrima que siempre nos recordaba que la práctica no hace al maestro, sino que la práctica hace permanente. Es decir, si haces algo una y otra vez, con el tiempo se convertirá en algo automático, de memoria. Eso es cierto, pero no dice nada sobre la calidad de lo que practicas. Si practicas malos hábitos, los malos hábitos se vuelven rutinarios. Por el contrario, debes seguir la regla de que la práctica perfecta hace al maestro. Con ese espíritu, te animo a que sigas el resto de los ejemplos de este libro como si estuvieras haciendo un sitio web real, como si tu reputación y tu remuneración dependieran de la calidad del resultado. Utiliza este libro no sólo para aprender nuevas habilidades, sino para practicar la creación de buenos hábitos.

Las prácticas en las que nos centraremos son el control de versiones y la garantía de calidad. En este capítulo hablaremos del control de versiones, y en el siguiente hablaremos de la garantía de calidad.

Control de versiones

Espero no tener que convencerte del valor del control de versiones (si tuviera que hacerlo, me llevaría todo un libro). En términos generales, el control de versiones ofrece estas ventajas:

Documentación

Poder volver atrás en la historia de un proyecto para ver las decisiones que se tomaron y el orden en que se desarrollaron los componentes puede ser una documentación valiosa. Tener un historial técnico de tu proyecto puede ser muy útil.

Atribución

Si trabajas en equipo, la atribución puede ser enormemente importante. Siempre que encuentres algo en el código que sea opaco o cuestionable, saber quién hizo ese cambio puede ahorrarte muchas horas. Puede que los comentarios asociados al cambio sean suficientes para responder a tus preguntas, y si no, sabrás con quién hablar.

Experimentación

Un buen sistema de control de versiones permite la experimentación. Puedes salirte por la tangente, probando algo nuevo, sin miedo a afectar a la estabilidad de tu proyecto. Si el experimento tiene éxito, puedes volver a integrarlo en el proyecto, y si no tiene éxito, puedes abandonarlo.

Hace años me pasé a los sistemas distribuidos de control de versiones (SCVD). Reduje mis opciones a Git y Mercurial y me decanté por Git, debido a su ubicuidad y flexibilidad. Ambos son sistemas de control de versiones excelentes y gratuitos, y te recomiendo que utilices uno de ellos. En este libro utilizaremos Git, pero puedes utilizar Mercurial (u otro sistema de control de versiones).

Si no estás familiarizado con Git, te recomiendo el excelenteControl de versiones con Gitde Jon Loeliger (O'Reilly). Además, GitHub tiene una buena lista de recursos de aprendizaje de Git.

Cómo utilizar Git con este libro

Primero, asegúrate de que tienes Git. Escribe git --version. Si no responde con un número de versión, tendrás que instalar Git. Consultala documentación de Git para obtener instrucciones de instalación .

Hay dos formas de seguir los ejemplos de este libro. Una es escribir tú mismo los ejemplos y seguirlos con los comandos de Git. La otra es clonar el repositorio complementario que estoy utilizando para todos los ejemplos y consultar los archivos asociados a cada ejemplo. Algunas personas aprenden mejor tecleando los ejemplos, mientras que otras prefieren ver y ejecutar los cambios sin tener que teclearlo todo.

Si sigues haciéndolo tú mismo

Ya tenemos una estructura muy aproximada para nuestro proyecto: algunas vistas, un diseño, un logotipo, un archivo de aplicación principal y un archivo package.json. Sigamos adelante y creemos un repositorio Git y añadamos todos esos archivos.

Primero, vamos al directorio del proyecto e inicializamos allí un repositorio Git:

git init

Ahora, antes de añadir todos los archivos, crearemos un archivo .gitignore para evitar que añadamos accidentalmente cosas que no queremos añadir. Crea un archivo de texto llamado .gitignore en el directorio de tu proyecto en el que puedas añadir los archivos o directorios que quieras que Git ignore por defecto (uno por línea). También admite comodines. Por ejemplo, si tu editor crea archivos de copia de seguridad con una tilde al final (como meadowlark.js~), podrías poner *~ en el archivo .gitignore. Si estás en un Mac, querrás poner .DS_Store. También querrás poner node_modules (por razones que se discutirán pronto). Así que, por ahora, el archivo podría tener este aspecto:

node_modules
*~
.DS_Store
Nota

Las entradas en el archivo .gitignore también se aplican a los subdirectorios. Así que si pones *~ en el .gitignore en la raíz del proyecto, todos esos archivos de copia de seguridad se ignorarán aunque estén en subdirectorios.

Ahora podemos añadir todos nuestros archivos existentes. Hay muchas formas de hacer esto en Git. Generalmente me inclino por git add -A, que es la más arrolladora de todas las variantes. Si eres nuevo en Git, te recomiendo que añadas los archivos uno a uno (git add meadowlark.js, por ejemplo) si sólo quieres confirmar uno o dos archivos, o que añadas todos tus cambios (incluidos los archivos que hayas borrado) utilizando git add -A. Como queremos añadir todo el trabajo que ya hemos hecho, utilizaremos lo siguiente:

git add -A
Consejo

Los recién llegados a Git suelen confundirse con elcomando git add; añade cambios, no archivos. Así que si has modificado meadowlark.js, y luego escribes git add meadowlark.js,lo que realmente estás haciendo es añadir los cambios que has hecho.

Git tiene un "área de preparación", donde van los cambios cuando ejecutas git add. Así que los cambios que hemos añadido aún no han sido confirmados, pero ya están listos. Para confirmar los cambios, utiliza git commit:

git commit -m "Initial commit."

El -m "Initial commit." te permite escribir un mensaje asociado a este commit. Git ni siquiera te permitirá hacer una confirmación sin un mensaje, y por una buena razón. Esfuérzate siempre por hacer mensajes de confirmación significativos; deben describir de forma breve pero concisa el trabajo que has hecho.

Si sigues utilizando el repositorio oficial

Para obtener el repositorio oficial de este libro, ejecuta git clone:

git clone https://github.com/EthanRBrown/web-development-with-node-and-express-2e

Este repositorio tiene un directorio para cada capítulo que contiene muestras de código. Por ejemplo, el código fuente de este capítulo se encuentra en el directorioch04. Los ejemplos de código de cada capítulo suelen estar numerados para facilitar la consulta. A lo largo del repositorio, he añadido libremente archivosREADME.md que contienen notas adicionales sobre los ejemplos.

Nota

En la primera versión de este libro, adopté un enfoque diferente con el repositorio, con una historia lineal como si estuvieras desarrollando un proyecto cada vez más sofisticado. Aunque este enfoque reflejaba agradablemente la forma en que podría desarrollarse un proyecto en el mundo real, causaba muchos quebraderos de cabeza, tanto a mí como a mis lectores. A medida que los paquetes npm cambiaban, las muestras de código también lo hacían y, a falta de reescribir todo el historial del repositorio, no había una buena forma de actualizar el repositorio o anotar los cambios en el texto. Aunque el enfoque de capítulo por directorio es más artificial, permite sincronizar mejor el texto con el repositorio y facilita la contribución de la comunidad.

A medida que este libro se actualice y mejore, el repositorio también se actualizará, y cuando lo haga, añadiré una etiqueta de versión para que puedas consultar una versión del repositorio que se corresponda con la versión del libro que estás leyendo ahora. La versión actual del repositorio es 2.0.0. Estoy siguiendo a grandes rasgos los principios semánticos de versionado (más adelante en este capítulo); el incremento PATCH (el último número) representa cambios menores que no deberían afectar a tu capacidad para seguir el libro. Es decir, si el repositorio está en la versión 2.0.15, debería seguir correspondiendo con esta versión del libro. Sin embargo, si el incremento MENOR (el segundo número) es diferente (2.1.0), eso indica que el contenido de la repo complementaria puede haberse desviado de lo que estás leyendo, y puede que quieras consultar una etiqueta que empiece por 2.0.

El repositorio complementario utiliza libremente los archivos README.md para añadir explicaciones adicionales a los ejemplos de código.

Nota

Si en algún momento quieres experimentar, ten en cuenta que la etiqueta que has comprobado te coloca en lo que Git llama un estado "HEAD desvinculado". Aunque eres libre de editar cualquier archivo, no es seguro confirmar nada de lo que hagas sin crear primero una rama. Así que si quieres basar una rama experimental en una etiqueta, simplemente crea una nueva rama y compruébala, lo que puedes hacer con un comando: git checkout -b experiment (donde experiment es el nombre de tu rama; puedes usar el que quieras). Luego puedes editar y confirmar con seguridad en esa rama todo lo que quieras.

Paquetes npm

Los paquetes npm de los que depende tu proyecto residen en un directorio llamadonode_modules. (Es una lástima que se llame node_modules y no npm_packages, ya que los módulos Node son un concepto relacionado pero diferente). Siéntete libre de explorar ese directorio para satisfacer tu curiosidad o depurar tu programa, pero nunca debes modificar ningún código de este directorio. Además de ser una mala práctica, todos tus cambios podrían ser fácilmente deshechos por npm.

Si necesitas hacer una modificación en un paquete del que depende tu proyecto, lo correcto sería crear tu propio fork del paquete. Si sigues este camino y crees que tus mejoras serán útiles para otros, enhorabuena: ¡ahora participas en un proyecto de código abierto! Puedes enviar tus cambios y, si cumplen las normas del proyecto, se incluirán en el paquete oficial. Contribuir a los paquetes existentes y crear compilaciones personalizadas está fuera del alcance de este libro, pero existe una vibrante comunidad de desarrolladores que te ayudarán si quieres contribuir a los paquetes existentes.

Dos de los principales objetivos del archivo package.json son describir tu proyecto y enumerar sus dependencias. Mira ahora tu archivopackage.json. Deberías ver algo como esto (los números exactos de versión probablemente serán diferentes, ya que estos paquetes se actualizan a menudo):

{
  "dependencies": {
    "express": "^4.16.4",
    "express-handlebars": "^3.0.0"
  }
}

Ahora mismo, nuestro archivo package.json sólo contiene información sobre las dependencias. El signo de intercalación (^) delante de las versiones del paquete indica que funcionará cualquier versión que empiece por el número de versión especificado hasta el siguiente número de versión mayor. Por ejemplo, este package.jsonindica que cualquier versión de Express que empiece por 4.0.0 funcionará, por lo que tanto la 4.0.1 como la 4.9.9 funcionarían, pero la 3.4.7 no, ni tampoco la 5.0.0. Esta es la especificidad de versión por defecto cuando utilizas npm install, y generalmente es una apuesta bastante segura. La consecuencia de este enfoque es que si quieres pasar a una versión más reciente, tendrás que editar el archivo para especificar la nueva versión. En general, eso es bueno porque evita que los cambios en las dependencias rompan tu proyecto sin que tú lo sepas. Los números de versión en npm son analizados por un componente llamadosemver (por "versionado semántico"). Si quieres más información sobre el versionado en npm, consulta laEspecificación de Versionado Semántico y este artículo de Tamas Piros.

Nota

La Especificación del Versionado Semántico establece que el software que utilice el versionado semántico debe declarar una "API pública". Siempre me ha parecido que esta redacción es confusa; lo que realmente quieren decir es "alguien debe preocuparse por interactuar con tu software". Si consideras esto en el sentido más amplio, realmente podría interpretarse que significa cualquier cosa. Así que no te obsesiones con esa parte de la especificación; los detalles importantes están en el formato.

Dado que el archivo package.json enumera todas las dependencias, el directorionode_modules es en realidad un artefacto derivado. Es decir, si lo borraras, todo lo que tendrías que hacer para que el proyecto volviera a funcionar sería ejecutar npm install, que volverá a crear el directorio y pondrá en él todas las dependencias necesarias. Por esta razón, recomiendo poner node_modules en tu archivo .gitignore y no incluirlo en el control de código fuente. Sin embargo, algunas personas consideran que su repositorio debe contener todo lo necesario para ejecutar el proyecto y prefieren mantener node_modules en el control de código fuente. A mí me parece que es "ruido" en el repositorio, y prefiero omitirlo.

Nota

A partir de la versión 5 de npm, se creará un archivo adicional, package-lock.json. Mientras que package.json puede ser "flojo" en su especificación de las versiones de las dependencias (con los modificadores de versión ^ y ~ ),package-lock.json registra las versiones exactas que se instalaron, lo que puede ser útil si necesitas volver a crear las versiones exactas de las dependencias en tu proyecto. Te recomiendo que verifiques este archivo en el control de código fuente y no lo modifiques a mano. Consultala documentaciónde package-lock. json para obtener más información.

Metadatos del proyecto

La otra finalidad del archivo package.json es almacenar los metadatos del proyecto, como el nombre del proyecto, los autores, la información sobre la licencia, etc. Si utilizas npm init para crear inicialmente tu archivo package.json, rellenará el archivo con los campos necesarios por ti, y podrás actualizarlos en cualquier momento. Si pretendes que tu proyecto esté disponible en npm o GitHub, estos metadatos se vuelven fundamentales. Si quieres más información sobre los campos de package.json, consultala documentación depackage.json. La otra pieza importante de metadatos es el archivo README.md. Este archivo puede ser un lugar práctico para describir la arquitectura general del sitio web, así como cualquier información crítica que pueda necesitar alguien nuevo en el proyecto. Está en un formato wiki basado en texto llamado Markdown. Consulta ladocumentación de Markdown para obtener más información.

Módulos Nodo

Como ya se ha mencionado, los módulos Node y los paquetes npm son conceptos relacionados pero diferentes. Los módulos Node, como su nombre indica, ofrecen un mecanismo de modularización y encapsulación. Los paquetes npmproporcionan un esquema estandarizado para almacenar, versionar y referenciar proyectos (que no están restringidos a módulos). Por ejemplo, importamos el propio Express como módulo en nuestro archivo de aplicación principal:

const express = require('express')

require es una función de Node para importar un módulo. Por defecto, Node busca módulos en el directorio node_modules (no debería sorprender, pues, que haya un directorio express dentro de node_modules). Sin embargo, Node también proporciona un mecanismo para crear tus propios módulos (nunca debes crear tus propios módulos en el directorio node_modules ). Además de los módulos instalados en node_modules a través de un gestor de paquetes, hay más de 30 "módulos básicos" proporcionados por Node, como , , , y . Para ver la lista completa, fs http os pathconsulta esta esclarecedora pregunta de Stack Overflow y consulta ladocumentación oficial de Node.

Veamos cómo podemos modularizar la funcionalidad de las galletas de la suerte que implementamos en el capítulo anterior.

Primero vamos a crear un directorio para almacenar nuestros módulos. Puedes llamarlo como quieras, pero lib (abreviatura de "biblioteca") es una opción común. En esa carpeta, crea un archivo llamado fortune.js(ch04/lib/fortune.js en el repositorio compañero):

const fortuneCookies = [
  "Conquer your fears or they will conquer you.",
  "Rivers need springs.",
  "Do not fear what you don't know.",
  "You will have a pleasant surprise.",
  "Whenever possible, keep it simple.",
]

exports.getFortune = () => {
  const idx = Math.floor(Math.random()*fortuneCookies.length)
  return fortuneCookies[idx]
}

Lo importante aquí es el uso de la variable globalexports. Si quieres que algo sea visible fuera del módulo, tienes que añadirlo aexports. En este ejemplo, la función getFortune estará disponible desde fuera de este módulo, pero nuestra matriz fortuneCookies estará completamente oculta. Esto es algo bueno: la encapsulación permite un código menos propenso a errores y más frágil.

Nota

Hay varias formas de exportar la funcionalidad de un módulo. A lo largo del libro iremos tratando distintos métodos y los resumiremos en el capítulo 22.

Ahora, en meadowlark.js, podemos eliminar la matriz fortuneCookies (aunque no pasaría nada por dejarla; no puede entrar en conflicto de ningún modo con la matriz del mismo nombre definida en lib/fortune.js). Es tradicional (pero no obligatorio) especificar las importaciones al principio del archivo, así que al principio del archivo meadowlark.js, añade la siguiente línea(ch04/meadowlark.js en el repositorio adjunto):

const fortune = require('./lib/fortune')

Observa que anteponemos al nombre de nuestro módulo ./. Esto indica a Node que no debe buscar el módulo en el directorio node_modules; si omitiéramos ese prefijo, fallaría.

Ahora, en nuestra ruta para la página Acerca de, podemos utilizar el método getFortune de nuestro módulo:

app.get('/about', (req, res) => {
  res.render('about', { fortune: fortune.getFortune() } )
})

Si nos estás siguiendo, vamos a confirmar esos cambios:

git add -A git commit -m "Moved 'fortune cookie' into module."

Descubrirás que los módulos son una forma potente y sencilla de encapsular funcionalidad, lo que mejorará el diseño general y la mantenibilidad de tu proyecto, además de facilitar las pruebas. Consulta ladocumentación oficial sobre módulos de Node para obtener más información.

Nota

Los módulos Node se denominan a veces módulos CommonJS (CJS), en referencia a una especificación antigua en la que se inspiró Node. El lenguaje JavaScript está adoptando un mecanismo oficial de empaquetado, llamado Módulos ECMAScript (ESM). Si has estado escribiendo JavaScript en React u otro lenguaje frontend progresivo, puede que ya estés familiarizado con ESM, que utiliza import y export (en lugar de exports, module.exports yrequire). Para más información, consulta la entrada del blog del Dr. Axel Rauschmayer"Módulos ECMAScript 6: la sintaxis definitiva".

Conclusión

Ahora que tenemos más información sobre Git, npm y los módulos, estamos preparados para hablar de cómo podemos obtener un producto mejor empleando buenas prácticas de control de calidad (QA) en nuestra codificación.

Te animo a que tengas en cuenta las siguientes lecciones de este capítulo:

  • El control de versiones hace que el proceso de desarrollo de software sea más seguro y predecible, y te animo a que lo utilices incluso en proyectos pequeños; ¡crea buenos hábitos!

  • La modularización es una técnica importante para gestionar la complejidad del software. Además de proporcionar un rico ecosistema de módulos que otros han desarrollado a través de npm, puedes empaquetar tu propio código en módulos para organizar mejor tu proyecto.

  • Los módulos Node (también llamados CJS) utilizan una sintaxis diferente a la de los módulos ECMAScript (ESM), y puede que tengas que cambiar entre las dos sintaxis cuando pases del código frontend al backend. Es una buena idea familiarizarse con ambas.

Get Desarrollo Web con Node y Express, 2ª Edición now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.