NPM ile Semantik Versiyonlama Kullanımı

Tuğsan Ünlü
5 min readJul 12, 2020
byte archer — semantic versioning cheatsheet

NPM’de semantik versiyonlama, hakkında pek Türkçe kaynak olmayan bir konu. O yüzden bu yazıda ne olduğundan ve neden önemli olduğundan bahsetmek niyetindeyim.

Başlığı NPM olarak attım ve örneklerimi de yine NPM üzerinden vereceğim. Fakat Yarn veya PHP ekosistemindeki Composer da çok benzer yaklaşımlar kullanıyorlar semantik versiyonlama için. O yüzden yazacaklarım farklı paket yönetim sistemleri için de geçerli olacaktır.

NPM üzerinden herhangi bir paket kurarak hızlıca başlayalım.

npm install webpack

Üstteki komutu çalıştırdığımızda varsayılan NPM paket deposundan (registry) webpack isimli paket ve bağımlılıkları bulunup projemiz içerisindeki node_modules dizinine indirilecektir. Hemen kontrol edelim.

npm view webpack

Üstteki komut bize ilgili paket hakkında hangi paket deposu üzerinde tutulduğu, bağımlılıkları, geliştiricileri ve versiyonları gibi detaylı bilgiler verir.

“npm view webpack” çıktısının ufak bir bölümü

Çıktıdan da anlayacağımız üzere webpack paketinin 4.43.0 versiyonu (aksini belirtmediğimiz için yayımlanmış son versiyonu), NPM’in varsayılan deposu registry.npmjs.org’dan indirilmiş. Fakat bu bilgileri bize NPM sunucusu veriyor. Bir de projemiz içerisinde bağımlılıkların tutulduğu package.json dosyasından kontrol edelim.

package.json içeriği

Ufak bir farkla aynı sonucu görüyoruz. O da versiyon numarasının başındaki caret, yani düzeltme (^) işareti. Bu işaretin ne anlama geldiğinden bahsetmeden evvel semantik versiyonlamanın ne olduğundan bahsetmemiz gerekiyor.

Semantik Versiyonlama

Geliştirdiğimiz uygulamaların, paketlerin hatta yazdığımız metinlerin dahi versiyon takibini yapmak için belli yöntemler kullanıyoruz. Bunlar kabaca semantik versiyonlama (SemVer) ve takvim versiyonlama (CalVer) olarak ikiye ayrılıyor. Semantik versiyonlama, söz konusu çıktının versiyonlanması esnasında yapılan değişiklikleri göz önünde bulundururken takvim versiyonlama değişikliğin zamanıyla ilgileniyor.

Webpack örneği üzerinden gidersek elimizde 4.43.0 gibi bir semantik versiyon numarası var. Bu numarayı zihnimizde MAJOR.MINOR.PATCH olarak canlandırıp okumamız gerekiyor.

MAJOR, paket içerisinde geriye dönük uyumluluğu olmayan, kapsamlı mimari değişikliklerinin yapıldığını bize söylerken MINOR geriye dönük uyumluluğu olan yeni özelliklerin eklendiğini söylüyor. PATCH ise aynı MAJOR ve MINOR değişiklikler içerisinde yapılan hata veya metin düzeltmeleri gibi yamaların yapıldığını söylüyor.

*Kimi zaman bu sıraya dördüncü bir versiyon olarak BUILD eklenebiliyor. Fakat NPM bunu kullanmıyor. Onun yerine pre-release etiketlerini kullanıyor.

Semantik versiyonlamanın ne olduğundan bahsettiysek konumuza geri dönebiliriz. NPM üzerinden indirip projeye dahil ettiğimiz paketleri zaman geçtikçe güncelleme ihtiyacı hissediyoruz. Bunun sebebi yeni bir özelliğe kavuşmak veya bir güvenlik zafiyetini ortadan kaldırmak olabileceği gibi o paketi kullanan farklı bir paketin ihtiyaç duyduğu asgari gereksinimleri sağlamak da olabilir.

Paket güncelleme pratiklerini yapabilmek için az önce kurduğumuz Webpack’in güncel versiyonunu kaldırıp yerine 6 sene önce yayımlanan 1.1.10 versiyonunu kuralım.

npm uninstall webpack
npm install webpack@1.1.10

Paket isminin sonuna koyacağımız “@versiyon numarası” ekiyle herhangi bir paketin spesifik olarak istediğimiz versiyonunu kurabiliyoruz. Kurulum sonrası package.json dosyamızdaki bağımlılık bölümü aşağıdaki hâle geldi.

Şimdi paketi herhangi bir versiyon numarası belirtmeden güncelleyelim.

npm update webpack

Varsayılan davranış olarak paketin en güncel versiyonuna yükseltilmesini bekleyebiliriz.

Fakat güncelleme sonrasında package.json dosyasını kontrol ettiğimizde 1.1.10 olan versiyonun 1.15.0'a yükseldiğini görüyoruz. Yazının başında webpack paketinin şu anda yayımlanmış en güncel versiyonunun 4.43.0 olduğunu görmüştük. Peki neden o versiyona değil de böyle bir ara versiyona güncellendi paket?

Bu soru üstte değindiğimiz, package.json dosyasındaki paket versiyonun başında yer alan caret (^) işaretine götürüyor bizi. NPM, kullandığı semantik versiyonlama sisteminde paketlerin yükseltilebileceği aralıklar oluşturuyor ve bunları ^, ~, -, x gibi işaretlerle belirtmemizi sağlıyor. Kısaca bunları açıklayayım.

