archlint: mimari sınırları CI'da deterministik olarak zorlamak
Mimari kararlar wiki'de doğru kalır, kodda çürür. archlint, architecture.json'daki sınırları her commit'te go/parser ile, modelsiz zorluyor.
Mimari kararların neden çürüdüğünü ayrı yazdım: bir ADR “domain altyapıyı import etmesin” der, iki yıl sonra bir bakarsın yüzlerce yerde import etmiş — çünkü kuralı izleyen hiçbir şey yoktu. Bu yazı o sorunun araç tarafı: küçük, bağımsız bir Go CLI — archlint.
architecture.json: kuralı koda yaz
Katmanları ve aralarındaki izinli bağımlılıkları tek bir dosyada beyan ediyorsun:
{
"module": "github.com/acme/app",
"layers": {
"domain": ["internal/domain"],
"db": ["internal/db"],
"http": ["internal/http"]
},
"rules": {
"domain": [],
"db": ["domain"],
"http": ["domain", "db"]
}
}
domain içeride hiçbir şey import edemez, db yalnızca domain’i, http ikisini de. Başka her iç kenar bir ihlal. Aynı-katman import’u her zaman serbest; [] “hiçbir katmanı import edemez” demek.
Nasıl çalışıyor
archlint check, her .go dosyasının import’larını stdlib’in go/parser’ıyla çıkarıyor — regex tahmini değil, derlemeye gerek yok, kesin. Her dosyayı ve her import’u (modül yolunu sıyırıp) bir katmana çözüyor; kuralı çiğneyen kenarı dosya:satır ile raporluyor:
$ archlint check examples/sample
Scanned examples/sample against examples/sample/architecture.json — 2 layer(s).
1 boundary violation(s):
internal/domain/bad.go:6 domain → db is not allowed (import "github.com/acme/app/internal/db")
İhlal varsa exit 1 → CI kırmızı. Çiğneyen import main’e ulaşmadan yakalanıyor; iki yıl sonra bir arkeoloji seansında değil.
Tasarım kararları
- Deterministik, modelsiz. Bütün amaç CI’da kapı kurabileceğin bir guardrail: aynı diff, aynı verdict, döngüde model yok. Bir LLM’e “mimariyi gözden geçir” demekten farkı bu.
- Sıfır bağımlılık (Go stdlib). Config şimdilik JSON — YAML bir bağımlılık ekliyor, o yüzden follow-up.
- Önce Go.
go/parserGo import’larını kusursuz veriyor; keskin bir MVP tek dilde iyi yapılır. TypeScript ve Python, kendi parser’larıyla sıradaki adım.
CI’da
Buradan sonrası, GitHub Actions ile kurduğum akışların alışılmış iki satırı; archlint’in tek beklentisi, ihlalde sıfırdan farklı bir kod dönerek job’ı düşürebilmek.
- run: go install github.com/muhammetsafak/archlint/cmd/archlint@latest
- run: archlint check
Ya da paketlenmiş GitHub Action ile:
- uses: muhammetsafak/archlint@v0.3.0
Sınırlar — dürüst liste
- Go, şimdilik. Import’lar
go/parserile okunuyor (kesin); TS/Python kendi parser’larıyla sıradaki adım. - Import sınırları, şimdilik. Katmanlar arası bağımlılık grafiğini yönetir. “Senkron olması gereken yerde async çağrı” ya da domain sınırını aşan doğrudan bir DB sorgusu, runtime trace’leri ilişkilendirmeyi gerektirir — sonraki faz.
- JSON config (YAML follow-up). Deterministik tasarım kasıtlı: gate’lenebilir olsun diye.
Denemek için
architecture.json’unu yaz, archlint check çalıştır — ya da archlint check examples/sample ile kasıtlı bir ihlalin yakalandığını gör. Hangi sınırı bir sonraki sürümde (TS? Python?) görmek istediğini yazarsan, sıraya koyarım.
Bu konudaki deneyler
Dile özgü serileştirme yerine dondurulmuş bir JSON zarfı; polyglot kuyruk standardı BabelQueue'ya dönüştü.
Şu an ne yapıyor
Dondurulmuş JSON zarfı dört dilde — PHP, Python, Go, Node.js — aynı baytları okutuyor; sidecar ya da broker eklentisi gerekmiyor. Polyglot kuyruk kuran ekipler spesifikasyonu ve SDK'ları bugün kullanabilir.
Diff'i makineden çıkarmadan bir modele denetleten, sağlayıcıdan bağımsız Go CLI; CommitBrief ürününe dönüştü.
Şu an ne yapıyor
Diff hiç makineden çıkmadan yerel kod incelemesi yapıyor; `Provider` arayüzü sayesinde Anthropic, OpenAI, Gemini ve Ollama aynı sözleşmeyle takılıyor. İmzalı binary'ler yayında — diff'ini bir bulut sağlayıcısına vermek istemeyen herkes kurabilir.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.