Architecture Decision Records (ADR) Nedir? Mimari Karar Alırken Nelere Dikkat Etmelisiniz? 🗒️ — TR

Architecture Decision Records (ADR), yazılım projelerinde alınan önemli mimari kararların, bağlam, alternatifler ve sonuçlarıyla birlikte belgelenmesini sağlayan bir yöntemdir. Bu dokümantasyon, kararların izlenebilirliğini artırır ve ekipler arasında şeffaf iletişim kurulmasını sağlar. ADR’ler, projelerin uzun vadede sürdürülebilirliğini desteklerken, yeni katılan ekip üyelerinin geçmiş kararları anlamasına da yardımcı olur.

Murat Dinç
Devops Türkiye☁️ 🐧 🐳 ☸️
10 min read5 days ago

--

Selamlar,

Yazılım geliştirme süreçleri içerisinde pek çok önemli karar alınır. Bu kararlar, projeyi nasıl inşa edeceğimiz, hangi teknolojileri seçeceğimiz ve mimarinin nasıl şekilleneceği ile ilgilidir. Bu tür kararların zamanla unutulmaması ve gelecekteki ekip üyelerine veya kendimize referans olması için Architecture Decision Records (ADR) kullanılır.

Bu yaklaşımı günümüz şartlarında hakkıyla uygulayan firma sayısı, şahsi görüşüme göre, bir elin parmaklarını geçmez. Ancak doğru şekilde uygulandığında, tüm mimari karar süreçlerinde geçmişe yönelik sorulara bile yanıt verebilen bir yaklaşımdır.

Bir şirkette işe başladığınızı düşünün; üzerinde çalıştığınız projede birçok farklı yaklaşım görüyorsunuz. Belki bazıları size saçma geliyor ve ‘eski yazılımcının işi’ ya da ‘projeye baştan yanlış başlanmış, böyle devam etmiş’ gibi düşüncelere kapılıyorsunuz. Hadi itiraf edelim, hepimiz bunu yapıyoruz. 😊

Ancak ADR ile bu kararlar kayıt altına alınmış olsaydı, sebeplerini sorgulama şansınız olabilirdi.

Hocam, ‘Sen bu yaklaşımı kullanıyor musun?’ diye sorduğunuzu duyar gibiyim. Bu sorunuza makalenin sonunda cevap vereceğim. 😊

Hadi inceleyelim👇🏻

Architecture Decision Records (ADR) Nedir?

Architecture Decision Records (ADR), yazılım projelerinde alınan önemli mimari kararları sistematik bir şekilde belgelemeye yarayan bir yöntemdir. Bir proje geliştirirken sürekli olarak çeşitli mimari tercihler ve kararlar alırız. Ancak bu kararların sadece alınması değil, neden alındıklarının ve hangi alternatiflerin değerlendirildiğinin de anlaşılır bir şekilde kaydedilmesi, projeye uzun vadede katkı sağlar.

ADR şu temel soruları cevaplar 👇🏻

  1. Hangi problemi çözmek istiyoruz?
  2. Hangi alternatifleri değerlendirdik?
  3. Hangi çözümü ve neden seçtik?
  4. Bu kararın sonuçları neler olacak?

💡 Neden ADR Kullanmalıyız?

Bir yazılım projesi ilerledikçe, mimaride alınan bazı kararlar neden o şekilde alındı, alternatif çözümler neden reddedildi gibi sorular önem kazanmaya başlar. ADR, projedeki belirsizlikleri ve karmaşıklıkları gidermeye yardımcı olur. Bunun en önemli faydalarını aşağıda sıralayabiliriz:

  • Kararların izlenebilirliği: Zamanla, yazılım projelerinde alınan kararların neden alındığı unutulabilir. Bir yıl sonra, “Bu mikro servis mimarisini neden seçtik?” veya “Bu kütüphaneyi kullanmanın avantajları neydi?” gibi sorulara yanıt aramak oldukça zor olabilir. ADR, bu sorulara net cevaplar verir.
  • Tutarlılık: Ekibe yeni katılan geliştiriciler, önceki mimari kararların neden alındığını anlamakta zorluk çekebilir. ADR, bu kişilerin projenin mevcut yapısını ve neden bu şekilde olduğunu anlamalarını kolaylaştırır. Bu sayede ekip büyüdüğünde veya projenin kapsamı genişlediğinde daha tutarlı bir çalışma yürütülür.
  • Gelecekteki gelişim: Proje üzerinde gelecekte yapılacak değişikliklerde, önceki kararların etkisini değerlendirmek ve hangi kararların güncellenmesi gerektiğine karar vermek kolaylaşır. Yeni bir mimari karar alınacağı zaman, önceki kararların sonuçları göz önünde bulundurulabilir.
  • İletişim: Bir projede birden fazla geliştirici ve ekip bulunuyorsa, iletişim eksikliği büyük sorunlara yol açabilir. ADR, ekipler arasındaki iletişimi düzenler ve herkesin aynı sayfada kalmasını sağlar.

