GitHub'da Review Süreci: CODEOWNERS, Rulesets, Suggested Changes, Merge Queue, Bot'lar ve Metrikler
GitHub'da review sözleşmesi CODEOWNERS, ruleset ve takım ataması ayarlarıyla platforma taşınır; ama CODEOWNERS'ta bir satırdaki iki takım 'ikisinden biri' demektir, ikinci zorunlu onay ruleset'in required reviewers kuralıyla kurulur. Yazı repo'da tutulan ruleset JSON'unu, suggested changes'i, merge_group tetikleyicisiyle merge queue'yu, labeler ve Dependabot'u, Copilot onaylarının yerini ve gh çıktısından review metriklerini çıkaran bir aracı gösteriyor.
Sürüm notu: Bu yazıdaki kodlar .NET 10 (SDK 10.0.402) ve actionlint 1.7.12 ile denenmiştir. GitHub ayarları (CODEOWNERS, rulesets, review ataması, suggested changes, merge queue, Copilot code review) Kasım 2026 itibarıyla GitHub Docs'tan anlatılmış, bir GitHub repo'sunda çalıştırılmamıştır; workflow dosyaları actionlint ile, ruleset JSON'u GitHub'ın REST (HTTP üzerinden kaynak odaklı API tarzı) API açıklamasındaki şemayla, Dependabot dosyası SchemaStore şemasıyla yerelde doğrulanmıştır.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) ekibi büyüdü: Emre ve Ayşe'nin yanına iki geliştirici daha katıldı ve ekip iki takıma ayrıldı, backend ve frontend. Ayşe kupon pull request'inde (PR) migration'ın bir sorununu yakalamıştı; ekip bundan sonra migration'ların hem backend takımından hem de tech lead'lerden onay almasını istiyor ve CODEOWNERS'a şu satırı yazıyor:
/src/Bookstore.Api/Data/Migrations/ @bookstore/backend @bookstore/leads
Ruleset'te de "Require review from Code Owners" açık. Tahmin et: bir migration PR'ı merge edilebilmek için kaç onaya ihtiyaç duyar? Backend'den bir kişi onaylasa yeter mi, yoksa iki takımdan da birer onay mı gerekir?
CODEOWNERS ile Alan Sahipliği
Cevap bir. GitHub Docs'a göre "code owner review zorunlu" ayarı açıkken, dosyanın sahiplerinden herhangi birinin onayı şartı sağlar. Bir satıra iki takım yazmak "ikisinden biri" demektir, "ikisi de" değil. Ekibin istediği ikinci onay CODEOWNERS ile kurulamaz; birazdan ruleset'teki required reviewers kuralıyla kuracağız. Bookstore'un dosyası:
# Son eşleşen pattern geçerli: genel kurallar üstte, özel kurallar altta.
* @bookstore/leads
/src/Bookstore.Api/ @bookstore/backend
/tests/ @bookstore/backend
/web/bookstore-web/ @bookstore/frontend
# Bir satırdaki sahiplerden herhangi birinin onayı yeter; migration için ikinci onay ruleset'ten gelir.
/src/Bookstore.Api/Data/Migrations/ @bookstore/backend @bookstore/leads
# Review kurallarını ve pipeline'ları değiştiren PR'ları lead'ler onaylar.
/.github/ @bookstore/leads
/eng/rulesets/ @bookstore/leads
Dosyanın yeri, pattern sözdizimi ve "en son eşleşen satır kazanır" kuralı Git & GitHub serisinin "Git ile Ekip Çalışması: Branch Stratejileri, Rulesets ve Conventional Commits" yazısında anlatıldı. Review açısından önemli olan birkaç ayrıntı daha var. GitHub dosyayı PR'ın base branch'inden okur; yani CODEOWNERS'ı değiştiren bir PR, kendi değişikliğiyle değil, mevcut kurallarla review edilir. Sahiplerin repo'ya yazma yetkisi olmalı; yetkisi olmayan bir takım yazılırsa satır sessizce işe yaramaz. Hatalı satırlar atlanır, hataları GitHub arayüzde dosyanın üstünde ve REST API'de listeler; bunu yerelde doğrulayan resmi bir araç yok, bu yüzden dosyayı değiştiren PR'ı açtıktan sonra GitHub'daki uyarılara bakmak gerekiyor. Draft PR'larda sahiplere review isteği gitmez; PR "ready for review" olduğunda gider. Son olarak /.github/ satırı dosyanın kendisini korur: CODEOWNERS'ı değiştirmek lead onayı ister. Takımlar (@bookstore/backend gibi) bir organization gerektirir; kişisel bir hesaba ait repo'da satırlara kullanıcı adları yazılır.
Bir de * satırına dikkat. Hiçbir özel satıra uymayan her dosya, README'den kök klasördeki configuration'a kadar lead'lere gider. Bu satır geniş tutulursa lead'ler önceki yazıda konuştuğumuz darboğaza dönüşür. Bookstore'da kodun tamamı özel satırlarla kapsandığı için *'a yalnız kenarda kalan dosyalar düşüyor.
Rulesets: Onay, Stale Review ve Son Push
Kurallar repo ayarlarında tıklanarak kurulabilir, ama bir JSON dosyası olarak repo'da tutmak onları review edilebilir yapar. GitHub ayarlardaki "Import a ruleset" seçeneğiyle böyle bir dosyayı içe alabiliyor; aynı gövde REST API'nin POST /repos/{owner}/{repo}/rulesets endpoint'ine de gönderilebilir. Bookstore'un main için ruleset'i eng/rulesets/main.json'da:
{
"name": "main",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": { "include": ["~DEFAULT_BRANCH"], "exclude": [] }
},
"bypass_actors": [],
"rules": [
{ "type": "deletion" },
{ "type": "non_fast_forward" },
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": true,
"require_last_push_approval": true,
"required_review_thread_resolution": true,
"allowed_merge_methods": ["squash"],
"required_reviewers": [
{
"file_patterns": ["src/Bookstore.Api/Data/Migrations/*"],
"minimum_approvals": 1,
"reviewer": { "id": 1234567, "type": "Team" }
}
]
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": false,
"required_status_checks": [{ "context": "api" }, { "context": "web" }]
}
},
{
"type": "merge_queue",
"parameters": {
"merge_method": "SQUASH",
"grouping_strategy": "ALLGREEN",
"max_entries_to_build": 5,
"min_entries_to_merge": 1,
"max_entries_to_merge": 5,
"min_entries_to_merge_wait_minutes": 5,
"check_response_timeout_minutes": 30
}
}
]
}
pull_request kuralının parametreleri, ilk yazıdaki review sözleşmesinin platforma çevrilmiş hâli:
| Parametre | Ne yapar | Neden açık |
|---|---|---|
required_approving_review_count: 1 | Merge için bir onay | Sözleşme bir onay diyor; sayıyı artırmak sorumluluğu dağıtır |
dismiss_stale_reviews_on_push | Onaydan sonra yeni commit gelirse onay düşer | Onay, onaylanan koda ait olmalı |
require_code_owner_review | Değişen dosyanın CODEOWNERS sahiplerinden birinin onayı | Alan bilgisi olan biri bakmalı |
require_last_push_approval | Son push'u, onu yapan kişi dışında biri onaylamalı | Onaydan sonra kendi push'unu kendin onaylayamazsın |
required_review_thread_resolution | Açık conversation varken merge edilmez | Bloklayıcı yorumlar sessizce geçilmez |
allowed_merge_methods: squash | Yalnız squash merge | Review turlarındaki fixup commit'leri main'e girmez |
required_reviewers | Belirli dosyalar için belirli bir takımdan en az N onay | Migration'larda lead onayı, CODEOWNERS'tan ayrı olarak |
Required reviewers kuralı 17 Şubat 2026'da genel kullanıma açıldı. Takımı adıyla değil sayısal kimliğiyle ister; dosyadaki 1234567, leads takımının kimliğiyle değiştirilecek bir yer tutucu. GitHub'ın duyurusu bu kuralın CODEOWNERS'ın yerine değil yanına geldiğini söylüyor: CODEOWNERS sahipliği tanımlar, tek tek kişileri de kapsar ve zorunlu olmasa bile review ister; required reviewers ise belirli dosyalar için belirli bir takımın onayını zorunlu kılar. Yalnız takımlarla çalışır, takımın repo'da yazma yetkisi olmalıdır ve takım kavramı olmadığı için kişisel hesaplara ait repo'larda kullanılamaz. Tahmin sorusundaki ikinci onay böylece kuruluyor: migration'a dokunan PR'da CODEOWNERS'tan bir onay, lead takımından bir onay.
Dosyayı yerelde doğrulamak için GitHub'ın rest-api-description repo'sundaki OpenAPI açıklamasından ruleset gövdesinin şemasını alıp kontrol ettik: dosya şemaya uyuyor. required_approving_review_count'u 11 yapıp merge_method'u küçük harfle yazınca iki kural da şemadan geçmiyor; birincinin üst sınırı 10, ikincisi yalnız büyük harfli değerleri kabul ediyor. JSON'daki küçük bir yazım hatası import sırasında fark edilir; ama dosya repo'da durduğu için review'da da görülebilir.
Review İsteği ve Atama
CODEOWNERS bir takımı review'a eklediğinde isteğin takımda kime düşeceği takımın ayarlarıyla belirlenir. GitHub'da takımlar için otomatik atama açılabiliyor: takım review'a eklendiğinde istek kaldırılır ve yerine belirlenen sayıda kişi atanır. İki algoritma var. Round robin, en uzun süredir istek almamış kişiyi seçer ve kişilerin elinde kaç review beklediğine bakmaz. Load balance ise son istekleri ve bekleyen review'ları hesaba katar ve 30 günlük bir dönemde herkese eşit sayıda review düşmesini hedefler. "Busy" durumundaki kişiler seçilmez; belirli kişiler hiç atanmayacak şekilde dışarıda bırakılabilir.
Bu ayar önceki yazıdaki review yorgunluğuna doğrudan bir cevap. Review'ların yarısı aynı senior'a gidiyorsa load balance yükü yayar, junior'ları da review'a sokar. Bir uyarı: code owner review zorunluysa takımın kendi isteği, atanan kişi review'unu bitirene kadar PR'da kalır; bu, onayın takımdan gelmesi şartının korunması için.
Suggested Changes: Küçük Düzeltmeyi Yorumda Önermek
Bir önceki yazıdaki nitpick'i hatırla: normalized değişkeni normalizedCode olabilir. Böyle bir değişikliği anlatan bir cümle yerine değişikliğin kendisini yorum olarak yazabilirsin. GitHub'ın yorum kutusundaki suggestion düğmesi, seçili satırları özel bir kod bloğu olarak ekler; sen bloğun içini düzenlersin:
nitpick (non-blocking): same naming as normalizedEmail in OrderService.
```suggestion
string normalizedCode = code.Trim().ToUpperInvariant();
Coupon? coupon = await db.Coupons.AsNoTracking().FirstOrDefaultAsync(c => c.Code == normalizedCode, ct);
```
Yazar öneriyi "Commit suggestion" ile tek başına ya da "Add suggestion to batch" ile diğer önerilerle birlikte tek commit olarak uygular. Öneriyi yazan kişi commit'e co-author olarak eklenir. Öneriyi uygulamak için repo'ya yazma yetkisi gerekiyor; fork'tan gelen PR'larda yazarın maintainer düzenlemelerine izin vermesi gerekir.
Suggested changes küçük ve kesin düzeltmeler için iyi: bir isim, bir yazım hatası, eksik bir CancellationToken. Tasarım değişikliği için kötü: yazar öneriyi tek tıkla uygular, ama neden önerildiğini anlamadan. Bir de öneriler birbirinden habersizdir; aynı satıra iki öneri gelirse ikisi birden uygulanamaz.
Merge Queue
Ekip büyüyünce yeni bir sorun çıkıyor. İki PR ayrı ayrı yeşil ama birlikte main'e girince build kırılıyor; her biri diğerinin değişikliğini görmeden test edildi. Bunun klasik çözümü "merge'den önce branch güncel olmalı" kuralı, ama her merge'den sonra bekleyen bütün PR'ların güncellenip CI'ın yeniden çalışması gerekir ve kalabalık bir repo'da bu bir kuyruğa dönüşür. Merge queue bu kuyruğu GitHub'a bırakır.
Dokümana göre işleyişi şöyle (çalıştırılmadı): PR kuyruğa girer, GitHub onu güncel main ve kuyrukta önündeki PR'larla birleştiren geçici bir branch oluşturur ve CI'ı bu birleşik hâl üzerinde çalıştırır. Kontroller geçerse grup main'e merge edilir; bir PR'ın kontrolleri kalırsa o PR kuyruktan çıkarılır ve grup onsuz yeniden kurulur. Ruleset'teki merge_queue parametreleri bu davranışı ayarlar: kaç PR'ın aynı anda CI çalıştıracağı, bir merge'e en az ve en fazla kaç PR gireceği, kontroller için ne kadar bekleneceği. strict_required_status_checks_policy'yi kapalı tutmamızın nedeni bu: güncellik kontrolünü kuyruk zaten birleşik hâl üzerinde yapıyor.
Kuyruğun çalışması için CI workflow'unun kuyruğun ürettiği merge_group event'inde de çalışması gerekir; aksi hâlde zorunlu kontroller hiç raporlanmaz ve merge başarısız olur. Bookstore'un CI'ının tetikleyicileri:
name: ci
on:
pull_request:
merge_group:
push:
branches: [main]
permissions:
contents: read
jobs:
api:
runs-on: ubuntu-latest
# ... SQL Server service container, dotnet build, dotnet test
web:
runs-on: ubuntu-latest
# ... npm ci, ng build, ng test
Tam workflow repo'da, .github/workflows/ci.yml'de; job adları (api, web) ruleset'teki zorunlu kontrol adlarıyla aynı. Workflow'u GitHub'a göndermeden önce actionlint ile kontrol ettik. Temiz dosyada çıktı yok; tetikleyiciyi merge-group diye yanlış yazınca:
$ actionlint .github/workflows/ci.yml
.github/workflows/ci.yml:5:3: unknown Webhook event "merge-group". see https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#webhook-events for list of all Webhook event names [events]
|
5 | merge-group:
| ^~~~~~~~~~~~
$ echo $?
1
Bu hata YAML (yapılandırma dosyalarında kullanılan metin biçimi) olarak geçerli; GitHub dosyayı kabul eder, sadece kuyruk hiç çalışmaz. Workflow sözdizimi ve Actions'ın diğer ayrıntıları GitHub Actions serisinde anlatıldı.
Merge queue, organization'a ait public repo'larda ve GitHub Enterprise Cloud kullanan organization'ların private repo'larında açılabiliyor. İki dört kişilik bir ekip için çoğu zaman gereksiz; günde onlarca PR'ın aynı branch'e merge edildiği yerde değerli. Stacked PR'larla birlikte kullanıldığında stack kuyruğa sırayla girer; bir katman kuyruktan çıkarsa üstündekiler de çıkar.
Otomasyon: Label Bot'ları, Dependency PR'ları ve AI Reviewer'lar
Label'lar reviewer'ın PR listesine bakarken neye ne kadar zaman ayıracağını söyler. Bookstore, actions/labeler ile değişen dosyalara göre label koyuyor:
backend:
- changed-files:
- any-glob-to-any-file: ['src/**', 'tests/**']
frontend:
- changed-files:
- any-glob-to-any-file: 'web/**'
migration:
- changed-files:
- any-glob-to-any-file: 'src/Bookstore.Api/Data/Migrations/**'
ci:
- changed-files:
- any-glob-to-any-file: ['.github/**', 'eng/**']
Labeler'ın workflow'u pull_request_target ile çalışır, çünkü fork'tan gelen PR'lara label yazabilmek için yazma yetkisi ister. Bu tetikleyici workflow'u base branch'in koduyla ve yazma yetkili bir token'la çalıştırır; bu yüzden PR'ın kodunu checkout eden ya da çalıştıran bir adım asla eklenmemeli. Bizim workflow'umuz yalnız labeler'ı çalıştırıyor. migration label'ı, önceki yazıda Ayşe'nin bir saat ayırdığı türden PR'ları listede hemen görünür kılıyor.
Dependency güncellemeleri ayrı bir review akışıdır. Dependabot'un .github/dependabot.yml dosyasında güncellemeleri gruplayıp (bütün @angular/* paketleri tek PR) yeni çıkan sürümler için yedi günlük bir bekleme süresi (cooldown) koyduk; dosya SchemaStore'daki şemadan geçiyor. Bu PR'larda reviewer'ın baktığı şey kod değil: CI yeşil mi, changelog'da kırıcı bir değişiklik var mı, paketin yeni sürümü gerçekten bekleme süresini doldurdu mu. Paket tedarik zinciri riskleri DevSecOps serisinin konusu.
AI reviewer'lar da bu katmanda. GitHub Copilot code review'un davranışı ve sınırları AI Destekli Geliştirme serisinin "AI'ın Yazdığı Kodu Review Etmek ve Doğrulamak: Tipik Hatalar, Checklist ve AI Reviewer'lar" yazısında anlatıldı; burada yalnız süreçteki yerine bakıyoruz. Ruleset'lerde copilot_code_review adlı bir kural var; yeni PR'lar için Copilot review'unu otomatik ister, isteğe bağlı olarak her push'ta ve draft PR'larda da çalışır. 28 Ekim 2026'dan beri Copilot'un onayları genel kullanımda: varsayılan olarak kapalı, enterprise, organization ya da repo yöneticisi açarsa Copilot'un onaylayan review'u zorunlu onay sayısına sayılıyor; yöneticiler Copilot'un hangi dosya yollarını onaylayabileceğini sınırlayabiliyor ve onaydan sonra gelen commit onayı düşürüyor. GitHub'ın kendi dokümanı ise Copilot'un bir PR'daki her sorunu bulmasının garanti olmadığını yazıyor ve geri bildirimin insan review'uyla tamamlanmasını öneriyor. Bookstore'un kararı: Copilot review'u insan review'undan önce çalışan bir ön kontrol olarak kullanmak, onayını zorunlu onaya saymamak. Önceki yazıdaki uyarı burada da geçerli: bot yorumlarına cevap verilmezse insan yorumları kalabalıkta kaybolur.
Review'u Ölçmek
İlk yazıda üç sinyal saymıştık: ilk review'a kadar geçen süre, PR boyutu ve review turu sayısı. Bunlar GitHub CLI'ın JSON çıktısından çıkarılabilir. Bookstore'un eng/review-metrics.cs'i, gh pr list'in çıktısını okuyan küçük bir file-based app:
#:property PublishAot=false
// Kullanım:
// gh pr list --state merged --limit 200 --json number,author,createdAt,mergedAt,additions,deletions,reviews > prs.json
// dotnet run eng/review-metrics.cs -- prs.json
using System.Globalization;
using System.Text.Json;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
JsonSerializerOptions json = new(JsonSerializerDefaults.Web);
List<PullRequest> prs = JsonSerializer.Deserialize<List<PullRequest>>(File.ReadAllText(args[0]), json) ?? [];
// Bot PR'ları (dependency güncellemeleri) ayrı bir akış; ekip sayılarına katılmaz.
List<PullRequest> human = [.. prs.Where(p => !p.Author.Login.EndsWith("[bot]"))];
Console.WriteLine($"{"PR",-5} {"author",-7} {"lines",6} {"first review (h)",17} {"rounds",7}");
List<double> waits = [];
foreach (PullRequest pr in human.OrderBy(p => p.Number))
{
Review? first = pr.Reviews
.Where(r => r.Author.Login != pr.Author.Login)
.OrderBy(r => r.SubmittedAt)
.FirstOrDefault();
double? wait = first is null ? null : (first.SubmittedAt - pr.CreatedAt).TotalHours;
if (wait is double w)
{
waits.Add(w);
}
int rounds = pr.Reviews.Count(r => r.State == "CHANGES_REQUESTED") + 1;
Console.WriteLine($"#{pr.Number,-4} {pr.Author.Login,-7} {pr.Additions + pr.Deletions,6} {wait?.ToString("0.0") ?? "-",17} {rounds,7}");
}
Console.WriteLine();
Console.WriteLine($"median time to first review: {Median(waits):0.0} h");
Console.WriteLine($"median PR size: {Median([.. human.Select(p => (double)(p.Additions + p.Deletions))]):0} lines");
Console.WriteLine($"PRs over 400 lines: {human.Count(p => p.Additions + p.Deletions > 400)} of {human.Count}");
Console.WriteLine("reviews given: " + string.Join(", ", human
.SelectMany(p => p.Reviews.Where(r => r.Author.Login != p.Author.Login).Select(r => (p.Number, r.Author.Login)))
.Distinct()
.GroupBy(x => x.Login)
.OrderByDescending(g => g.Count())
.Select(g => $"{g.Key} {g.Count()}")));
static double Median(List<double> values)
{
if (values.Count == 0)
{
return 0;
}
List<double> sorted = [.. values.Order()];
int middle = sorted.Count / 2;
return sorted.Count % 2 == 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
}
record Author(string Login);
record Review(Author Author, string State, DateTimeOffset SubmittedAt);
record PullRequest(
int Number, Author Author, DateTimeOffset CreatedAt, DateTimeOffset? MergedAt, int Additions, int Deletions,
List<Review> Reviews);
İlk satırdaki PublishAot=false, file-based app'lerin varsayılan olarak açık gelen native AOT (uygulamayı önceden makine koduna derleme) ayarını kapatıyor; repo'daki TreatWarningsAsErrors ile birlikte, AOT derleyici paketinin bu SDK'daki sürümü için gelen güvenlik uyarısı build'i kırıyordu ve bu araç için AOT'a gerek yok. GitHub'a bağlanmadığımız için gh'nin çıktısıyla aynı biçimde, Ekim 2026'daki 13 PR'ı temsil eden bir örnek dosya hazırladık (eng/metrics/sample-prs.json):
$ dotnet run eng/review-metrics.cs -- eng/metrics/sample-prs.json
PR author lines first review (h) rounds
#101 emre 98 1.8 1
#102 ayse 182 3.7 2
#104 emre 1416 32.8 2
#105 ayse 42 0.6 1
#106 emre 82 1.2 1
#107 ayse 335 3.3 2
#108 emre 15 0.6 1
#109 emre 650 67.2 1
#110 ayse 117 1.3 1
#112 emre 79 0.8 1
#113 ayse 180 3.7 1
median time to first review: 1.8 h
median PR size: 117 lines
PRs over 400 lines: 2 of 11
reviews given: ayse 6, emre 5
İki bot PR'ı (103 ve 111) listede yok. Medyan bekleme 1,8 saat; sözleşmedeki bir iş günü hedefi rahat tutuyor. Asıl bilgi uçlarda: 400 satırı aşan iki PR, en uzun bekleyen iki PR. #104'ün 1416 satırı bir migration'ın üretilmiş dosyalarını da içeriyor; gh'nin additions değeri .gitattributes'taki linguist-generated işaretine bakmaz. #109'un 67 saati ise bir cuma öğleden sonra açılıp pazartesi sabahı review edilmesinden geliyor; saat farkını iş günüyle hesaplamayan bir metrik, hafta sonunu reviewer'ın yavaşlığı gibi gösterir.
Sayıların nasıl kullanılacağı en az kendileri kadar önemli. Bir ölçü hedef hâline geldiğinde iyi bir ölçü olmaktan çıkar; ekonomist Charles Goodhart'a atfedilen bu gözlem review metriklerinde hızla görünür. "İlk review'a kadar geçen süre iki saatin altında olsun" hedefi, içi boş hızlı onaylar üretir. "PR'lar 400 satırın altında olsun" hedefi, tek başına anlamsız parçalara bölünmüş PR'lar üretir. Bu yüzden bu sayılar ekip retrospektifinde bir konuşma başlatmak için kullanılır, kişileri karşılaştırmak için değil. Ölçülmemesi gerekenler de var: kişi başına yorum sayısı, kişi başına bulunan bug, "en çok onay veren" listesi. Bunlar review'u bir performans yarışına çevirir. Teslim hızının ve kalitesinin ekip düzeyindeki ölçüsü DORA (DevOps Research and Assessment; yazılım teslim performansını ölçen araştırma programı) metrikleri ve CI/CD Temelleri serisinin "Pipeline'ı Ölçmek ve Hızlandırmak: DORA Metrikleri, Cache ve Paralellik" yazısında anlatıldı.
Üretimde Dikkat
Ruleset'i önce "evaluate" modunda denemek mümkün; bu mod kuralları uygulamadan neyin engelleneceğini gösterir, ama GitHub Enterprise'a özgü. Diğer planlarda yeni bir kuralı açmadan önce ekibe duyurmak ve ilk hafta bypass ihtiyacı doğan durumları toplamak işe yarar. Bypass listesi bilinçli olarak boş: acil bir düzeltmede bile PR açılır, ama tek onayla ve hızlı review'la geçer.
Kuralların dosyada durması, kural değişikliğinin de review'dan geçmesi demek. eng/rulesets/ ve .github/ CODEOWNERS'ta lead'lere ait; zorunlu onayı kaldıran bir değişiklik, kimse fark etmeden ayarlardan yapılamaz. Ama dosya ile GitHub'daki ayar birbirinden kopabilir; dosyayı değiştiren PR merge edildikten sonra ayarın da güncellenmesi gerekiyor. Bunu bir workflow ile otomatikleştirmek mümkün, ama yönetici yetkili bir token ister; Bookstore'da bu adım elle yapılıyor ve PR template'inin "Risk and rollback" bölümüne yazılıyor.
Copilot onaylarını açan bir ekip, bunu ruleset'teki onay ayarlarıyla birlikte düşünmeli: her yeni commit'te onay düşer, Copilot yeniden çalışır ve yeniden onaylayabilir. Code owner onayı zorunlu değilse ve zorunlu onay sayısı birse, bir PR hiçbir insan onaylamadan merge edilebilir. Copilot onayının code owner şartını karşılayıp karşılamadığını dokümanda bulamadık; bu yüzden bu ayarı açmadan önce bir test repo'sunda denemek gerekir.
Sık Düşülen Tuzaklar
Zorunlu onay sayısını artırıp sorumluluğu dağıtmak. İki onay istenen bir PR'da her reviewer diğerinin dikkatli baktığını varsayabilir. Bir kişinin gerçekten sorumlu olduğu tek onay, iki yüzeysel onaydan iyidir; ikinci göz gereken yerleri (migration, ödeme) required reviewers ile dosya bazında işaretle.
CODEOWNERS'ı fazla geniş yazmak. Kök klasördeki tek bir * satırı bir takımı her PR'ın zorunlu reviewer'ı yapar ve darboğazı platform kuralına çevirir.
Bot yorumlarının insan yorumlarını boğması. Label, dependency, coverage ve AI bot'larının her biri PR'a birkaç yorum bırakırsa reviewer'ın yorumu sayfanın ortasında kaybolur. Bot'ları yorum yerine check ya da label üretecek şekilde ayarlamak, ya da yorumlarını tek bir özet yorumda birleştirmek bunu önler.
Kuralı ayarda bırakıp sözleşmeye yazmamak. Ruleset'in neden require_last_push_approval istediğini bilmeyen biri, ilk takıldığında kuralı "gereksiz" bulup kaldırılmasını ister. Kuralın gerekçesi review sözleşmesinde ya da bir ADR'da (Architecture Decision Record; mimari bir kararı gerekçesiyle kaydeden kısa doküman) yazılı olmalı.
Metrikleri kişilere çevirmek. Ekip ortalaması bir sohbet başlatır; kişi bazında tablo bir sıralama başlatır.
Küçük Bir Deneme
★☆☆ Kendi repo'nda CODEOWNERS'ı aç ve her satırın sahiplerinin repo'ya yazma yetkisi olduğunu kontrol et. Bir satıra iki sahip yazılmışsa, ekibin niyeti "ikisinden biri" mi, "ikisi de" mi? İkincisiyse required reviewers kuralına taşı.
★★☆ gh pr list --state merged --limit 50 --json number,author,createdAt,mergedAt,additions,deletions,reviews çıktısını bir dosyaya yaz ve review-metrics.cs'i bu dosyayla çalıştır. Bekleme süresini iş saatleriyle hesaplayacak şekilde değiştir: hafta sonu ve mesai dışı saatler sayılmasın.
★★★ Ekibinin review sözleşmesini (ilk yazıdaki docs/code-review.md gibi) madde madde ruleset parametreleriyle eşleştir. Hangi madde bir platform kuralına dönüşebiliyor, hangisi yalnız ekip alışkanlığıyla korunabiliyor? Platform kuralı olabilecekleri bir ruleset JSON'u olarak yaz ve bir PR ile review'a sun.
Seri burada bitiyor. Review'un neden yapıldığından başladık, PR'ı küçük ve okunur yazmayı, reviewer olarak neye ve hangi sırayla bakılacağını, yorumların dilini ve sonunda bunların GitHub'da hangi ayarlarla desteklendiğini gördük. Ayarlar süreci korur ama yürütmez; review'u değerli yapan, onu yapan iki kişinin aynı amaca, kodun ve ekibin bir öncekinden daha iyi olmasına bakması.