npm, Cargo, Composer y pip interpretan de manera diferente la misma abreviatura de semver, y solo dos de las trece expresiones de rango que probé se comportan de forma idéntica en los cuatro.

Escribí analizadores (parsers) independientes para cada gestor, seguí sus especificaciones oficiales y luego ejecuté cada rango contra una matriz de diecinueve números de versión. La cuadrícula de 13 × 19 (247 celdas) mostró un panorama fragmentado: solo 17 celdas dieron la misma respuesta de "sí" o "no" en todas las herramientas, y la mayoría de esas 17 fueron rechazos unánimes. En resumen, las herramientas coinciden en el "no" mucho más a menudo que en el "sí".

Por qué divergen las herramientas

Los cuatro gestores afirman seguir el Versionado Semántico (Semantic Versioning), pero cada uno añade sus propias reglas de abreviatura.

  • Caret (^) y dot-x – npm admite el caret; pip ignora tanto el caret como el dot-x. Solo eso crea un desacuerdo de 49 celdas entre npm y pip.
  • Versiones simples (Bare versions) – Cargo trata una versión simple como 1.2.3 como un rango caret, lo que significa "compatible con 1.x". npm y Composer leen la misma cadena como una coincidencia exacta, aceptando únicamente 1.2.3.
  • Tilde (~) – La tilde de Cargo fija la versión minor (~1.2 coincide con 1.2.* pero no con 1.3.0). La tilde de Composer fija la versión major (~1.2 coincide con 1.*). Las dos interpretaciones divergen en cualquier versión que cambie el componente minor.

El manejo de las versiones preliminares (pre-releases) añade otra sorpresa. Un rango como ^1.2.3 no incluye 2.0.0-rc.1, a pesar de que la parte numérica es inferior al límite superior. La regla: las versiones preliminares se excluyen a menos que el rango mencione explícitamente un identificador de pre-release.

La única sintaxis segura entre herramientas

El experimento demuestra que los rangos de desigualdad explícitos —por ejemplo, >=1.2.0 <2.0.0— se comportan de la misma manera en npm, Cargo, Composer y pip. Cada gestor trata los dos límites como límites numéricos literales, sin semántica oculta de caret o tilde.

Si necesitas expresar "cualquier versión 1.x que sea al menos 1.2", escríbelo de forma completa. Cuesta unos pocos caracteres extra, pero garantiza una resolución predecible dondequiera que se ejecute el código.

Cuándo sigue teniendo sentido el uso de abreviaturas

Las abreviaturas siguen siendo atractivas para proyectos de un solo lenguaje. Dentro de un ecosistema puramente de npm, ^1.2.3 captura de forma sucinta "compatible con futuros lanzamientos minor y patch". Lo mismo ocurre con el caret de Cargo o la tilde de Composer cuando sabes que la herramienta que consume el paquete no cambiará. El peligro aparece cuando el mismo manifiesto se reutiliza entre diferentes lenguajes, o cuando un trabajo de CI descarga dependencias de múltiples ecosistemas.

Conclusión

Confiar en las abreviaturas de semver para la compatibilidad entre lenguajes es una apuesta arriesgada. La única forma fiable de garantizar que una restricción de versión signifique lo mismo en todas partes es escribir desigualdades explícitas. Cuando necesites brevedad, mantente dentro de un único ecosistema; cuando necesites consistencia, cambia la abreviatura por la claridad.