🗓️ Ne Zaman ADR Kullanmalıyız?

Her mimari karar için ADR oluşturmak gerekmeyebilir. Örneğin, bir UI framework seçimi için ADR yazmak her zaman şart değildir. Ancak, aşağıdaki durumlarda ADR kullanmak oldukça faydalıdır:

  • Kritik mimari kararlar: Eğer karar, uygulamanın genel yapısını veya performansını önemli ölçüde etkileyecekse, bu karar için bir ADR yazmak iyi bir uygulamadır. Örneğin, bir veritabanı teknolojisi seçimi veya bir mikro servis mimarisine geçiş kararı.
  • Teknik borç veya risk taşıyan kararlar: Eğer bir karar kısa vadede avantaj sağlasa da, uzun vadede teknik borç yaratma riski taşıyorsa, bu kararın belgelenmesi ve sonuçlarının gelecekte değerlendirilmesi için ADR kullanılmalıdır.
  • Ekip kararları: Ekip içinde alınan ve tüm geliştiricileri etkileyen kararlar da ADR ile belgelenmelidir. Örneğin, bir geliştirme metodolojisi (Scrum, Kanban) seçimi veya kodlama standartları gibi.

🔴 ADR Neden Önemlidir?

Projeler zamanla büyür ve bir projede yer alan ekip üyeleri değişebilir. İlk başta alınan mimari kararlar zaman içinde karmaşık hale gelebilir ve anlamını yitirebilir. ADR’ler bu noktada devreye girer ve alınan kararların arka planını, alternatifleri ve nihai kararı sistematik bir şekilde belgelendirir.

Bununla birlikte, ADR’ler şunları sağlar:

  • İzlenebilirlik: Alınan kararların neden alındığını ve hangi alternatiflerin değerlendirildiğini belgeleyerek, gelecekte bu kararlara dönüp bakmayı kolaylaştırır.
  • İletişim: Ekip üyeleri arasında alınan kararlar konusunda netlik sağlar.
  • Tutarlılık: Yeni ekip üyeleri projeye katıldığında, proje boyunca alınan önemli kararları anlamaları ve devam ettirmeleri daha kolay olur.
  • Daha İyi Karar Verme: Alternatiflerin tartışılması ve bunların kaydedilmesi, daha iyi kararlar almayı sağlar.

🗒️ ADR’nin Temel İçeriği

Her ADR belgesi, bir mimari kararın tüm yönlerini açık ve anlaşılır bir şekilde anlatmalıdır. Standart bir ADR dokümanı aşağıdaki başlıklardan oluşur:

1. Başlık (Title): Alınan kararın kısa bir özeti. Örneğin, “ORM Seçimi: Entity Framework vs Dapper”.

2. Bağlam (Context): Kararın alınmasına sebep olan durumları ve bağlamı açıklar. Burada, proje ihtiyaçları, sınırlamalar ve mevcut problemler belirtilir. Bu bölümde, kararın hangi ihtiyaçtan doğduğunu net bir şekilde ifade etmek gerekir. Örneğin, veritabanı işlemlerinin performansı çok düşükse ve bir ORM çözümü arıyorsak, bunu burada açıklamalıyız.

3. Karar (Decision): Hangi çözümün tercih edildiği ve neden seçildiği açıklanır. Bu bölüm, alınan kararın detaylarını içerir. Örneğin, “Dapper ORM’yi seçtik çünkü performans açısından ihtiyaçlarımızı daha iyi karşılıyor.”

4. Alternatifler (Alternatives): Karar alınırken göz önünde bulundurulan diğer seçenekler ve bu seçeneklerin neden seçilmediği açıklanır. Her zaman birden fazla çözüm vardır ve bunların artıları ile eksileri değerlendirilmelidir. Örneğin, “Entity Framework’ü kullanmadık çünkü performans açısından yeterli bulmadık.”

5. Sonuçlar (Consequences): Alınan kararın projeye etkilerini açıklar. Hem olumlu hem de olumsuz sonuçlar burada belirtilir. Örneğin, “Dapper kullanımı daha fazla manuel SQL yazmayı gerektirebilir, bu da uzun vadede bakım maliyetini artırabilir.”

