Mert Özen Her Satırda Daha İleri
Yazılım Tasarımı Coding Conventions ve Ekip Standartları — #05

Kuralları Otomatikleştirmek: Pre-commit Hook, CI Kapısı, Legacy Kod Tabanına Kademeli Geçiş ve .git-blame-ignore-revs

Mert Özen 14 Kas 2026 15 dk 3 görüntülenme
Kuralları Otomatikleştirmek: Pre-commit Hook, CI Kapısı, Legacy Kod Tabanına Kademeli Geçiş ve .git-blame-ignore-revs

Pre-commit hook hızlı geri bildirim verir ama --no-verify ile atlanır; convention'ı main'e girmeden durduran tek yer zorunlu bir CI kontrolüdür. Yazı Husky.Net ve lint-staged ile yalnız staged dosyaları formatlamayı, düzeltme komutunu söyleyen CI adımlarını, legacy kodda dokunulan dosyalar ile big bang arasındaki seçimi, .git-blame-ignore-revs'i, açık PR'ları çatışmasız güncellemeyi ve suppression politikasını gerçek çıktılarla anlatıyor.

Sürüm notu: Bu yazıdaki kodlar .NET 10 (SDK 10.0.402, CSharpier 1.4.0, Husky.Net 0.9.1) ve Angular 22.2 (Husky 9.1.7, lint-staged 17.6.0, Prettier 3.10.1, ESLint 10.15) ile Git 2.54 üzerinde denenmiştir; GitHub Actions workflow'u actionlint 1.7.12 ile doğrulanmış, adımları yerelde aynı komutlarla çalıştırılmıştır.

Önce Tahmin Et: Hook Varken CI'a Gerek Var mı?

Bookstore repo'sunda artık bir pre-commit hook var: her commit'te staged C# dosyaları CSharpier'dan, TypeScript ve HTML (HyperText Markup Language) dosyaları ESLint ve Prettier'dan geçiyor. Hook kurulduktan sonraki ilk toplantıda biri soruyor: "Commit zaten formatlanmış geliyor, CI'daki format kontrolünü kaldırsak build iki dakika kısalır."

Aynı hafta Grace acil bir düzeltme için şu commit'i atıyor; hook çalışırken beklemek istemediği için --no-verify ile:

$ git commit -qam "Extract threshold" --no-verify
$ git log --oneline | head -1
2323705 Extract threshold

Değişikliğin içinde bir var ve isExpress?29m:0m gibi sıkışık bir satır var. Bu kod main'e girer mi? Girmeyecekse onu hangi katman, hangi mesajla durdurur? Ve aynı CI kontrolü, ekibin bir yıl önce yazdığı ve kimsenin dokunmadığı OrderService için ne der?

Yerel Katman: Yalnız Değişen Dosyaları Formatlamak

Hook'un amacı hızlı geri bildirim: sorunu commit anında, yalnız değiştirdiğin dosyalarda ve otomatik düzelterek göstermek. Bütün repo'yu formatlamak bu katmanın işi değil; birkaç saniyeyi geçen bir hook'u insanlar atlamaya başlar. Hook'ların Git tarafındaki temelini Git & GitHub serisinin, commit'leri SSH anahtarıyla imzalamayı da anlatan "Git Hook'ları ve Commit İmzalama: pre-commit'ten SSH Signature'ına" yazısında, Git 2.54 ile gelen config tabanlı hook'ları da "Git 2.50–2.56: git history, Config Tabanlı Hook'lar ve git add --resolved" yazısında anlattık. Burada .NET ve Angular'ı aynı repo'da tutan Bookstore için bir hook yöneticisi seçiyoruz.

Bookstore'da Bookstore.slnx kökte, Angular uygulaması web/ klasöründe. Hook yöneticisi olarak Husky.Net'i seçtik, çünkü CSharpier'ın durduğu aynı dotnet-tools.json manifest'iyle geliyor ve npm komutlarını da çalıştırabiliyor. Kurulum, repo kökünde:

