npm, Cargo, Composer và pip đều diễn giải các ký hiệu viết tắt semver theo những cách khác nhau, và chỉ có hai trong số mười ba biểu thức phạm vi mà tôi đã thử nghiệm là hoạt động giống hệt nhau trên cả bốn trình quản lý này.
Tôi đã viết các bộ phân tích (parsers) riêng biệt cho từng trình quản lý, tuân theo các đặc tả chính thức của chúng, sau đó chạy mọi phạm vi so với một ma trận gồm mười chín số phiên bản. Lưới 13 × 19 (247 ô) đã cho thấy một bức tranh rời rạc: chỉ có 17 ô đưa ra cùng một câu trả lời "có" hoặc "không" từ mọi công cụ, và hầu hết trong số 17 ô đó là những trường hợp bị từ chối đồng loạt. Nói tóm lại, các công cụ đồng ý với câu trả lời "không" thường xuyên hơn nhiều so với "có".
Tại sao các công cụ lại khác biệt
Cả bốn trình quản lý đều tuyên bố tuân theo Semantic Versioning, nhưng mỗi trình quản lý lại thêm vào các quy tắc viết tắt riêng của mình.
- Caret (^) và dot-x – npm hỗ trợ caret; pip bỏ qua cả caret và dot-x. Chỉ riêng điều này đã tạo ra sự bất đồng ở 49 ô giữa npm và pip.
- Phiên bản trần (Bare versions) – Cargo coi một phiên bản thuần túy
1.2.3là một phạm vi caret, nghĩa là "tương thích với 1.x". npm và Composer đọc cùng một chuỗi đó như một sự khớp chính xác, chỉ chấp nhận đúng1.2.3. - Tilde (~) – Dấu tilde của Cargo chốt phiên bản minor (
~1.2khớp với1.2.*nhưng không khớp với1.3.0). Dấu tilde của Composer chốt phiên bản major (~1.2khớp với1.*). Hai cách diễn giải này khác nhau ở bất kỳ phiên bản nào thay đổi thành phần minor.
Việc xử lý bản pre-release mang lại một bất ngờ khác. Một phạm vi như ^1.2.3 không bao gồm 2.0.0-rc.1, mặc dù phần số nhỏ hơn giới hạn trên. Quy tắc là: các bản pre-release bị loại trừ trừ khi phạm vi đó đề cập rõ ràng đến một định danh pre-release.
Cú pháp an toàn duy nhất dùng chung cho các công cụ
Thí nghiệm cho thấy các phạm vi bất đẳng thức rõ ràng—ví dụ: >=1.2.0 <2.0.0—hoạt động giống nhau trong npm, Cargo, Composer và pip. Mọi trình quản lý đều coi hai ranh giới là các giới hạn số học thực tế, không có các ngữ nghĩa ẩn của caret hay tilde.
Nếu bạn cần diễn đạt "bất kỳ phiên bản 1.x nào ít nhất là 1.2", hãy viết đầy đủ ra. Nó tốn thêm một vài ký tự, nhưng nó đảm bảo việc phân giải (resolution) có thể dự đoán được ở bất cứ nơi nào mã nguồn chạy.
Khi nào việc viết tắt vẫn có ý nghĩa
Viết tắt vẫn rất hấp dẫn đối với các dự án chỉ sử dụng một ngôn ngữ. Trong một hệ sinh thái npm thuần túy, ^1.2.3 tóm gọn một cách súc tích ý nghĩa "tương thích với các bản phát hành minor và patch trong tương lai". Điều tương tự cũng đúng với caret của Cargo hoặc tilde của Composer khi bạn biết chắc rằng công cụ sử dụng sẽ không thay đổi. Nguy hiểm xuất hiện khi cùng một tệp manifest được tái sử dụng qua các ranh giới ngôn ngữ, hoặc khi một công việc CI kéo các phụ thuộc từ nhiều hệ sinh thái khác nhau.
Bài học rút ra
Việc dựa vào các ký hiệu viết tắt semver để đạt được khả năng tương thích đa ngôn ngữ là một sự mạo hiểm. Cách duy nhất đáng tin cậy để đảm bảo rằng một ràng buộc phiên bản có cùng ý nghĩa ở mọi nơi là viết các bất đẳng thức rõ ràng. Khi bạn cần sự ngắn gọn, hãy ở trong một hệ sinh thái duy nhất; khi bạn cần sự nhất quán, hãy đánh đổi sự viết tắt để lấy sự rõ ràng.