Gerçek Hayattan ADR Örnekleri

Bir mimari karar kaydı yazarken, proje içerisinde aldığımız somut bir kararı ve bu kararı etkileyen faktörleri belgelememiz gerekir. C# ile çalıştığımız bir projede bu süreç nasıl işleyebilir, bir örnek üzerinden bakalım.

Örnek 1: Veritabanı Erişimi için ORM Seçimi

Projemizde veritabanı ile etkileşim kurmamız gerekiyor ve bu noktada bir ORM (Object-Relational Mapping) seçimi yapmamız gerekiyor. Entity Framework mü kullanacağız yoksa daha hafif bir çözüm olan Dapper mı?

1. Title:

ORM Seçimi — Entity Framework vs Dapper

2. Context:

Projemizde veritabanı ile sık sık etkileşim kuracağız. Bu noktada, doğrudan SQL yazmak yerine bir ORM kullanmak istiyoruz. ORM, nesne tabanlı programlama ile veritabanı arasındaki etkileşimi kolaylaştıracak ve daha az hata yapmamızı sağlayacaktır. Ancak kullanacağımız ORM’nin performans açısından proje gereksinimlerine uygun olması gerekiyor.

3. Decision:

Yüksek performans ve esneklik gerektiği için Dapper ORM’ini seçtik. Entity Framework’ün sağladığı change tracker ve daha yüksek soyutlama katmanına ihtiyacımız olmadığı için, daha hafif olan Dapper bizim için daha uygun.

4. Consequences:

Artılar

  • Daha hafif ve hızlı bir ORM olduğu için performans avantajı sağlar.
  • Daha fazla kontrol ve esneklik sağlar.

Eksiler

  • Entity Framework kadar güçlü bir soyutlama sunmaz, manuel SQL yazmamız gerekebilir.
  • Change tracker gibi gelişmiş özellikler Dapper’da bulunmuyor.

Bu örnekte olduğu gibi, karar verme sürecimizi kayıt altına almak hem şu anki hem de gelecekteki ekip üyelerine bu kararın neden alındığını anlatacaktır.

Kod örneği ile olaya yaklaşalım ..

Dapper ORM’yi seçtiğimizi düşünelim ve bunun nasıl kullanıldığını basit bir örnekle gösterelim. Bir ürün tablosundan veri çekeceğimiz basit bir sorgu düşünelim.

C# Dapper Örneği

using System.Data.SqlClient;
using Dapper;
using System.Collections.Generic;

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}

public class ProductRepository
{
private string _connectionString = "connstr";

public IEnumerable<Product> GetAllProducts()
{
using (var connection = new SqlConnection(_connectionString))
{
string query = "SELECT Id, Name, Price FROM Products";
return connection.Query<Product>(query);
}
}
}

Yukarıdaki kodda Dapper kullanarak Products tablosundan tüm ürünleri çeken basit bir sorgu gerçekleştirdik. Bu, Dapper’ın basit ve hızlı bir şekilde veritabanı işlemlerini yapmamıza olanak tanıyan bir örneğidir.

[
{ "Id": 1, "Name": "Product 1", "Price": 100.00 },
{ "Id": 2, "Name": "Product 2", "Price": 200.00 },
{ "Id": 3, "Name": "Product 3", "Price": 300.00 }
]

Alternatif Bir Karar: Entity Framework Kullanımı

Aynı sorunu Entity Framework kullanarak çözmek de mümkündür. Entity Framework’ün avantajı, change tracker ve daha fazla soyutlama sunmasıdır. Eğer projemiz daha karmaşık bir yapıda olsaydı, Entity Framework kullanmayı tercih edebilirdik.

using System.Collections.Generic;
using System.Linq;

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}

public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}

public class ProductRepository
{
private readonly AppDbContext _context;

public ProductRepository(AppDbContext context)
{
_context = context;
}

public List<Product> GetAllProducts()
{
return _context.Products.ToList();
}
}

Bu çözümde, Entity Framework kullanarak aynı işlemi gerçekleştirdik. Entity Framework, veritabanı bağlantılarını ve SQL sorgularını daha fazla soyutlayarak geliştiriciye daha temiz bir arayüz sunar.

[
{ "Id": 1, "Name": "Product 1", "Price": 100.00 },
{ "Id": 2, "Name": "Product 2", "Price": 200.00 },
{ "Id": 3, "Name": "Product 3", "Price": 300.00 }
]

Örnek 2: API Tasarımı