^

Majör versiyon değişmeden paketi var olan en güncel versiyona yükseltir. Örneğin, ^1.1.1 versiyonuna sahip bir paketin güncellenebileceği en yüksek versiyon 1.9.9'dur. Majör versiyon asla 2 olmaz. Minör ve patch versiyonları yükseltilir.

Majör versiyon 0 ise yalnızca patch versiyonlarını güncellerken hem majör hem de minör versiyon 0 ise herhangi bir güncelleme yapmaz.

2014'ten bu yana varsayılan olarak kurulum esnasında tüm paket versiyonları ^ ile işaretlenir.

~

Minör versiyon değişmeden paketi var olan en güncel versiyona yükseltir. Yani yalnızca patch versiyonları güncellenir. ~1.1.1 versiyonuna sahip bir paketin güncellenebileceği en yüksek versiyon 1.1.9'dur.

Eğer patch versiyonu belirtilmezse minör versiyon güncellemelerini yapar. ~1.1 versiyonuna sahip paketi, yine majör versiyonu değişmeden 1.9'a kadar günceller.

-

Paketin güncellenebileceği bir versiyon aralığı tanımlamamızı sağlar. Örneğin, bir paketin 1.1.1–2.1.1 versiyonları arasında kalmasını bu şekilde sağlayabiliriz. Bu paketin versiyon 1.1.1'den düşük, 2.1.1'den yüksek olamaz.

x

Paketin güncellenebileceği majör, minör veya patch versiyonlarından birini isteğe bağlı olarak serbest bırakır. Örneğin, 1.1.x versiyonuna sahip bir paket majör ve minör versiyonu değişmeden 1.1.1–1.1.9 aralığında herhangi bir versiyona güncellenebilir.

Peki, Tüm Bunlara Neden İhtiyacımız Var?

Üzerinde çalıştığımız projelere üçüncü parti onlarca paket dahil edebiliyoruz. Bu paketler de arkada başka onlarca pakete bağımlı oluyorlar. Maalesef her kullanım senaryosunda bu paketlerin birbirinden bağımsız ve izole şekilde çalışabilmesi mümkün olmuyor.

Webpack paketinin bağımlılık ağacı

Somut bir örnek vermek gerekirse, projemizde üçüncü parti bir HTTP istemcisi ve bambaşka bir geliştiricinin bu HTTP istemcisi için yazdığı bir eklentiyi kullanıyoruz diyelim. Bu iki paketin geliştirme süreci birbirinden habersiz olarak farklı kişiler tarafından yürütülüyor. Derken, HTTP istemcisinin geliştiricisi radikal bir karar alıp 1.9 versiyonunda sunduğu bir API’ı 2.0 versiyonunda kullanımdan kaldırıyor. İstemci geliştiricisinin yaptığı gayet olağan bir şey ve semantik versiyonlama kurallarına da uyarak bunu bildiriyor.

Fakat o API’ı kullanan eklenti geliştiricisinin bu değişiklikten ne zaman haberi olacak? Haberi olsa dahi istemci geliştiricisi kendisine yeni bir API sunacak mı? Sunsa dahi kendisi eklentisini güncelleyecek mi? Tüm bunlar muamma ve çözümü bir parmak şıklatmasıyla olacak sorunlar değil. Biz bu sorunlar yaşanırken projemizde kullandığımız tüm paketleri bir anda en güncel versiyonlarına yükseltmiş olsaydık eklenti geliştiricisinin bize sunduğu imkân her neyse ondan mahrum kalmış olacaktık. Eklentinin proje içerisindeki konumuna göre de belki hâlihazırda çalışan özelliklerimiz çalışmayacak ve hiç tahmin etmediğimiz hatalar meydana gelecekti. Bunların önüne geçmek için semantik versiyonlamayı iyi anlamak ve uygulamak gerekiyor.

NPM’in çok pratik bir semantik versiyon hesaplama aracı var. Paket ismini yazıp hangi versiyonlama kuralının hangi versiyonları içerip içermeyeceğini kolayca görüntüleyebiliyoruz. Ona ek olarak yazıda kullandığım bazı kaynakları da aşağıya ekliyorum.

npm semantik versiyon hesaplama aracı

NPM’deki semantik versiyonlama mühim ve kapsamlı bir konu. Eksik kaldığını fark ettiğim veya aklıma gelip eklemek istediğim bir şey olursa yazıyı zamanla güncelleyeceğim.

Düzenleme: Ömer Ak’ın uyarısı üzerine (bkz: yorumlar) bir düzeltme yapmış olayım. Örneklerde sıkça, “bu paketin güncellenebileceği en yüksek versiyon 1.9.9'durgibi cümleler kullanmışım. Burada kastetmek istediğim duruma göre majör veya minör versiyonların değişmeden paketin güncellenebilecek en yüksek versiyona güncellenmesidir. Versiyon örneklerini tek haneli olarak verdiğim için 1.9.9 sembolik bir son versiyondur. Elbette 1.9.10, 1.9.9'dan teknik olarak daha günceldir. Örneklerde asıl dikkat edilmesi gereken nokta, majör veya minör versiyonların artmamasıdır.

Kaynaklar

https://semver.org/
https://semver.npmjs.com/
https://docs.npmjs.com/about-semantic-versioning
https://docs.npmjs.com/misc/semver
https://npm.anvaka.com/#!/view/2d/webpack
https://nodesource.com/blog/semver-tilde-and-caret/
https://bytearcher.com/goodies/semantic-versioning-cheatsheet/

--

--