$ dotnet tool install husky --version 0.9.1
$ dotnet husky install
Git hooks installed
$ git config --get core.hooksPath
.husky
$ dotnet husky add pre-commit -c "dotnet husky run --group pre-commit"
created .husky/pre-commit

Görevler .husky/task-runner.json'da. ${staged} değişkeni staged dosyaların listesine açılıyor; include görevi yalnız ilgili dosyalar staged ise çalıştırıyor:

{
  "$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
  "tasks": [
    {
      "name": "csharpier",
      "group": "pre-commit",
      "command": "dotnet",
      "args": ["csharpier", "format", "${staged}"],
      "include": ["**/*.cs", "**/*.csproj", "**/*.props"]
    },
    {
      "name": "web-lint-staged",
      "group": "pre-commit",
      "command": "npx",
      "args": ["lint-staged"],
      "cwd": "web",
      "include": ["web/**/*"]
    }
  ]
}

İkinci görev Angular tarafını lint-staged'e devrediyor. web/.lintstagedrc.json hangi glob'a hangi komutun uygulanacağını söylüyor; TypeScript dosyaları önce eslint --fix'ten, sonra Prettier'dan geçiyor:

{
  "*.ts": ["eslint --fix", "prettier --write"],
  "*.{html,css,scss,json}": "prettier --write"
}

Biri bilerek sıkıştırılmış bir C# dosyası ve bir TypeScript dosyası commit'lediğinde hook'un çıktısı (ayırıcı çizgileri çıkardık):

$ git commit -m "Add shipping rules"
[Husky] 🚀 Loading tasks ...
[Husky] ⚡ Preparing task 'csharpier'
[Husky] ⌛ Executing task 'csharpier' ...
Formatted 1 files in 119ms.
[Husky]  ✔ Successfully executed in 248ms
[Husky] ⚡ Preparing task 'web-lint-staged'
[Husky] ⌛ Executing task 'web-lint-staged' ...
⋯ Running tasks for staged files…
    *.ts — 1 file
      ⋯ eslint --fix
      ⋯ prettier --write
✔ eslint --fix
✔ prettier --write
✔ Done running tasks for staged files!
⋯ Staging changes from tasks…
✔ Done staging changes from tasks!
[Husky]  ✔ Successfully executed in 1,440ms
[main 313ac7d] Add shipping rules
 2 files changed, 12 insertions(+)

Commit'e giren ShippingCalculator.cs formatlanmış hâli; working tree temiz kaldı, yani formatlanan dosya yeniden stage edildi. Bir de kısmi stage'i denedik: dosyada iki değişiklik yaptık, yalnız birini git add ile stage ettik, diğerine WIP (work in progress: yarım iş) olduğunu söyleyen "WIP: not approved yet" yorumu koyup dışarıda bıraktık. Commit'e yalnız staged satır formatlanmış olarak girdi, WIP satırı working tree'de kaldı. Hook'un yarım kalmış işini commit'e sızdırmaması bu katmanın en kritik özelliği; kendi kurulumunda bunu mutlaka dene.

Repo yalnız Angular'dan oluşuyorsa Husky.Net'e gerek yok; npm tarafındaki Husky aynı işi yapar. npx husky init package.json'a "prepare": "husky" script'ini ve içinde npm test yazan bir .husky/pre-commit ekliyor; o satırı npx lint-staged yapınca, export const maxCartItems=20 satırı commit'te export const maxCartItems = 20; olarak kaydedildi.

Hook Bir Kapı Değildir

Önce tahminin ilk yarısı: hook Grace'in commit'ini durdurmadı, çünkü --no-verify hook'u hiç çalıştırmaz. Hook'un atlanabildiği tek yol bu değil. Hook'lar git clone ile gelmez; her geliştiricinin bir kez dotnet tool restore ve dotnet husky install çalıştırması gerekir (npm Husky'de bunu prepare script'i npm install sırasında yapar). GitHub'ın web arayüzünden yapılan bir düzenleme, bir rebase ya da başka bir araçla atılan commit de hook'tan geçmeyebilir. CSharpier'ın dokümanı CI'da Husky.Net'i HUSKY=0 ortam değişkeniyle kapatmayı öneriyor; Bookstore'un workflow'u da bunu yapıyor.