Bir projede API tasarımı yaparken RESTful API mı yoksa GraphQL mi kullanılacağına karar vermeniz gerekebilir. Bu kararı bir ADR olarak belgeleyebilirsiniz.

Örnek 3: Loglama Stratejisi

Projenizde loglama için hangi framework’ü kullanacağınıza karar verirken (örneğin, Serilog mu yoksa NLog mu) bu kararı bir ADR ile kaydedebilirsiniz.

ADR’leri Kayıt Altına Almanın Yöntemleri

1. Version Control (Git gibi) ile Kayıt Tutma

ADR’ler, yazılım geliştirme sürecinde kullanılan version control systems (VCS) ile kayıt altına alınabilir. Özellikle Git gibi sürüm kontrol sistemlerinde her karar bir metin dosyası olarak saklanabilir.

Avantajlar

  • Projeye entegre bir şekilde, sürüm kontrolü altında tutulur.
  • Her değişiklik izlenebilir ve tarihçesi saklanır.
  • Erişim ve yönetimi kolaydır, özellikle teknik ekipler için.

Dezavantajlar

  • Teknik olmayan paydaşlar için erişimi ve anlaşılması zor olabilir.
  • Git kullanımı bilmeyen ekip üyeleri için ek bir öğrenme gerektirebilir.

Örnek: Projenizin kök dizininde bir “adr” klasörü oluşturup her kararı ayrı bir markdown dosyası olarak saklayabilirsiniz:

/adr
001-database-choice.md
002-logging-strategy.md

2. Documentation ve Wiki Tabanlı Sistemler

Eğer teknik olmayan paydaşlar da bu kararlara erişebilecekse, Confluence, Google Docs veya bir Wiki sistemi kullanmak faydalı olabilir. Bu platformlar sayesinde ekip üyeleri kararları kolayca okuyabilir ve güncelleyebilir.

Avantajlar

  • Teknik olmayan ekip üyeleri için erişim ve kullanım daha kolaydır.
  • Kararların aranabilir, düzenlenebilir ve herkes tarafından görüntülenebilir olması sağlanır.

Dezavantajlar

  • Version control sistemleri kadar güçlü sürüm takibi sağlamaz.
  • Belge güncellemeleri düzenli yapılmadığında, kararlar eski kalabilir.

3. Proje Yönetim Araçlarıyla Kayıt Tutma

ADR’ler, Jira, Trello gibi proje yönetim araçları ile kart veya görev olarak kaydedilebilir. Bu, kararların proje planlamasının bir parçası olarak görünmesini ve görevlerle ilişkilendirilmesini sağlar.

Avantajlar

  • Kararlar, proje görevleriyle entegre edilir.
  • Tüm ekip üyeleri, teknik veya teknik olmayan kişiler dahil, kararları görebilir ve takip edebilir.

Dezavantajlar

  • Version control sistemleri kadar güçlü bir versiyon takibi sağlamaz.
  • Kararların düzenli takibi ve yapısal bir belge formatı olmaması karmaşaya neden olabilir.

4. Specialized Tools (ADR Tools gibi)

ADR Tools gibi özel yazılımlar, bu süreci otomatikleştirmek ve daha kolay yönetilebilir hale getirmek için kullanılabilir. Ayrıca, bu araçlar Git gibi version control sistemleriyle de entegre çalışabilir.

Avantajlar

  • ADR’leri oluşturmayı ve takip etmeyi otomatik hale getirir.
  • Version control sistemleri ile entegre edilerek değişiklik takibi yapılabilir.

Dezavantajlar

  • Ek bir yazılım öğrenme ve yönetme gerektirir.
  • Kapsamlı projelerde bu araçların kullanımı sınırlı olabilir.

5. Codebase İçinde Yorumlar ve Annotations Kullanarak Kayıt Tutma

Bazı durumlarda mimari kararlar doğrudan kod içerisine yerleştirilmiş annotations veya comments ile belgelenebilir. Bu yöntemle, bir kararın kodun nasıl çalıştığını doğrudan etkilediği durumlar şeffaf bir şekilde görülebilir.

Avantajlar

  • Kararın kodla nasıl ilişkili olduğunu doğrudan görmek mümkündür.
  • Kodu inceleyen her geliştirici, kararın gerekçesini ve etkilerini kolayca görebilir.

Dezavantajlar

  • Kararların merkezi bir kayıt altında olmaması, izlenebilirliği zorlaştırabilir.
  • Tüm kararların kod içinde yönetilmesi, büyük projelerde karmaşıklığa neden olabilir.

Kod içerisinde ADR örnekleri yapalım.

Örnek: ORM Seçimi Hakkında Karar (Comment)

