El problema: "en mi ordenador funciona"
Ana ha creado TasteMatch, una pequeña aplicación para consultar recetas desde el navegador. La abre en su ordenador, busca una receta y todo funciona.
Te envía la carpeta con el código, es decir, los archivos con las instrucciones del programa. Intentas ponerla en marcha en tu equipo, pero aparece un error y no puedes consultar las recetas.
Ana vuelve a probar y te dice:
«Qué raro… En mi ordenador funciona».
Ordenador de Ana
TasteMatch funciona
│
│ Copiamos el mismo código
▼
Tu ordenador
TasteMatch no arranca
Si el código es exactamente el mismo, ¿qué ha cambiado? Antes de buscar una herramienta que lo resuelva, vamos a entender qué puede estar pasando.
Una receta no es una tarta
Imagina que Ana te entrega también su receta de una tarta. Los pasos están claros, pero al llegar a tu cocina descubres que te falta harina o que no tienes horno.
¿Está mal la receta? No necesariamente. Tener las instrucciones no significa disponer de lo necesario para seguirlas.
Incluso con los ingredientes, hay condiciones que importan: no obtendrás el mismo resultado si usas otras cantidades o cambias la temperatura.
Con una aplicación ocurre algo parecido:
| En la cocina | En una aplicación |
|---|---|
| La receta indica los pasos. | El código contiene instrucciones. |
| Necesitas ingredientes y utensilios. | Puedes necesitar otros programas y librerías. |
| Importan las cantidades y la temperatura. | Importan las versiones y la configuración. |
| Cocinas en una cocina preparada para ello. | Ejecutas la aplicación en un entorno compatible. |
Es solo un símil: una aplicación no funciona literalmente como una receta. Nos ayuda a separar dos ideas: las instrucciones y las condiciones necesarias para ejecutarlas.
El entorno también forma parte del problema
El código tiene que ejecutarse en una máquina: tu ordenador, el de Ana o un servidor, un equipo que ofrece un servicio a otros equipos, como una web.
Llamamos entorno de ejecución al conjunto de software, recursos y configuración con los que funciona una aplicación en esa máquina.
Para entenderlo, vamos a mirar qué necesita nuestra aplicación ficticia, TasteMatch:
TasteMatch
├── Node.js para ejecutar su código
├── Librerías para realizar ciertas tareas
├── Configuración para indicar cómo arrancar
├── Un archivo con las recetas
└── Un sistema compatible donde ejecutarse
No necesitas instalar nada para seguir este ejemplo. Vamos a distinguir cada pieza.
Aplicación, runtime y librerías
TasteMatch es la aplicación que queremos utilizar. Su código está escrito en JavaScript, un lenguaje de programación.
En este ejemplo, la parte que sirve las recetas se ejecuta con Node.js. Es un runtime: software que permite ejecutar código de un lenguaje. Tener el navegador instalado no sustituye a Node.js para ejecutar esa parte de TasteMatch.
Algo parecido ocurre si recibes un programa escrito en Python: si solo te entregan el código, normalmente necesitas disponer del intérprete de Python, el programa que lo procesa para ejecutarlo.
Además, una aplicación puede utilizar librerías, que son conjuntos de código ya preparado para tareas concretas. Por ejemplo, una librería puede ayudar a reducir el tamaño de las fotografías de las recetas.
Si TasteMatch necesita esa librería y no está disponible, la función que la utiliza puede fallar aunque hayas copiado correctamente el código de TasteMatch.
A estos elementos de los que depende el programa los llamamos dependencias. Una librería es un ejemplo de dependencia; el software necesario para ejecutar el código también es un requisito del entorno.
Quédate con esta idea: copiar el código no instala automáticamente todo lo que necesita para funcionar.
La base: un sistema compatible
El runtime y las librerías se ejecutan sobre un sistema operativo, como Windows o Linux, que gestiona los recursos de la máquina.
Una aplicación o alguna de sus dependencias puede necesitar características de un sistema concreto. No basta con que el archivo se pueda copiar: el equipo debe poder ejecutar el software que requiere.
Esto tampoco significa que dos sistemas diferentes sean siempre incompatibles. Depende de cómo esté preparada la aplicación y de lo que utilice.
Observa dos equipos con el mismo código
Ana revisa su ordenador y tú revisas el tuyo. Encontráis lo siguiente:
| Qué comparamos | Ordenador de Ana | Tu ordenador |
|---|---|---|
| Código de TasteMatch | La misma copia | La misma copia |
| Node.js | Instalado | No está instalado |
| Librería para las fotografías | Disponible | No está disponible |
| Archivo de recetas | Está en la carpeta esperada | No se ha copiado |
Antes de seguir, piensa:
- ¿Qué impide ejecutar el código con Node.js en tu equipo?
- Si instalases Node.js, ¿podrías asegurar que ya funciona toda la aplicación?
- ¿Hace falta cambiar el código para explicar estas diferencias?
Comprobamos el razonamiento
Sin Node.js no puedes ejecutar esta parte de TasteMatch de la forma prevista. Pero instalarlo solo resuelve una diferencia: todavía faltan la librería y el archivo de recetas.
El código puede ser correcto y, aun así, no encontrar lo que necesita. Según qué elemento falte, el fallo puede aparecer al arrancar o al usar una función concreta, como abrir una fotografía.
«En mi ordenador funciona» nos dice que existe un entorno donde funciona. Todavía no nos dice cómo preparar el nuestro.
Las versiones también importan
Ahora imagina que ambos equipos tienen Node.js y las mismas librerías. ¿Es suficiente con que los nombres coincidan?
El software cambia con el tiempo. Cada versión identifica una edición de una herramienta o librería. Una versión nueva puede añadir funciones o cambiar cómo se utiliza alguna de las anteriores.
Supongamos que TasteMatch utiliza una función de su librería de fotografías:
Equipo de Ana
Versión con la función que usa TasteMatch
↓
La fotografía se procesa correctamente
Otro equipo
Versión antigua que no tiene esa función
↓
Esa operación falla
También puede ocurrir que una versión más nueva cambie o retire una función que la aplicación necesitaba.
No todas las diferencias de versión provocan errores. Lo importante es conocer qué versiones son compatibles y con cuáles se ha probado la aplicación, en lugar de instalar una cualquiera y dar por hecho que servirá.
Tener lo mismo instalado no es tener el mismo entorno
Imagina que ya has igualado las versiones. TasteMatch sigue buscando el archivo de recetas en una carpeta que solo existe en el ordenador de Ana.
El software está instalado, pero falta revisar la configuración: los valores que indican a la aplicación cómo debe funcionar, por ejemplo, dónde buscar sus archivos.
Configuración y archivos
Para leer las recetas, TasteMatch necesita la ruta correcta, que indica dónde está el archivo, y permisos para acceder a él.
La configuración puede guardarse en un archivo o recibirse mediante variables de entorno: valores con nombre que se proporcionan al programa al ejecutarlo.
Indicar dónde buscar las recetas no sustituye al archivo: también debe estar disponible.
Puertos y servicios disponibles
TasteMatch recibe las peticiones del navegador en un puerto, identificado por un número: en este ejemplo, el 3000. Un conflicto con otra aplicación que lo utilice puede impedir que arranque.
También puede depender de servicios externos, como un servicio de correo para enviar recetas. Esa función necesita que el servicio esté disponible y que el acceso esté bien configurado; copiar TasteMatch no basta.
Preparar otra máquina
Ana quiere llevar TasteMatch a un servidor para que otras personas puedan utilizarlo. Una forma habitual de hacerlo consiste en:
- Comprobar que el sistema es compatible.
- Preparar el runtime y las librerías en versiones compatibles.
- Copiar la aplicación y sus archivos necesarios.
- Ajustar la configuración, los permisos y el acceso a los servicios que utilice.
- Arrancar la aplicación y comprobar sus funciones, no solo que se abre.
Esta forma de trabajar es válida. Podemos documentarla y automatizar pasos para reducir errores. La dificultad aparece cuando repetimos el proceso y cada equipo acaba con pequeñas diferencias que nadie ha registrado.
Ordenador de Ana
↓ Preparar y comprobar
Tu ordenador
↓ Preparar y comprobar
Servidor de pruebas
↓ Preparar y comprobar
Servidor de producción
Un servidor de pruebas permite comprobar cambios antes de usarlos de verdad. El de producción presta el servicio a las personas que utilizan la aplicación.
Si cada instalación se hace de memoria, podemos olvidar una librería, escoger otra versión o dejar un valor de configuración distinto. El problema no es solo conseguir que funcione una vez: es poder repetir la preparación y saber qué hemos preparado.
Tu turno: ¿qué le pedirías a Ana?
Vuelve a TasteMatch y responde con tus propias palabras:
- ¿Basta con recibir la carpeta de código? Explica por qué.
- ¿Qué comprobarías antes de intentar arrancarla en otro equipo?
- Aunque el código no cambie, ¿qué diferencias podrían afectar al resultado?
- ¿Qué información pedirías para que otra persona pudiera preparar el entorno sin adivinar?
Una respuesta útil incluiría el sistema compatible, el runtime, las librerías y sus versiones, los archivos necesarios y los valores de configuración. También cómo arrancar la aplicación y cómo comprobar que funciona.
No se trata de copiar sin pensar todos los ajustes de Ana: una ruta de su ordenador puede no existir en el servidor. Necesitamos saber qué significa cada ajuste y qué valor corresponde en cada entorno.
¿Y si tenemos dos aplicaciones?
Hasta ahora solo intentábamos ejecutar TasteMatch. Imagina que el servidor también tiene otra aplicación y ambas utilizan una misma librería instalada para las dos.
En este ejemplo ficticio, sus requisitos son incompatibles:
Un mismo servidor
├── TasteMatch
│ └── Necesita la versión 2 de una librería
└── Otra aplicación
└── Necesita la versión 1 de esa librería
Si sustituimos la versión compartida para que funcione TasteMatch, podríamos romper la otra aplicación. Cambiar una configuración compartida también puede afectar a ambas.
No significa que dos aplicaciones no puedan convivir ni que toda actualización cause problemas. El conflicto aparece cuando comparten algo y necesitan que sea diferente.
Piensa en estas preguntas:
- ¿Cómo evitamos que preparar una aplicación cambie lo que necesita la otra?
- ¿Cómo dejamos claro qué versiones y ajustes pertenecen a cada una?
- ¿Cómo repetimos esa preparación en otro servidor?
Todavía no vamos a elegir una herramienta para resolverlo. Primero pongamos nombre a lo que necesitamos conseguir.
Dos necesidades: aislamiento y reproducibilidad
Aislamiento significa, en este contexto, separar los entornos de las aplicaciones para reducir las interferencias entre ellas. Nos interesa poder preparar lo que necesita TasteMatch sin cambiar innecesariamente lo que utiliza otra aplicación.
Reproducibilidad significa poder volver a preparar un entorno a partir de una descripción y unos pasos definidos. Queremos que otra persona, o nosotros dentro de un mes, pueda repetirlo sin depender de lo que alguien recuerde.
Son necesidades relacionadas, pero diferentes. Podemos tener TasteMatch separado de otra aplicación y no recordar cómo lo preparamos. También podemos repetir una instalación que sigue mezclando las dependencias de ambas.
Importante: reproducir un entorno no significa que cualquier programa vaya a funcionar en cualquier máquina. Siguen importando la compatibilidad del sistema, los recursos disponibles y los servicios de los que dependa.
El objetivo es controlar las condiciones relevantes y reducir diferencias inesperadas. Hay distintas formas de avanzar hacia ese objetivo; ninguna sustituye la necesidad de entender qué necesita la aplicación.
Ideas que pueden llevarnos a error
«Si tengo el código, tengo todo lo necesario»
Puede faltar el runtime, una librería, un archivo o la configuración para acceder a un servicio. El código es una parte de lo que necesitamos para ejecutar una aplicación.
«Si funciona aquí, funcionará en cualquier ordenador»
Solo hemos comprobado que funciona en unas condiciones concretas. Otro equipo puede tener un sistema, unas versiones o una configuración distintos.
«El problema siempre está en el código»
Un fallo puede estar en el código, en el entorno o en cómo interactúan. Antes de cambiar instrucciones al azar, conviene observar el error y comparar las condiciones en las que funciona y en las que falla.
«Solo hay que instalar la última versión de todo»
Una versión más reciente no garantiza compatibilidad. Necesitamos versiones adecuadas para la aplicación y comprobar los cambios antes de dar una actualización por válida.
Mini reto: de tu equipo a veinte servidores
Una compañera te entrega una aplicación que funciona en su ordenador. Solo te pasa la carpeta del código. En tu equipo falla.
Escribe al menos cinco cosas que comprobarías y explica por qué cada una podría provocar el fallo. Intenta que tus comprobaciones cubran problemas diferentes.
Después imagina que mañana tienes que preparar la misma aplicación en 20 servidores:
- ¿Qué errores podrían aparecer si repites la instalación de memoria?
- ¿Qué información dejarías registrada para repetirla?
- ¿Qué podría ocurrir si alguno de esos servidores ya tiene otras aplicaciones?
- Si la aplicación arranca, pero no puede leer un archivo, ¿por qué no basta con decir «está instalada»?
Compara tu respuesta
Estas son algunas comprobaciones posibles:
| Comprobaría… | Porque… |
|---|---|
| El sistema y el runtime requeridos. | El código necesita poder ejecutarse en ese equipo. |
| Las versiones del runtime y las librerías. | Una función necesaria puede no existir o haberse modificado. |
| Que estén todas las dependencias. | Tener el runtime no significa tener todas las librerías. |
| Los archivos, sus rutas y sus permisos. | Un archivo puede faltar o no ser accesible para la aplicación. |
| La configuración y las variables de entorno. | La aplicación puede estar buscando recursos en otro lugar. |
| El puerto que necesita y los servicios externos. | Puede haber un conflicto con otra aplicación o un servicio no disponible. |
En veinte servidores, un paso olvidado o una versión distinta puede dejar instalaciones diferentes. Registrar los requisitos, los pasos y las comprobaciones ayuda a repetir el proceso. Separar lo que necesita cada aplicación ayuda a reducir conflictos con las demás.
Si has relacionado los fallos con esas condiciones, has entendido el problema. No necesitas conocer todavía la herramienta que utilizaremos después.
Qué debes recordar
- El mismo código puede funcionar en un equipo y fallar en otro porque el entorno cambia.
- El runtime, las dependencias, sus versiones, la configuración y los recursos disponibles importan.
- Necesitamos poder repetir la preparación del entorno y reducir las interferencias entre aplicaciones.
La próxima vez que escuches «en mi ordenador funciona», la pregunta útil será: «¿Qué tiene ese ordenador que necesita la aplicación?»
Y ahora podemos plantear el siguiente paso: ¿podríamos empaquetar una aplicación junto con parte de lo que necesita para ejecutarse y utilizar ese entorno de forma consistente en distintas máquinas compatibles?
En la siguiente unidad conoceremos una forma de abordar esta necesidad: los contenedores. Empezaremos por entender qué son.