Guillermo Tejero Marín entró en marzo en un proyecto de una consultora de Valladolid: PHP, cuatro años de historia y catorce personas tocándolo. Clonó el repositorio, escribió composer install y a los dos minutos tenía el proyecto funcionando. Nada de lo que aprobó en el módulo hizo falta ese día.
Tres semanas después, un compañero escribió composer update para poner las librerías al día y rompió cuarenta pruebas de golpe. Las palabras install y update se diferencian en cinco letras y hacen cosas opuestas con el mismo composer.lock delante.
Ahí está el tema. La sintaxis de PHP le llevó a Guillermo un fin de semana en primero de DAW; el ecosistema alrededor lleva ya cuatro meses y no se ha acabado.
Cinco gestores de paquetes, y una sola forma
Cada lenguaje trae una herramienta que hace lo mismo: leer un fichero donde el proyecto declara qué necesita, descargarlo, y anotar en un segundo fichero la versión exacta que descargó. Cambia el nombre y no cambia el trabajo.
- PHP: composer.json declara, composer.lock anota, composer install instala.
- Java: pom.xml declara, y Maven no anota nada aparte porque la versión ya va escrita entera.
- JavaScript: package.json declara, package-lock.json anota, npm ci instala.
- Python: pyproject.toml declara, uv.lock anota, uv sync instala.
- Rust: Cargo.toml declara, Cargo.lock anota, cargo build instala y compila.
Reconocer esa tabla ahorra la mitad del tiempo de aprender el siguiente lenguaje, porque lo que se aprende ya no es una herramienta sino un nombre distinto para una que ya se usaba. Y explica por qué la primera pregunta delante de un repositorio ajeno no es en qué lenguaje está, sino qué fichero declara y cuál anota.
Lo que sí cambia entre lenguajes es cuánto trae puesto cada uno. Contando todo lo que hay que instalar para que un programa conteste una petición HTTP, JavaScript necesita 49 paquetes, Rust 4, Python 2 y Go 0. Un número alto no es un defecto: significa que el trabajo lo hace un registro en lugar de un comité, y se paga en árbol de dependencias en vez de en años de espera.
Los tres números de 2.4.1, y los dos signos que los aflojan
Una versión como 2.4.1 son tres números con un contrato detrás: 2.4.1 es la versión mayor 2, la menor 4 y el parche 1. El mayor sube cuando algo que funcionaba deja de funcionar, el menor cuando se añade sin romper, y el parche cuando se arregla sin añadir. Quien publica la librería promete eso, y el gestor de paquetes se apoya en la promesa.
Por eso un proyecto casi nunca pide una versión exacta, sino un rango, y el rango se escribe con dos signos:
- El circunflejo de ^2.4.1 acepta la 2.9.0 y rechaza la 3.0.0.
- La virgulilla de ~2.4.1 acepta la 2.4.9 y rechaza la 2.5.0.
La librería que rompió las cuarenta pruebas de Guillermo estaba declarada con circunflejo, subió de 6.4.2 a 6.5.0 y cumplió el contrato: no rompió nada a propósito. Cambió el orden en que devolvía una consulta, que el contrato no cubre y las pruebas sí notaban.
Maven es la excepción que conviene tener presente en Java, y en España se tropieza con ella pronto: Maven no tiene circunflejo, y en un pom.xml la versión se escribe entera. Sale un fichero más largo y una sorpresa menos, y esa diferencia se nota el día que se pasa de un proyecto a otro dentro de la misma empresa.
composer install lee el lockfile y composer update lo reescribe
El fichero que anota es el que decide si dos ordenadores tienen el mismo programa delante. Guillermo instaló la 6.4.2 porque estaba escrita en composer.lock, no porque estuviera en composer.json, que solo pedía cualquier 6 punto algo.
De ahí sale la regla entera: composer install lee el lockfile y composer update lo reescribe. El primero es el comando de todos los días y el de la máquina que compila; el segundo es una decisión, se toma a propósito, se mira lo que ha cambiado y se prueba antes de subirlo. npm distingue lo mismo con npm ci y npm install, y uv con uv sync y uv lock.
El lockfile fija la versión exacta que se instaló, y va dentro del repositorio. Fuera de él no sirve para nada, porque lo único que hacía era dar la misma respuesta en la segunda máquina.
El lockfile tampoco arregla la otra mitad del anillo. La versión del intérprete se exige en composer.json, no en el lockfile: una línea en el bloque require que dice php y el rango, y con ella composer se niega a instalar donde no se cumple. Sin esa línea el proyecto entra igual en el portátil con PHP 8.3 y en el hosting compartido con 8.1, y lo que revienta es una función que existe en uno y no en el otro. uv guarda ese dato en un fichero .python-version y Node en .nvmrc.
El proyecto que Guillermo tiene en casa, en Python, no tiene ninguno: lo montó con pip y un requirements.txt con los nombres sueltos. Funciona, y volverá a montarse distinto cada vez que alguien lo instale, incluido él mismo dentro de seis meses.
Lo que en el aula ya venía montado
Aquí se para casi todo el que llega de un ciclo, y el motivo es material y no de actitud: el ordenador del aula viene con la imagen puesta. El JDK instalado, el entorno instalado, el servidor levantado, la base de datos con su usuario creado, y todo eso se restaura solo entre grupos. Un curso de dos años se puede terminar sin haber instalado nunca una dependencia.
El primer día en un cliente el portátil está vacío, y la primera tarea es dejarlo capaz de arrancar un proyecto que ya existe. Eso no estaba en ningún módulo, no lo pregunta ninguna prueba, y no se parece a nada de lo que examinaron, así que la conclusión que llega sola es que falta base. Falta el anillo tercero, y se aprende en dos semanas.
La otra mitad de esta factura está escrita en inglés. El mensaje de error de composer, la documentación de la librería y la respuesta que resuelve el problema vienen en inglés, y eso no es una anécdota sino una parte del oficio que se aprende aparte.
Qué abrir en 2026, y en qué orden se abre
Composer apareció en 2012 y sustituyó a PEAR y a la costumbre de copiar una carpeta de librerías dentro del proyecto. Es gratis, tiene el código abierto y es lo que se va a encontrar quien salga de DAW, porque buena parte del PHP que se mantiene en España se instala así. El fallo típico es escribir update cuando se quería install.
Maven apareció en 2004 y sustituyó a los scripts de Ant escritos a mano en cada proyecto. También es gratis, y es la herramienta con la que se compila casi todo el Java de empresa que hay aquí. El fallo típico es tratar el repositorio local de la carpeta .m2 como si fuera parte del proyecto.
Para Python, uv salió en 2024, es gratis y de código abierto, y sustituyó a pip, virtualenv y pyenv, que eran tres herramientas repartiéndose un solo trabajo. npm viene dentro de Node y sustituyó a bajarse las librerías a mano, que es lo que se hacía antes de 2010.
El orden en el que estas cosas se cogen no es el del temario. Con la sintaxis ya aprobada, el anillo que toca es el tercero: instalar un proyecto ajeno, romperlo y volverlo a dejar como estaba. PHP y Java son los dos sitios donde eso se practica con más probabilidad de que luego sirva en una nómina; Python y JavaScript son donde suele estar el proyecto propio, que es el que enseña porque nadie lo tenía montado.
Un segundo lenguaje devuelve la sensación del primer mes y aplaza lo demás, así que vale la pena añadirlo cuando el anillo tercero ya está cerrado en el primero. Rust es el caso que más lo enseña, porque su compilador discute cada decisión sobre memoria y eso es exactamente lo que en los fundamentos de programación se estudia por escrito.
Lo que hay que decidir ahora es cuál es el primero, y esa pregunta tiene su propia página en qué lenguaje elegir. Después de esa vienen las dos que este tema deja abiertas a propósito: dónde corre el programa, que está en el desarrollo web, y cómo sale de tu máquina, que está en la puesta en producción.