Sonuç: hook bir kolaylık, bir kapı değil. Kural ihlalinin main'e girmesini engelleyen tek yer, atlanamayan bir CI kontrolü ve onu zorunlu tutan branch kuralı. Hook'u kaldırmak geliştiriciye geç geri bildirim demek, CI kontrolünü kaldırmak ise kuralı fiilen kaldırmak demek.

CI Katmanı: Kontrol Et, Düzeltme

Bookstore'un convention workflow'u iki job'dan oluşuyor. Her adım aracı kontrol modunda çalıştırıyor ve başarısız olduğunda geliştiriciye neyi çalıştırması gerektiğini söyleyen bir hata satırı yazıyor. GitHub Actions'ta ::error title=…:: ile başlayan satırlar PR ekranında ve job özetinde annotation olarak görünür:

name: conventions

on:
  pull_request:
  push:
    branches: [main]

jobs:
  dotnet:
    runs-on: ubuntu-latest
    env:
      HUSKY: 0
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-dotnet@v6
        with:
          global-json-file: global.json
      - run: dotnet tool restore
      - name: Formatting (CSharpier)
        run: |
          dotnet csharpier check . || {
            echo "::error title=Formatting::Run 'dotnet csharpier format .' and commit the result."
            exit 1
          }
      - name: Code style (IDE rules)
        run: |
          dotnet format style Bookstore.slnx --verify-no-changes || {
            echo "::error title=Code style::Run 'dotnet format style Bookstore.slnx' and commit the result."
            exit 1
          }
      - name: Build with style rules
        run: dotnet build Bookstore.slnx -warnaserror

  web:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: web
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
      - run: npm ci
      - name: Formatting (Prettier)
        run: |
          npx prettier --check . || {
            echo "::error title=Formatting::Run 'npx prettier --write .' in web/ and commit the result."
            exit 1
          }
      - name: Lint (ESLint)
        run: npx ng lint

setup-dotnet, global.json ve setup-node'un ayrıntıları GitHub Actions serisinin "GitHub Actions ile .NET CI: setup-dotnet, global.json, NuGet Cache, Test Raporu ve Coverage" ve "GitHub Actions ile Angular CI: setup-node, npm ci, Lint, Vitest, Build Budget'ları ve Playwright" yazılarında; burada yalnız convention adımları var. Grace'in --no-verify commit'inde dotnet job'unun ilk adımını yerelde aynı komutla çalıştırdık:

$ dotnet csharpier check .
Error ./src/Bookstore.Ordering/ShippingCalculator.cs - Was not formatted.
  ----------------------------- Expected: Around Line 9 -----------------------------
          if (subtotal >= freeShippingThreshold)
              return isExpress ? 29m : 0m;
          return isExpress ? 79m + bookCount * 5m : 39m;
  ----------------------------- Actual: Around Line 9 -----------------------------
          if (subtotal >= freeShippingThreshold)
              return isExpress?29m:0m;
          return isExpress ? 79m + bookCount * 5m : 39m;

Checked 5 files in 125ms.
::error title=Formatting::Run 'dotnet csharpier format .' and commit the result.

Tahminin ikinci yarısı bu: kod main'e girmez, CI kırmızı olur ve mesaj düzeltme komutunu söyler. Bunun bir kapı olması için workflow'un sonucunun merge için zorunlu olması gerekir; GitHub'da bu, rulesets ya da branch protection'da "required status checks" ayarıyla yapılır (Git & GitHub serisinin "Git ile Ekip Çalışması: Branch Stratejileri, Rulesets ve Conventional Commits" yazısında rulesets'i ve CODEOWNERS'ı anlattık).

