Docker & DevOps

    Conventional Commits ve Semantic Versioning

    Commit mesajı standardının sürüm numaralarına ve changelog üretimine nasıl bağlandığı.

    12 dk okuma Güncellendi: 25 Ağustos 2026

    Bir projenin git log çıktısına bakıp "son iki haftada ne değişti" sorusuna cevap veremiyorsanız sorun hafızanızda değil, mesaj disiplininizde. "düzeltme", "son hâli", "wip", "asdf" gibi commit mesajları o an için hızlıdır ama üç ay sonra tarihçe tamamen okunamaz hâle gelir. Conventional Commits, commit mesajına küçük ve makine tarafından okunabilir bir yapı dayatarak bu sorunu kökünden çözer; semantic versioning ise o yapıdan sürüm numaranızın otomatik türetilmesini sağlar.

    Bu rehberde önce Conventional Commits mesaj anatomisini parça parça açacağım, sonra hangi tipin ne anlama geldiğini ve hangisinin sürüm numarasını nasıl etkilediğini tabloyla göstereceğim. Ardından semantic versioning kurallarını (MAJOR, MINOR, PATCH ve ön sürüm etiketleri) pratik örneklerle ele alıp, commit tipinden otomatik CHANGELOG ve etiket üretimine giden zinciri kuracağım. Son bölümde kuralı ekipte gerçekten uygulatmanın yollarını ve en sık düşülen tuzakları anlatacağım.

    Conventional Commits Mesajının Anatomisi#

    Standardın tamamı tek bir kalıba dayanır ve öğrenmesi beş dakika sürer. Bir commit mesajı üç bölümden oluşur: zorunlu bir başlık satırı, isteğe bağlı bir gövde ve isteğe bağlı bir altbilgi. Başlık satırının biçimi şudur:

    <tip>(<kapsam>)<!>: <kısa açıklama>
    

    Buradaki tip sabit bir kelime listesinden gelir, kapsam parantez içinde yazılan ve değişikliğin hangi modülü ilgilendirdiğini söyleyen isteğe bağlı bir etikettir, ! işareti geriye dönük uyumu bozan bir değişikliği işaretler, kısa açıklama ise emir kipinde ve küçük harfle başlayan tek cümledir. Gerçek bir örnek:

    feat(fatura): toplu PDF indirme desteği eklendi
    
    Müşteri panelinde seçili faturaların tek bir ZIP dosyası hâlinde
    indirilmesini sağlar. Sunucu tarafında akış tabanlı üretim kullanıldığı
    için 500 faturaya kadar bellek tüketimi sabit kalıyor.
    
    Refs: #482
    

    Başlık satırının uzunluğunu 72 karakterin altında tutun; git log --oneline çıktısı ve çoğu arayüz bu genişliğe göre kırpar. Açıklamayı "eklendi" yerine "ekle" gibi emir kipiyle yazmak yaygın bir gelenektir çünkü commit, koda uygulandığında ne yapacağını anlatır. Türkçe kullanıyorsanız tutarlı olun; yarısı İngilizce yarısı Türkçe bir tarihçe okunmayı zorlaştırır.

    Geriye dönük uyumu bozan değişiklikler iki şekilde işaretlenebilir: başlıkta iki nokta üst üsteden önce ! koyarak ya da altbilgide BREAKING CHANGE: satırı açarak. İkisini birlikte kullanmak en açık yoldur:

    refactor(api)!: v1 uç noktaları kaldırıldı
    
    BREAKING CHANGE: /api/v1/* altındaki tüm uç noktalar kaldırıldı.
    İstemcilerin /api/v2/* adreslerine geçmesi gerekiyor.
    

    Commit Tipleri ve Hangisini Ne Zaman Kullanmalı#

    Tip listesi kısa tutulmalı; on kelimeyi geçen bir liste kimsenin aklında kalmaz ve karar vermeyi zorlaştırır. Aşağıdaki tablo yaygın kabul gören tipleri, anlamlarını ve sürüm numarasına etkisini özetliyor.

    TipAnlamıSürüme etkisiÖrnek
    featYeni bir kullanıcı özelliğiMINORfeat(panel): iki adımlı doğrulama
    fixHata düzeltmesiPATCHfix(cron): yaz saatinde iki kez çalışıyordu
    perfDavranış aynı, performans iyileşmesiPATCHperf(sorgu): rapor sorgusuna indeks
    refactorDavranış değişmeden iç yapı düzenlemeYokrefactor(auth): token doğrulama ayrıldı
    docsYalnızca belge değişikliğiYokdocs: kurulum adımları güncellendi
    testTest ekleme veya düzeltmeYoktest(fatura): sınır değer testleri
    buildDerleme sistemi, bağımlılıklarYokbuild: node 22'ye geçildi
    ciCI yapılandırmasıYokci: dağıtım adımı yeniden denenir
    choreDiğer bakım işleriYokchore: eski betikler silindi
    styleBiçimlendirme, boşluk, noktalamaYokstyle: prettier uygulandı

    Karar verirken kendinize tek bir soru sorun: bu değişiklik kullanıcının gördüğü davranışı değiştiriyor mu? Cevap "yeni bir şey yapabiliyor" ise feat, "bozuk olan düzeldi" ise fix, "hiçbir şey değişmedi ama kod daha temiz" ise refactor. Performans iyileştirmesi kullanıcı için hissedilir bir düzelmedir ve genellikle perf altında PATCH sayılır.

    chore tipini çöp kutusu gibi kullanma eğilimi çok yaygındır ve tarihçenin değerini düşürür. Bağımlılık güncellemesi build, CI dosyası düzenlemesi ci, belge güncellemesi docs altına aittir. Gerçekten hiçbirine girmeyen bir şey kalırsa chore uygundur.

    Kapsam (scope) alanını da rastgele seçmeyin. Projenizdeki gerçek modül adlarından oluşan kısa bir sözlük belirleyin (api, panel, db, deploy gibi) ve ona sadık kalın. Kapsam sözlüğü, ileride "son sürümde ödeme modülünde ne değişti" sorusunu git log --oneline --grep '^feat(odeme)' ile cevaplanabilir hâle getirir.

    Semantic Versioning: MAJOR.MINOR.PATCH#

    Semantic versioning (kısaca semver), sürüm numarasını üç sayıya böler ve her sayıya kesin bir anlam yükler: MAJOR.MINOR.PATCH. Kural cümlesi tek satırdır ve ezberlemeye değer:

    • MAJOR — geriye dönük uyumu bozan bir değişiklik yaptıysanız artırılır ve MINOR ile PATCH sıfırlanır.
    • MINOR — geriye dönük uyumlu yeni bir özellik eklediyseniz artırılır ve PATCH sıfırlanır.
    • PATCH — geriye dönük uyumlu bir hata düzeltmesi yaptıysanız artırılır.

    Yani 2.4.7 sürümünden sonra bir hata düzeltirseniz 2.4.8, yeni bir özellik eklerseniz 2.5.0, bir API'yi kaldırırsanız 3.0.0 olur. Semver'ın anlamı, sürüm numarasına bakan bir kullanıcının güncellemenin kendisini bozup bozmayacağını okumadan anlayabilmesidir.

    İki ayrıntı sık atlanır. Birincisi, 0.y.z aralığı özeldir: 1.0.0 çıkmadan önce hiçbir şey kararlı sayılmaz ve 0.x içindeki her MINOR artışı uyumu bozabilir. Bu yüzden bir kütüphaneyi 0.x sürümüyle üretimde kullanırken sürümü tam olarak sabitlemek gerekir. İkincisi, ön sürüm ve derleme etiketleri tire ve artı işaretiyle eklenir:

    SürümAnlamıKararlı mı
    1.4.0Normal yayınEvet
    2.0.0-alpha.1Erken test sürümüHayır
    2.0.0-rc.2Yayın adayıHayır
    1.4.0+20260825Derleme meta verisiEvet (öncelik değişmez)

    Sıralamada ön sürümler kendi kararlı sürümlerinden önce gelir: 2.0.0-rc.1 < 2.0.0. Derleme meta verisi ise sıralamada hiç dikkate alınmaz. Bağımlılık dosyalarınızda ^1.4.0 yazdığınızda "1.x içinde en yenisi", ~1.4.0 yazdığınızda "1.4.x içinde en yenisi" demiş olursunuz; bu iki işaretin farkını bilmemek, beklenmedik bir MINOR güncellemesiyle üretimde sürpriz yaşamanın klasik yoludur.

    Commit Tipinden Sürüm Numarasına Giden Zincir#

    İki standardın birlikte anılmasının sebebi şu: Conventional Commits, semver'ın ihtiyaç duyduğu bilgiyi commit anında yakalar. Yayın zamanı geldiğinde son etiketten bu yana atılan commit'lere bakan bir araç, hangi sayıyı artıracağını kendi başına bulabilir.

    Karar mantığı basit bir öncelik sırasıdır:

    1. Commit'lerin herhangi birinde ! işareti veya BREAKING CHANGE: altbilgisi varsa → MAJOR artır.
    2. Yoksa, en az bir feat varsa → MINOR artır.
    3. Yoksa, en az bir fix veya perf varsa → PATCH artır.
    4. Hiçbiri yoksa → yeni sürüm çıkarma.

    Bunu elle de yapabilirsiniz. Son etiketten bu yana ne olduğunu görmek için:

    # En son etiketi bul
    LAST=$(git describe --tags --abbrev=0)
    echo "Son sürüm: $LAST"
    
    # O etiketten bu yana atılan commit'leri tipleriyle listele
    git log --oneline "$LAST"..HEAD
    
    # Uyum bozan var mı
    git log "$LAST"..HEAD --grep='BREAKING CHANGE' --grep='^[a-z]*(.*)\!:' -E --oneline
    
    # Yeni özellik sayısı
    git log "$LAST"..HEAD --oneline --grep='^feat' | wc -l
    

    Sürüme karar verdikten sonra etiketi imzalı ve açıklamalı olarak atın; hafif (lightweight) etiket yerine açıklamalı etiket kullanmak, git describe çıktısının ve yayın notlarının düzgün çalışması için önemlidir:

    git tag -a v2.5.0 -m "v2.5.0 — toplu PDF indirme ve fatura filtreleri"
    git push origin v2.5.0
    

    Dal düzeninizle bu akışın nasıl kesiştiğini merak ediyorsanız Git branch stratejileri yazısında yayın dalı ile sürekli entegrasyon modellerinin sürümleme üzerindeki etkisi anlatılıyor. Etiket atmadan önce tarihçeyi düzenlemeniz gerekirse rebase ve merge farkı yazısındaki güvenli kurallara uymanız işinizi kolaylaştırır.

    Otomatik CHANGELOG Üretimi#

    Standarda uyulan bir tarihçeden yayın notu üretmek tamamen mekanik bir iştir. Harici bir araç kurmadan, sadece git log biçimlendirmesiyle bile kullanılabilir bir CHANGELOG çıkarabilirsiniz:

    #!/usr/bin/env bash
    # changelog.sh — son etiketten bu yana yayın notu üret
    set -euo pipefail
    LAST=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
    RANGE=${LAST:+$LAST..HEAD}
    
    echo "## Yeni Özellikler"
    git log $RANGE --no-merges --pretty='- %s (%h)' --grep='^feat' || true
    
    echo
    echo "## Hata Düzeltmeleri"
    git log $RANGE --no-merges --pretty='- %s (%h)' --grep='^fix' || true
    
    echo
    echo "## Uyumu Bozan Değişiklikler"
    git log $RANGE --no-merges --pretty='- %s (%h)' --grep='BREAKING CHANGE' || true
    

    Bu betiği çalıştırdığınızda düzgün gruplanmış bir markdown çıktısı alırsınız. Daha gelişmiş araçlar (standard-version, semantic-release, git-cliff gibi) aynı mantığı kurar, ek olarak sürüm numarasını hesaplar, paket dosyalarını günceller ve etiketi kendisi atar. Hangi aracı seçerseniz seçin, temel şart aynıdır: mesajlar standarda uygun olmalı. Standart yoksa aracın üreteceği çıktı da anlamsız olur.

    CHANGELOG dosyasını depoda tutmayı alışkanlık hâline getirin. Kullanıcılarınız için tek bakışta "neyi güncelliyorum" cevabını verir; sizin için ise bir yıl sonra "bu davranış ne zaman değişti" sorusunun en hızlı cevabıdır.

    Kuralı Ekipte Gerçekten Uygulatmak#

    Standardı benimsemek kolay, sürdürmek zordur. İnsan iradesine güvenmek yerine üç katmanlı bir düzen kurun.

    Birinci katman yerel denetim: commit-msg kancasıyla format uymayan mesajı daha commit oluşmadan reddedin. Betiği ve kurulumunu Git hooks ile otomasyon yazısında bulabilirsiniz; kısa hâli şöyle:

    #!/usr/bin/env bash
    # .githooks/commit-msg
    PATTERN='^(feat|fix|perf|refactor|docs|test|build|ci|chore|style)(\([a-z0-9._-]+\))?!?: .{1,72}$'
    head -n1 "$1" | grep -qE "$PATTERN" || {
      echo "Mesaj formatı: tip(kapsam): açıklama"
      exit 1
    }
    

    İkinci katman CI denetimi: kanca --no-verify ile atlanabildiği için, pull request üzerindeki commit'leri boru hattında tekrar denetleyin. Basit bir kabuk döngüsü yeterlidir:

    # CI adımı: PR içindeki tüm commit başlıklarını denetle
    git log --format='%s' origin/main..HEAD | while read -r line; do
      echo "$line" | grep -qE "$PATTERN" || { echo "Hatalı: $line"; exit 1; }
    done
    

    Üçüncü katman birleştirme politikası: pull request'i squash ile birleştiriyorsanız, oluşan tek commit'in başlığı PR başlığından gelir. Bu durumda PR başlığını denetlemek, tek tek commit'leri denetlemekten daha etkilidir; geliştiriciler dal içinde serbestçe çalışır, ana dala yalnızca standarda uygun tek bir commit girer. Çoğu ekip için en pratik model budur.

    Bir de kültürel ayrıntı: tip listesini ve kapsam sözlüğünü depo köküne kısa bir CONTRIBUTING.md olarak yazın. Üç ekran uzunluğunda bir belge kimsenin okumadığı bir belgedir; yarım sayfa yeterlidir.

    Sık Yapılan Hatalar ve Tuzaklar#

    Her şeyi chore yapmak. Tarihçenin yarısı chore: güncelleme ise standardı uygulamış olmazsınız, sadece kelimeyi değiştirmiş olursunuz. Tip seçerken bir saniye düşünmek, aracın üreteceği CHANGELOG'un işe yarar olup olmamasını belirler.

    Uyumu bozan değişikliği işaretlememek. En pahalı hata budur. ! koymayı unuttuğunuz bir commit, MAJOR yerine MINOR sürüm çıkmasına yol açar; ^1.0.0 yazan kullanıcılar güncellemeyi otomatik alır ve sistemleri bozulur. Uyum kırıcı değişiklikte hem ! koyun hem BREAKING CHANGE: altbilgisi yazın hem de neyin nasıl taşınacağını anlatın.

    Tek commit'te iki iş yapmak. "feat ve fix birlikte" diye bir tip yoktur; çünkü atomik olmayan commit zaten sorunludur. Bir commit tek bir mantıksal değişiklik içermelidir. Bu disiplin, geri alma (git revert) ve hata ayıklama (git bisect) işlemlerini de mümkün kılar.

    Sürüm numarasını pazarlama aracı sanmak. Semver'da MAJOR artışı "büyük yenilik" demek değildir, "uyumu bozdum" demektir. Küçük ama kırıcı bir değişiklik MAJOR gerektirir; devasa ama uyumlu bir özellik yalnızca MINOR'dur.

    Etiket ile paket sürümünü ayrı bırakmak. package.json, composer.json veya benzeri dosyadaki sürüm ile Git etiketinin farklı olması, hangi kodun yayında olduğunu izlemeyi imkânsız hâle getirir. Yayın adımında ikisini birlikte güncelleyin.

    BelirtiKök nedenÇözüm
    CHANGELOG boş çıkıyorMesajlar standart dışıcommit-msg kancası ekleyin
    Yanlış sürüm atlandı! işareti unutulduPR şablonuna kontrol maddesi koyun
    Kullanıcı güncellemede bozulduMAJOR yerine MINOR çıkıldıKırıcı değişikliği CI'da denetleyin
    git describe çalışmıyorHafif etiket atılmışgit tag -a kullanın

    Sıkça Sorulan Sorular#

    Conventional Commits kullanmak zorunlu mu#

    Hayır, hiçbir teknik zorunluluk yok; Git her mesajı kabul eder. Ancak otomatik sürümleme, otomatik CHANGELOG ve tarihçede arama yapabilme gibi kazanımların hepsi mesajın makine tarafından okunabilir olmasına bağlıdır. Tek kişilik küçük bir projede getirisi sınırlı kalabilir; iki kişiden fazla geliştiricinin olduğu ve dışarıya sürüm yayınlanan her projede maliyetinden çok daha fazlasını geri verir.

    Commit mesajını Türkçe mi İngilizce mi yazmalıyım#

    Belirleyici olan projenin kimin okuyacağıdır. Açık kaynak ya da uluslararası bir ekip söz konusuysa İngilizce standarttır. Tamamen yerel bir ekipte Türkçe açıklama gayet uygundur; ancak tip kelimeleri (feat, fix, docs) standardın parçası olduğu için İngilizce kalır ve araçlar bunlara bakar. Asıl önemli olan tutarlılıktır: aynı depoda iki dili karıştırmayın.

    Yanlış formatta yazdığım commit mesajını nasıl düzeltirim#

    En son commit için git commit --amend yeterlidir; editör açılır, mesajı düzeltip kaydedersiniz. Daha eski commit'ler için git rebase -i HEAD~5 ile interaktif rebase açıp ilgili satırı reword olarak işaretleyebilirsiniz. Her iki durumda da commit kimlikleri değişir, dolayısıyla paylaşılan bir dalda bunu yapıyorsanız ekiple konuşun ve git push --force-with-lease kullanın.

    0.x sürümündeki bir kütüphaneyi üretimde kullanmalı mıyım#

    Kullanabilirsiniz ama sürümü tam olarak sabitleyerek. Semver kurallarına göre 1.0.0 öncesinde her MINOR artışı uyumu bozabilir, yani ^0.5.2 yazmak sizi 0.6.0 ile gelen kırıcı bir değişikliğe açık bırakır. Bu tür bağımlılıkları 0.5.2 şeklinde tam sürümle sabitleyin ve güncellemeyi elle, sürüm notlarını okuyarak yapın.

    Squash merge kullanırken commit standardı işe yarar mı#

    Evet, hatta işi kolaylaştırır. Squash merge'de dal içindeki tüm commit'ler tek bir commit'e indirgenir ve bu commit'in başlığı genellikle pull request başlığından gelir. Bu durumda tek tek commit'leri değil PR başlığını standarda uygun tutmak yeterli olur; geliştiriciler kendi dallarında serbestçe çalışır, ana dalın tarihçesi ise tertemiz ve makine tarafından okunabilir kalır.

    Semantic versioning ile takvim tabanlı sürümleme arasında nasıl seçim yaparım#

    Semver, başkalarının bağımlılık olarak kullandığı kütüphaneler ve API'ler için doğru tercihtir; çünkü kullanıcıya uyumluluk sözü verir. Takvim tabanlı sürümleme (2026.08 gibi) ise doğrudan son kullanıcıya sunulan, kimsenin kod düzeyinde bağımlı olmadığı uygulamalarda daha anlamlıdır; orada asıl bilgi "ne kadar güncel" olmasıdır. Bir SaaS panelinde takvim, bir SDK'da semver kullanmak yaygın ve makul bir ayrımdır.

    BREAKING CHANGE nasıl duyurulur#

    Yalnızca işaretlemek yetmez, taşıma yolunu da yazmak gerekir. Commit altbilgisinde neyin kaldırıldığını ve yerine ne kullanılacağını açıkça belirtin, CHANGELOG'da ayrı bir başlık altında toplayın ve mümkünse eski davranışı bir sürüm boyunca kullanımdan kaldırma uyarısıyla ayakta tutun. Kullanıcıya "bir sürüm önceden haber verildi" hissi vermek, MAJOR geçişlerinin en az sancılı yoludur.

    Kapanış#

    Conventional Commits ile semantic versioning aynı madalyonun iki yüzü: biri bilgiyi commit anında yakalar, diğeri o bilgiyi sürüm numarasına dönüştürür. Aklınızda tutmanız gereken dört alışkanlık şunlar: her commit tek bir mantıksal işi yapsın; tipini bilinçli seçin ve chore çöp kutusuna dönmesin; uyumu bozan her değişikliği hem ! hem BREAKING CHANGE ile işaretleyin; ve kuralı insan hafızasına değil, commit-msg kancasıyla CI denetimine emanet edin. Bunlar oturduğunda yayın notu yazmak bir işten çıkıp otomatik bir çıktıya dönüşür.

    Bu akışı barındıran depoyu ve dağıtım hattını kendi altyapınızda çalıştırmak isterseniz tam root erişimli VDS ve sanal sunucu paketlerimiz uygun bir başlangıçtır; kurulum, güncelleme ve izleme yükünü bize bırakmak isterseniz sunucu yönetimi hizmetimiz bu işi üstlenir. Yayın öncesi yedek almayı süreçlerinize eklemek için yedekleme sayfamıza, diğer sürüm yönetimi rehberleri için wiki bölümüne göz atabilirsiniz.

    GitSürüm YönetimiDevOps

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.