Bir projede ORM olarak Dapper kullanımı kararını belgelemek için kod içerisinde açıklayıcı bir comment yazılabilir. Bu kararın neden alındığını ve hangi alternatiflerin değerlendirildiğini belirten bir comment ekleyebilirsiniz.

using System.Data.SqlClient;
using Dapper;
using System.Collections.Generic;

public class ProductRepository
{
private string _connectionString = "connstr";

// Decision: We chose Dapper over Entity Framework for our ORM
// Reason: Dapper provides better performance for our specific use case where we need fast, lightweight data access
// Alternatives: Entity Framework was considered but was too heavy for our needs
// Consequences: Developers will need to manually write SQL queries, which may require more attention to detail

public IEnumerable<Product> GetAllProducts()
{
using (var connection = new SqlConnection(_connectionString))
{
string query = "SELECT Id, Name, Price FROM Products";
return connection.Query<Product>(query);
}
}
}

Bu comment bloğu, ORM seçimi ile ilgili alınan kararın detaylarını içerir. Kararın bağlamı, değerlendirilmiş alternatifler ve sonuçlar doğrudan kodun yanında yer alır. Böylece bir geliştirici bu kodu incelediğinde neden Dapper’ın seçildiğini ve bu kararın ne anlama geldiğini hızlıca anlayabilir.

Örnek: Caching Stratejisi Hakkında Karar (Comment)

Bir başka örnek, bir caching stratejisi ile ilgili alınan mimari kararı belgelerken görülebilir. Örneğin, Redis Cache kullanma kararını kod içinde yorumlayabiliriz.

public class CacheService
{
private readonly IDistributedCache _cache;

// Decision: Using Redis as our distributed cache system.
// Reason: Redis offers low-latency caching, which is essential for our application's performance requirements.
// Alternatives: In-memory caching (not distributed), Memcached were considered.
// Consequences: Need to manage Redis infrastructure, but performance benefits outweigh this cost.

public CacheService(IDistributedCache cache)
{
_cache = cache;
}

public async Task<string> GetCachedValueAsync(string key)
{
return await _cache.GetStringAsync(key);
}
}

Bu örnekte, Redis kullanma kararı, kod içerisinde bir comment ile detaylandırılmıştır. Bu sayede, gelecekteki geliştiriciler sadece kararın kendisini değil, aynı zamanda alternatiflerin neden uygun olmadığını ve Redis’in avantajlarını da anlayabilir.

Örnek: Dependency Injection Yapısına Yönelik Karar (Comment)

Dependency Injection (DI) kullanarak servisleri yönetme kararı hakkında bir comment eklenebilir.

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Decision: Implementing Dependency Injection to manage service lifetimes and dependencies.
// Reason: Promotes loose coupling and better testability in our services.
// Alternatives: Manually managing service instances was considered but would lead to tight coupling.
// Consequences: Developers must be familiar with DI patterns; potential overuse of DI could complicate the architecture.

services.AddTransient<IProductService, ProductService>();
services.AddSingleton<ICacheService, RedisCacheService>();
}
}

Bu comment, DI yapısının neden kullanıldığını, hangi alternatiflerin düşünüldüğünü ve bu yapının proje üzerindeki olası sonuçlarını açıklar.

Gördüğünüz gibi, bir mimari karar kaydı, projeyi uzun vadede daha sürdürülebilir ve anlaşılır kılmak için oldukça faydalıdır. Ayrıca, C# ile gerçek hayattan incelediğimiz örnekler, ADR’lerin somut kullanımını da göstermektedir. Bir projede önemli bir karar aldığınızda, bu kararı mutlaka ADR formatında belgeleyerek hem kendiniz hem de ekip arkadaşlarınız için gelecekte büyük bir kolaylık sağlayabilirsiniz.

Makalenin başında bahsettiğim ‘Hocam, siz kullanıyor musunuz?’ sorusuna gelecek olursak, bu standartları elimden geldiğince uygulamaya çalışıyorum, ancak kullandığım çok az yer oldu. Makalede bahsettiğim dezavantajlar benim de zaman zaman karşıma çıkıyor, fakat bunun bir bahane olmaması gerektiğini düşünüyorum. Özellikle kalabalık ekipleri yöneten mimarların veya takım liderlerinin bu yaklaşımı benimsemelerinin, ekip içinde oluşabilecek çatışmaları da önleyeceğini düşünüyorum.

Kaynaklar

Makaleyi faydalı bulduysanız takip ederek destek olabilirsiniz 🙏

Bir sonraki yazıda görüşmek üzere 😊

--

--