Mert Özen Her Satırda Daha İleri
Yazılım Mimarisi Mimariye Giriş — #08

Conway's Law: Ekip Yapısı Mimariyi Nasıl Şekillendirir?

Mert Özen 7 Kas 2026 11 dk 4 görüntülenme
Conway's Law: Ekip Yapısı Mimariyi Nasıl Şekillendirir?

Sistemler, onları tasarlayan organizasyonun iletişim yapısını kopyalar: aynı 14 commit katman ekiplerinde %86, akış ekiplerinde %21 oranında birden fazla ekibin koordinasyonunu gerektiriyor. Conway (1968), Inverse Conway Maneuver, Team Topologies'in ekip tipleri ve etkileşim modları, cognitive load ve Bookstore'un 3 kişilik ekip ile 4 stream-aligned ekip için iki farklı doğru mimarisi.

Sürüm notu: Bu yazıdaki kodlar C# 14 ve .NET 10 ile derlenip denenmiştir.

Önce Tahmin Et

Bookstore'un Git geçmişinden son 14 commit'i aldık. Kod bugünkü layered yapıda: Bookstore.Web, Bookstore.Business ve Bookstore.Data project'leri, her birinin içinde Catalog, Ordering, Inventory gibi alt klasörler. Commit'ler sıradan iş istekleri: arama sayfasına yayın yılı filtresi, kısmi sipariş iptali, checkout'ta stok ayırma, kargo takip sayfası, kargo çıkınca e-posta.

Şimdi aynı 14 commit'i üç farklı ekip yapısına göre değerlendireceğiz. Birincisi bugünkü hâl: 3 developer, tek ekip. İkincisi şirket büyüyünce akla ilk gelen yapı: bir web ekibi, bir backend ekibi, bir data ekibi. Üçüncüsü iş akışına göre kurulmuş dört ekip: Catalog & Search, Ordering & Payment, Fulfillment (Inventory ve Shipping), Customer Experience (Notification ve hesap).

Her yapıda, commit'lerin yüzde kaçı birden fazla ekibin dokunduğu dosyaları değiştiriyor? Yani yüzde kaçı, birden fazla ekibin konuşmasını, planlamasını ya da birbirini beklemesini gerektiriyor? Üç sayı tahmin et. Kod hiç değişmiyor; değişen yalnız hangi dosyanın hangi ekibe ait olduğu.

Conway's Law: Sistem, Onu Tasarlayan Organizasyonun Kopyasıdır

Melvin Conway, Nisan 1968'de Datamation dergisinde yayımlanan "How Do Committees Invent?" makalesinde şu tezi ortaya koydu: sistem tasarlayan organizasyonlar, kendi iletişim yapılarının kopyası olan tasarımlar üretmeye mecburdur. Conway bunu matematiksel bir benzetmeyle anlatıyor: sistemin parçalarını ve interface'lerini bir graf, tasarım gruplarını ve aralarındaki iletişim kanallarını başka bir graf olarak çizersen, ikisi arasında yapıyı koruyan bir eşleme (homomorphism) vardır. İki grubun konuşmadığı yerde, iki parçanın da birbiriyle konuşması zordur.

Makale bir de şunu söylüyor: tasarım organizasyonunu kurmak, tasarım kararlarının bir kısmını baştan vermek demek. Ekipleri böldüğün an, sistemin hangi parçalara bölüneceğine dair ilk kararı da vermiş olursun. Fred Brooks bu fikri 1975'te The Mythical Man-Month kitabında Conway's Law adıyla andı ve isim yerleşti.

Martin Fowler, Ekim 2022'de yazdığı Conway's Law yazısında yasanın gördüğü her sistemi etkilediğini ve ona karşı savaşmanın çoğu zaman kaybedilen bir savaş olduğunu söylüyor. Yazıdaki örnekler bunu somutlaştırıyor: tek bir ekibin yazdığı compiler tek geçişli, iki ekibin yazdığı iki geçişli olur; altı farklı şehirde altı ekibe architect'lik yapan biri, mesafe ve saat farkı konuşmayı kısıtlayacağı için baştan altı alt sistem ve aralarında dar, iyi tanımlı interface'ler kararlaştırır. Fowler'ın önemli bir notu daha var: birbiriyle gayriresmî konuşan bir iki düzine kişi doğal olarak bir monolith üretir ve bunda bir sorun yoktur. Yasa, insanları organize etmek gerektiği ölçüde önem kazanır.