Workflow'un bilinçli olarak yapmadığı bir şey var: kodu düzeltip commit'lemiyor. CI'ın formatter'ı yazma modunda çalıştırıp bot adına commit atması cazip görünür ama üç sorunu var. Geliştiricinin yerel branch'i sessizce geride kalır ve bir sonraki push'ta çatışma çıkar. Review edilen kod ile merge edilen kod farklılaşır. Ve CI'a repo'ya yazma yetkisi vermek gerekir; bu da güvenlik tarafında gereksiz bir kapı açar. CI raporlar, geliştirici düzeltir.

Dört katman bir arada: soldan sağa geri bildirim yavaşlıyor ama kapı sertleşiyor. Biçim ve stil kuralları ilk üç katmanda yakalanır; review'a yalnız aracı olmayan kararlar kalır.

05-enforcement-layers-tr.png

Legacy Kod Tabanına Geçiş: Dokunulan Dosyalar mı, Big Bang mi?

Aynı CI adımı ilk kez açıldığında yalnız yeni koda bakmadı. dotnet format style'ın çıktısı Grace'in tek var'ının yanında, bir yıl önce yazılmış OrderService'ten altı uyarı daha getirdi:

$ dotnet format style Bookstore.slnx --verify-no-changes
src/Bookstore.Ordering/OrderService.cs(1,1): warning IDE0161: Convert to file-scoped namespace
src/Bookstore.Ordering/OrderService.cs(20,13): warning IDE0008: Use explicit type instead of 'var'
src/Bookstore.Ordering/OrderService.cs(21,13): warning IDE0008: Use explicit type instead of 'var'
src/Bookstore.Ordering/OrderService.cs(28,13): warning IDE0008: Use explicit type instead of 'var'
src/Bookstore.Ordering/OrderService.cs(15,13): warning IDE0003: Name can be simplified
src/Bookstore.Ordering/OrderService.cs(22,40): warning IDE0003: Name can be simplified
src/Bookstore.Ordering/ShippingCalculator.cs(7,9): warning IDE0008: Use explicit type instead of 'var'

Gerçek bir legacy kod tabanında bu liste binlerce satır olur ve iki yol var. Birincisi, kontrolü yalnız PR'ın değiştirdiği dosyalara uygulamak. dotnet format ve CSharpier dosya listesi kabul ediyor:

$ CHANGED=$(git diff --name-only --diff-filter=ACMR HEAD~1 HEAD -- '*.cs')
$ dotnet format style Bookstore.slnx --verify-no-changes --include $CHANGED
src/Bookstore.Ordering/ShippingCalculator.cs(7,9): warning IDE0008: Use explicit type instead of 'var'
$ dotnet csharpier check $CHANGED
Error ./src/Bookstore.Ordering/ShippingCalculator.cs - Was not formatted.
...
Checked 1 files in 126ms.

CI'da HEAD~1 yerine PR'ın hedef branch'i kullanılır (checkout'ta yeterli geçmiş çekilerek). Bu yol Robert C. Martin'in Clean Code'da andığı "boy scout rule"un (kamp yerini bulduğundan temiz bırak) otomatik hâli: dokunduğun dosyayı kurala uydurursun. Bedeli, bir dosyaya küçük bir değişiklik için girenin dosyanın bütün eski ihlallerini de düzeltmek zorunda kalması; bir satırlık bug fix'in diff'i yine büyür. Ayrıca kural kod tabanının tamamında hiçbir zaman sağlanmayabilir.

İkinci yol big bang: bütün repo'yu tek seferde, tek bir commit'le kurala uydurmak. Bookstore'da bunu seçtik: dotnet format style ve dotnet csharpier format . çalıştırıp sonucu ayrı bir commit olarak attık. Bu commit'te davranış değişikliği yok, başka bir değişiklik de yok. Avantajı, ertesi günden itibaren CI'ın bütün repo'yu kontrol edebilmesi ve kimsenin "eski ihlalleri" taşımaması. Bedeli ise iki yerde ödeniyor: git blame ve açık PR'lar.

.git-blame-ignore-revs ve Açık PR'lar