Ölçmek: Commit Geçmişinden Koordinasyon Maliyeti

Conway's Law'u soyut bırakmamak için küçük bir araç yazdık. Araç, repository'nin commit geçmişini git log ile okuyor, her dosyayı bir sahiplik dosyasındaki kurallara göre bir ekibe eşliyor ve birden fazla ekibe dokunan commit'leri sayıyor. Sahiplik kuralları GitHub'ın CODEOWNERS dosyasındaki gibi işliyor: bir dosyaya uyan kurallardan sonuncusu kazanıyor.

using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

// Kullanım: dotnet run -- <repo> <owners-file>
string repo = args[0];
List<(Regex Pattern, string Team)> rules = File.ReadLines(args[1])
    .Where(line => line.Trim() is { Length: > 0 } t && !t.StartsWith('#'))
    .Select(line => line.Split(' ', StringSplitOptions.RemoveEmptyEntries))
    .Select(parts => (GlobToRegex(parts[0]), parts[1]))
    .ToList();

List<Commit> commits = ReadCommits(repo);
int crossTeam = 0;
Dictionary<string, int> pairCounts = [];

foreach (Commit commit in commits)
{
    // CODEOWNERS'taki gibi: dosyaya uyan son kural kazanır
    string[] teams = commit.Files
        .Select(file => rules.LastOrDefault(r => r.Pattern.IsMatch(file)).Team ?? "(unowned)")
        .Distinct()
        .Order()
        .ToArray();

    if (teams.Length > 1)
    {
        crossTeam++;
        string pair = string.Join(" + ", teams);
        pairCounts[pair] = pairCounts.GetValueOrDefault(pair) + 1;
    }
}

Console.WriteLine($"Commits: {commits.Count}, needing more than one team: {crossTeam} ({(double)crossTeam / commits.Count:P0})");
foreach ((string pair, int count) in pairCounts.OrderByDescending(p => p.Value).ThenBy(p => p.Key))
    Console.WriteLine($"  {count,2} x {pair}");

static List<Commit> ReadCommits(string repo)
{
    ProcessStartInfo psi = new("git", ["-C", repo, "log", "--name-only", "--pretty=format:@%h %s"]) { RedirectStandardOutput = true };
    using Process git = Process.Start(psi)!;
    List<Commit> result = [];
    Commit? current = null;
    while (git.StandardOutput.ReadLine() is { } line)
    {
        if (line.StartsWith('@')) result.Add(current = new Commit(line[1..], []));
        else if (line.Length > 0) current!.Files.Add(line);
    }
    git.WaitForExit();
    return result;
}

static Regex GlobToRegex(string glob) =>
    new("^" + Regex.Escape(glob).Replace(@"\*\*/", "(.*/)?").Replace(@"\*\*", ".*").Replace(@"\*", "[^/]*") + "$");

sealed record Commit(string Title, List<string> Files);

Commit geçmişinden bir kesit, aracın ne gördüğünü gösteriyor:

@2e4654c Email when order ships
src/Bookstore.Business/Notification/EmailService.cs
src/Bookstore.Business/Shipping/ShipmentService.cs

@4396d9d Low stock alert page
src/Bookstore.Business/Inventory/StockService.cs
src/Bookstore.Data/Inventory/StockRepository.cs
src/Bookstore.Web/Inventory/AlertsPage.cs

Üç sahiplik dosyası, üç ekip yapısı:

# Bugün: 3 developer, tek ekip
src/**                     bookstore-team
# Layer'a göre ekipler
src/Bookstore.Web/**       web-team
src/Bookstore.Business/**  backend-team
src/Bookstore.Data/**      data-team
# Akışa (domain'e) göre ekipler
**/Catalog/**              catalog-search-team
**/Ordering/**             ordering-payment-team
**/Payment/**              ordering-payment-team
**/Inventory/**            fulfillment-team
**/Shipping/**             fulfillment-team
**/Notification/**         customer-experience-team

Aynı repository, aynı 14 commit, üç çalıştırma:

for o in owners-single-team owners-by-layer owners-by-stream; do echo "== $o"; dotnet run -- ../repo ../$o.txt; done
== owners-single-team
Commits: 14, needing more than one team: 0 (0 %)
== owners-by-layer
Commits: 14, needing more than one team: 12 (86 %)
   8 x backend-team + data-team + web-team
   3 x backend-team + data-team
   1 x backend-team + web-team
== owners-by-stream
Commits: 14, needing more than one team: 3 (21 %)
   1 x (unowned) + ordering-payment-team
   1 x customer-experience-team + fulfillment-team
   1 x fulfillment-team + ordering-payment-team

Tek ekipte koordinasyon maliyeti sıfır; zaten herkes aynı masada. Layer ekiplerinde commit'lerin %86'sı birden fazla ekip gerektiriyor ve 14 commit'in 8'i üç ekibin birden dokunduğu değişiklikler. "Arama sayfasına yayın yılı filtresi" gibi küçük bir istek web ekibinin endpoint'ini, backend ekibinin service'ini ve data ekibinin sorgusunu değiştiriyor; üç backlog, üç sprint planı, iki handoff. Akış ekiplerinde oran %21'e iniyor ve geriye kalan üç commit gerçekten iki iş alanını birleştiren işler: checkout'ta stok ayırma (Ordering ile Inventory), kargo çıkınca e-posta (Shipping ile Notification) ve bir migration.

Bu sonuç, altıncı yazıdaki technical ve domain partitioning ayrımının organizasyon tarafı. Kod layer'lara bölündüğü için layer ekipleri kurmak doğal görünüyor, ama Bookstore'un değişiklikleri iş alanlarından geliyor ve her biri layer'ları dikine kesiyor. Conway's Law burada iki yönde işliyor: layer ekipleri layered mimariyi kalıcılaştırıyor, layered mimari de layer ekiplerini haklı gösteriyor. Örnek küçük ve commit'leri biz seçtik, yani yüzdeleri genelleme. Ama aynı aracı kendi repository'nin son altı ayına çalıştırırsan, hangi ekip yapısının sana ne kadar koordinasyona mal olduğunu ilk kez sayıyla görürsün.

Üçüncü çıktıdaki (unowned) satırına da dikkat et. Migration klasörü hiçbir ekibe ait değil; "Add order note field" commit'i hem Ordering ekibinin dosyalarına hem de sahibi olmayan bir dosyaya dokunuyor. Sahipsiz kod, ekip yapısı değişince en hızlı çürüyen kod: herkes ona dokunur, kimse sorumluluğunu almaz.

Inverse Conway Maneuver

Yasa bir gözlemse, onu bir araç olarak da kullanabilirsin. Inverse Conway Maneuver, istediğin mimariyi elde etmek için önce ekip yapısını ve iletişim yollarını değiştirmek demek. Terimi Jonny LeRoy ve Matt Simons, Cutter IT (Information Technology) Journal'ın Aralık 2010 sayısındaki bir makalede kullandı; Fowler'ın Conway's Law yazısı da kaynağı böyle veriyor.

Fowler, Conway's Law karşısında üç tavır sayıyor. Görmezden gelmek, ki bunu hata olarak görüyor. Kabul etmek: mimariyi mevcut iletişim yapısıyla çatışmayacak şekilde tasarlamak. Ve ters manevra: iletişim yapısını, istenen mimariyi teşvik edecek şekilde değiştirmek. Microservices'i bu açıdan, önce bir organizasyon tasarımı aracı olarak görüyor: iş yeteneği etrafında kurulmuş küçük ve uzun ömürlü ekipler, bağımsız deploy edilebilen service'leri doğal olarak üretir.

Uyarısı da net. Ters manevra her şeye kadir değil: organizasyonu değiştirmek, yerleşmiş ve esnemeyen bir mimariyi hızla düzeltmez, üstüne sürtünme yaratabilir. Değişiklikleri küçük adımlarla ve geri bildirime bakarak yapmak gerekiyor. Bookstore'un tablosunda bunun anlamı şu: dört akış ekibini kurduğun gün kod hâlâ layered olacak. Ekipler kendi alanlarındaki kodu zamanla module'lere çekecek; mimari, ekip yapısını birkaç ay geriden takip edecek.

Team Topologies: Dört Ekip Tipi, Üç Etkileşim Modu

Matthew Skelton ve Manuel Pais'in Team Topologies kitabı (IT Revolution, 2019; ikinci baskı Eylül 2025) Conway's Law'u ekip tasarımının merkezine koyuyor ve ekipleri dört temel tipe ayırıyor. Stream-aligned team, iş alanının bir bölümüne ait bir değer akışına hizalanmış ekip; Bookstore'un dört akış ekibi bu tipte ve organizasyondaki ekiplerin çoğunluğu bu tipte olmalı. Enabling team, stream-aligned ekiplerin önündeki engelleri kaldırıp eksik yetenekleri fark eden, bir süre yanlarında çalışıp sonra çekilen ekip. Complicated-subsystem team, ciddi uzmanlık (matematik, hesaplama, özel bir teknik alan) gerektiren bir alt sistemin sahibi. Platform team, diğer ekiplerin işini hızlandıran iç bir ürün sunan ekip: deploy hattı, gözlem araçları, hosting.

Ekipler arası ilişkiler de üç modla sınırlanıyor. Collaboration: iki ekip, yeni bir şeyi keşfetmek için belirli bir süre birlikte çalışır. X-as-a-Service: bir ekip bir şeyi sunar, diğeri onu bir service gibi kullanır. Facilitation: bir ekip diğerine yardım eder ve onu yetiştirir. Modların bilinçli seçilmesinin sebebi Conway: iki ekip sürekli collaboration modundaysa, sorumlulukları ve kodları da birbirine karışır; X-as-a-Service ilişkisi ise iki ekip arasında net bir interface ister.

Bookstore'un 18 aylık hedefinde dört stream-aligned ekip ve bir platform ekibi var. Platform ekibi CI (Continuous Integration) hattını, ortamları ve izleme araçlarını X-as-a-Service olarak sunuyor. Ordering & Payment ile Fulfillment arasında stok ayırma kuralları netleşene kadar birkaç haftalık bir collaboration dönemi mantıklı; sınır oturduktan sonra ilişki X-as-a-Service'e dönmeli. Güvenlik tarafında OWASP (Open Worldwide Application Security Project) pratiklerini yaymak için küçük bir enabling ekip ya da rol, ekipleri facilitation moduyla destekleyebilir.

Cognitive Load: Bir Ekip Ne Kadar Sistem Taşır?

Team Topologies'in en kullanışlı kavramlarından biri cognitive load. Kitap, psikolojideki cognitive load theory'den üç türü alıyor: intrinsic load (işin kendisinin temel becerileri), extraneous load (işin etrafındaki mekanik yük: araçlar, ortamlar, deploy adımları) ve germane load (asıl değeri üreten domain bilgisine harcanan zihinsel çaba). Hedef, extraneous yükü platform ve iyi araçlarla azaltıp ekibin kapasitesini germane yüke ayırmak.

Buradan çıkan mimari kural basit: bir ekibe verilen yazılımın büyüklüğü, ekibin zihinsel kapasitesine sığmalı. Bir ekip sahibi olduğu service'leri hem geliştirip hem de işletebilecek kapasitede olmalı. Bunun kesin bir ölçüsü yok, ama ekipler birbiriyle kıyaslanabilir: keşif ağırlıklı, sürekli değişen bir domain (Bookstore'da Ordering & Payment) tekrar eden işlerden oluşan bir domain'den daha fazla yük getirir. Skelton ve Pais'in önerdiği pratik yöntemlerden biri, ekibe sistemini ne kadar anladığını düzenli olarak 1'den 5'e puanlatmak; düşen puan ya bir kişinin desteğe ihtiyacı olduğunu ya da mimaride bir sorun olduğunu gösterir.

Bu yüzden "ekip yapısı" ve "mimari" ayrı kararlar değil. Bookstore'un bugünkü 3 kişilik ekibi için bütün sistem tek bir cognitive load birimi ve tek bir monolith doğru cevap. Dört ekip olduğunda her ekibin taşıyabileceği bir parça olması gerekecek; o parçanın sınırı da Team Topologies'in fracture plane dediği doğal dikiş yerlerinden, yani iş alanı sınırlarından, farklı değişim hızlarından ya da farklı uyumluluk gereksinimlerinden geçecek.

Bookstore: Aynı Sistem, İki Doğru Mimari

Bugün Bookstore'da 3 developer ve yarı zamanlı bir ops kişisi var. Herkes her yere dokunuyor, günlük konuşma her koordinasyonu çözüyor ve ölçtüğümüz koordinasyon maliyeti sıfır. Bu ekip için doğru mimari tek bir deployment unit: layered ya da tercihen modular monolith. Bu ekibe beş service vermek, Conway'in tersine çalışmak olur: sistemi tek bir ekibin taşıyamayacağı bir operasyon yüküyle bölmüş olursun ve service sınırları ekibin iletişim yapısında hiçbir karşılık bulmaz.

18 ay sonra dört stream-aligned ekip ve bir platform ekibi olduğunda aynı sistem için doğru cevap değişiyor. Önce module sahipliği: her ekip kendi module'lerinin sahibi, CODEOWNERS dosyası bu sahipliği yazılı hâle getiriyor ve module'ler arası değişiklikler iki ekibin onayını istiyor. Sonra, bağımsız release ihtiyacı ölçülebilir hâle geldiğinde, ekip sınırlarıyla örtüşen birkaç kaba taneli service; yani bir önceki yazıdaki haritanın service-based köşesi. Service sınırı ekip sınırıyla örtüşüyorsa Conway seninle aynı yönde çalışır.

İki mimari de kendi zamanı için doğru. Hata, birinin cevabını diğerinin zamanında uygulamaktır: 3 kişiyle microservices kurmak ya da 30 kişiyi tek bir module'süz monolith'te tutmak.

Sık Düşülen Tuzaklar

Ekip yapısını değiştirmeden microservice'e geçmek. Layer ekipleri microservice'leri de layer'lara göre böler: bir "frontend service", bir "business service", bir "data service". Sonuç, bir önceki yazıların distributed monolith'i.

Sahipliği belirsiz bırakmak. "Bu module'ü herkes geliştiriyor" cümlesi pratikte "kimse sahiplenmiyor" demek. Migration klasörü, ortak kütüphaneler, build script'leri gibi sahipsiz kalan her yer, ekip yapısı değiştiğinde ilk bozulan yer olur. Her dosyanın bir sahibi olmalı; CODEOWNERS bunu yazılı ve kontrol edilebilir hâle getirir.

Team Topologies'i yeniden etiketleme sanmak. Mevcut backend ekibinin adını "platform team" yapmak hiçbir şeyi değiştirmez. Platform ekibinin ürünü, onu kullanan ekiplerin işini gerçekten kolaylaştırıyorsa platformdur.

Collaboration modunu kalıcı hâle getirmek. İki ekibin sürekli birlikte çalışması, aslında tek bir ekip olmaları gerektiğine ya da aralarındaki sınırın yanlış yerde olduğuna işaret eder.

Inverse Conway'i tek seferlik bir reorganizasyon sanmak. Organizasyonu bir gecede değiştirip mimarinin ertesi gün takip etmesini beklemek, Fowler'ın uyardığı sürtünmeyi üretir. Ekip değişikliği küçük adımlarla, kodun yeniden yapılanmasıyla birlikte ilerlemeli.

Küçük Bir Deneme

★☆☆ Kendi repository'nde son 100 commit'e git log --name-only ile bak ve her commit'in kaç farklı üst klasöre dokunduğunu say. En çok birlikte değişen iki klasör hangileri? Bu iki klasör aynı ekibe mi ait?

★★☆ Bu yazıdaki aracı kendi repository'ne ve ekip yapına uyarlayan bir sahiplik dosyası yaz. Hiçbir kurala uymayan, (unowned) çıkan dosyaları listele. Bunların sahibi kim olmalı?

★★★ Aracı genişlet: commit'leri son 30, 90 ve 180 gün olarak grupla ve ekipler arası commit oranının zaman içinde nasıl değiştiğini yazdır. Oran artıyorsa ekip yapısı mı, mimari mi geride kalıyor? Bir de iki ekibin en sık birlikte dokunduğu dosyaları listele; bunlar bir sınırın yanlış yerde olduğunun ilk işaretidir.

Bu yazıda mimarinin ekip yapısından nasıl etkilendiğini ölçtük. Ama doğru ekip yapısı ve doğru mimariyle başlasan bile, zaman geçtikçe kod kararlardan uzaklaşır: bir kestirme, bir acil rapor, bir "şimdilik" referans. Bir sonraki ve serinin son yazısında bu kaymaya, yani architecture erosion'a bakıyoruz ve kararları ArchUnitNET ile test'e, query sayısını bir fitness function'a çevirerek CI'da otomatik koruyoruz.