Big bang commit'inden sonra OrderService'in git blame'i her satırın sahibi olarak formatlama commit'ini gösteriyor:

$ git blame src/Bookstore.Ordering/OrderService.cs
aeadc118 (Linus 2026-11-14 01:54:38 +0300 18)     public decimal CalculateTotal()
aeadc118 (Linus 2026-11-14 01:54:38 +0300 19)     {
aeadc118 (Linus 2026-11-14 01:54:38 +0300 20)         decimal subtotal = 0m;
aeadc118 (Linus 2026-11-14 01:54:38 +0300 21)         int bookCount = 0;
aeadc118 (Linus 2026-11-14 01:54:38 +0300 22)         foreach (OrderItem item in _items)

"Bu satırı kim, neden yazdı?" sorusunun cevabı artık "Linus formatladı". Çözüm repo köküne bir .git-blame-ignore-revs dosyası. GitHub Docs'a göre dosyada her satıra bir tam commit hash'i yazılır, üstüne açıklama yorumu konabilir ve GitHub'ın blame görünümü bu dosyayı repo kökünde bulursa listelenen commit'leri otomatik olarak atlar:

# style: apply conventions to the whole repository (CSharpier + dotnet format style)
aeadc1185b799d60a34a35811e2ba13b47da7bb6

Yerel Git dosyayı kendiliğinden okumaz; ya komutta verilir ya da bir kez config'e yazılır:

$ git blame --ignore-revs-file .git-blame-ignore-revs src/Bookstore.Ordering/OrderService.cs
^3b79c97 (Ada   2026-11-14 01:53:00 +0300 18)     public decimal CalculateTotal()
aeadc118 (Linus 2026-11-14 01:54:38 +0300 19)     {
^3b79c97 (Ada   2026-11-14 01:53:00 +0300 20)         decimal subtotal = 0m;
^3b79c97 (Ada   2026-11-14 01:53:00 +0300 21)         int bookCount = 0;
^3b79c97 (Ada   2026-11-14 01:53:00 +0300 22)         foreach (OrderItem item in _items)
$ git config blame.ignoreRevsFile .git-blame-ignore-revs

Satırların çoğu Ada'nın orijinal commit'ine döndü; decimal subtotal satırı bile, oysa orijinalinde var yazıyordu. Ama 19. satırdaki { hâlâ Linus'ta. Git atlanan commit'teki her satırı öncekiyle eşleyemiyor; eski dosyada block-scoped namespace yüzünden girintisi ve yeri değişen satırlar eşleşmeyebilir. Bunu görünür yapmak için iki ayar var: blame.markIgnoredLines atlanan commit'ten geçerek bulunan satırları ? ile, blame.markUnblamableLines eşlenemeyen satırları * ile işaretliyor:

$ git -c blame.markUnblamableLines=true -c blame.markIgnoredLines=true blame src/Bookstore.Ordering/OrderService.cs
^?3b79c9 (Ada   2026-11-14 01:53:00 +0300 18)     public decimal CalculateTotal()
*aeadc11 (Linus 2026-11-14 01:54:38 +0300 19)     {
^?3b79c9 (Ada   2026-11-14 01:53:00 +0300 20)         decimal subtotal = 0m;

İkinci bedel açık PR'lar. Big bang'den önce açılmış, BulkDiscountRate'i değiştiren bir branch'i main'e rebase etmeye çalıştık:

$ git rebase main
Could not apply efed0e8... # Raise bulk discount to 15%
$ git status --short
UU src/Bookstore.Ordering/OrderService.cs

Tek satırlık iş değişikliği, bütün dosyayı değiştiren formatlama commit'iyle çatıştı. İşe yarayan yol, branch'i önce aynı araçlarla formatlamak: big bang commit'inden hemen önceki main'e dayalı branch'te aynı configuration'la dotnet format style ve dotnet csharpier format . çalıştırıp commit'ledik, sonra formatlama commit'ini değişiklik almadan "merge edilmiş" saydık ve main'i normal biçimde merge ettik:

$ dotnet format style Bookstore.slnx
$ dotnet csharpier format .
$ git commit -qam "style: apply conventions (same tools as aeadc11)"
$ git merge -q -s ours aeadc11 -m "Merge formatting commit aeadc11 (already applied)"
$ git merge main -m "Merge main"
...
$ git diff main -- src/Bookstore.Ordering/OrderService.cs | grep '^[-+] '
-    private const decimal BulkDiscountRate = 0.10m;
+    private const decimal BulkDiscountRate = 0.15m;

Çatışma çıkmadı ve main'e göre fark yalnız iş değişikliği. -s ours stratejisi diğer tarafın içeriğini hiç almaz; bu yüzden yalnız iki tarafın aynı araç sürümü ve aynı configuration'la formatlandığından eminsen kullan ve sonunda git diff ile doğrula. Zamanlama da önemli: big bang'i ekibe önceden duyur, mümkünse açık PR sayısının en az olduğu ana (bir release'in hemen sonrası gibi) denk getir ve yukarıdaki adımları herkesin uygulayabileceği bir script olarak hazırla.

Suppression Politikası

Her kuralın meşru istisnaları olur ve bunları susturmanın iki yolu var. [SuppressMessage] attribute'u tek bir symbol'e bağlanır ve bir Justification taşır; #pragma warning disable ise satır aralığına uygulanır ve gerekçe taşımaz. Bookstore'da reflection ile adından okunan bir field için:

using System.Diagnostics.CodeAnalysis;

namespace Bookstore.Ordering;

public sealed class LegacyInvoiceSnapshot
{
    [SuppressMessage(
        "Style",
        "IDE1006:Naming Styles",
        Justification = "Read by name via reflection in the legacy invoice exporter (INV-142)."
    )]
    private readonly string invoiceNumber;

#pragma warning disable IDE0008
    private readonly decimal _total;
#pragma warning restore IDE0008

    public LegacyInvoiceSnapshot(string number, decimal total)
    {
        invoiceNumber = number;
        _total = total;
    }

    public override string ToString() => $"{invoiceNumber}: {_total}";
}

_camelCase kuralı açıkken build bu dosya için IDE1006 vermedi; attribute işini yaptı. #pragma bloğu ise gereksiz: içindeki satırda var yok, IDE0008'in susturacağı bir şey yok. Bunu IDE0079 (gereksiz suppression) yakalamalı, ama ikinci yazıda gördüğümüz gibi IDE0079 build'de hiç çalışmayan kurallardan. Denedik: dotnet_diagnostic.IDE0079.severity = warning ile build sıfır uyarı verdi, dotnet format style --diagnostics IDE0079 da bir şey raporlamadı. Gereksiz suppression'ları bugün yalnız IDE ve review bulur.

Bookstore'un politikası bu yüzden kısa: suppression yalnız [SuppressMessage] ile ve Justification'da bir gerekçe ve iş kaydı numarasıyla; #pragma yalnız bir symbol'e bağlanamayan durumlarda (üretilmiş bir bloğun ortası gibi); dosya ya da klasör genelinde istisna gerekiyorsa .editorconfig'te o klasör için severity none ve bunun gerekçesi convention dokümanında. .editorconfig, eslint.config.js ve .prettierrc değişiklikleri CODEOWNERS ile tech lead'in onayına bağlı; bu dosyaları değiştirmek bir kuralı değiştirmek demek.

AI Coding Agent'lar ve Convention'lar

Bookstore ekibi kod yazarken AI coding agent'lar da kullanıyor ve agent'lar repo'daki talimat dosyalarını (AGENTS.md ve benzerleri) okuyor. Akla gelen ilk fikir convention dokümanının tamamını bu dosyaya kopyalamak. Bunu yapma. Kurallar zaten araçlarda; dokümana ikinci bir kopya koymak, ilk yazıdaki "wiki'de kalan convention" sorununu agent için yeniden üretir. İki kopya bir gün ayrışır ve agent yanlış olanı izler. Agent'a gereken tek şey kuralları uygulayan komutlar:

## Conventions

Formatting, style and naming are enforced by tools. Before you finish a task, run:

- dotnet csharpier format .
- dotnet format style Bookstore.slnx
- dotnet build Bookstore.slnx -warnaserror
- in web/: npx prettier --write . and npx ng lint

Do not edit .editorconfig, eslint.config.js or .prettierrc to make a check pass.

Son satır önemli: kontrolü geçmenin en kısa yolu kuralı gevşetmek ve bir agent bunu "çözüm" olarak sunabilir. Pre-commit hook ve CI agent'ın yazdığı kod için de aynı şekilde çalışır; kapı aynı kalır. Agent'lara context vermenin ayrıntısı ilerideki AI Destekli Geliştirme serisinin konusu.

Sık Düşülen Tuzaklar

CI'da formatter'ın düzeltip commit'lemesi. Sessiz değişiklik, yerel branch'lerle çatışma ve CI'a yazma yetkisi demek. CI kontrol modunda çalışır, hata mesajı düzeltme komutunu söyler.

Hook'u kapı sanmak. --no-verify, kurulmamış hook'lar ve web arayüzünden yapılan düzenlemeler hook'u atlar. Zorunlu CI kontrolü olmadan kural yoktur.

Yavaş hook. Bütün çözümü build eden ya da test koşan bir pre-commit hook'u birkaç gün içinde herkesin --no-verify alışkanlığına dönüşür. Hook'ta yalnız staged dosyalar ve yalnız hızlı araçlar; build, test ve tam dotnet format CI'da.

Big bang commit'ini kötü zamanlamak. Yirmi açık PR varken yapılan bir formatlama commit'i yirmi çatışma demek. Duyur, sakin bir ana denk getir, açık branch'ler için format-sonra-merge script'ini hazırla.

.git-blame-ignore-revs'i unutmak ya da yanlış hash yazmak. Dosyaya kısa hash değil tam hash yazılır ve yalnız saf formatlama commit'leri girer; içinde davranış değişikliği olan bir commit'i blame'den saklamak, o değişikliğin izini de saklar.

Toplu #pragma ile susturmak. Bir dosyanın başına #pragma warning disable koyup sonuna restore koymamak kuralı o dosya için kalıcı olarak kapatır. Suppression symbol'e bağlı ve gerekçeli olmalı.

Küçük Bir Deneme

★☆☆ Kendi repo'nda staged dosyaları formatlayan bir pre-commit hook kur (Husky.Net, Husky ya da Git'in config tabanlı hook'ları). Bir dosyada iki değişiklik yapıp yalnız birini stage et ve commit'e yalnız onun girdiğini git show ile doğrula.

★★☆ CI'daki format adımına, başarısız olduğunda düzeltme komutunu söyleyen bir ::error satırı ekle. Sonra bir PR'ı --no-verify ile formatlanmamış bir dosyayla aç ve mesajın PR ekranında nasıl göründüğüne bak.

★★★ Bir deneme branch'inde bütün repo'yu formatlayan bir commit at, hash'ini .git-blame-ignore-revs'e ekle ve blame.markUnblamableLines ile kaç satırın hâlâ formatlama commit'ine bağlı kaldığını say. Big bang'den önce açılmış bir branch'i yukarıdaki format-sonra-merge yöntemiyle güncelle ve git diff main'in yalnız iş değişikliğini gösterdiğini doğrula.

Seri burada bitiyor. İlk yazıda convention'ın değerinin kuralın kendisinden değil tartışılmaz ve otomatik olmasından geldiğini söylemiştik. Beş yazının sonunda Bookstore'da her kuralın bir sahibi var: biçim CSharpier'ın ve Prettier'ın, stil ve naming analyzer'ın ve ESLint'in, kapı CI'ın, anlam ise review'ın. Bir sonraki adım bu düzeni kod kalitesi kurallarına genişletmek; o da ilerideki Statik Analiz serisinin konusu.