.NET 11 RC1: Kararlı C# Union'ları, Yeniden Üretilebilir İmajlar ve Acele Etmemek
.NET 11 RC1, go-live lisansı ve kararlı C# 15 union'larıyla geldi. Go-live gerçekte ne anlama geliyor, yeniden üretilebilir container imajları neden önemli ve ben ilk olarak nelere bakardım.
Umut Korkmaz
Yayın tarihi: 4 dk okuma
Bu sayfada6
Microsoft, 8 Eylül'de .NET 11 Release Candidate 1 sürümünü yayınladı. Bu ilk release candidate ve go-live destek lisansıyla geliyor, yani Microsoft onu production ortamında destekliyor. Öne çıkan haber şu: C# 15 artık .NET 11 projelerinde varsayılan dil sürümü ve union tipleri için preview flag'ine gerek kalmadı. Zamanla daha çok fark yaratacağını düşündüğüm değişiklik ise daha sessiz: container publish artık yeniden üretilebilir (reproducible) imajlar üretebiliyor.
Production'da .NET 10 çalıştırıyorum. Bu yüzden bu hafta benim için soru RC1'in iyi olup olmadığı değil. Asıl soru, bir ekibin şu anda onunla gerçekte ne yapması gerektiği.
Go-live ne demek, ne demek değil
.NET destek politikası, go-live sürümlerini production'da desteklenen ve genellikle GA'dan hemen önceki release candidate build'leri olarak tanımlıyor. Bu işe yarar bir bilgi, ama desteklenen şey hareket eden bir hedef: aynı sayfa RC1'in kendi destek bitiş tarihini 13 Ekim 2026 olarak gösteriyor. RC1 ile production'a çıkarsanız, ardından gelen her build'i çıktığı anda almayı taahhüt etmiş olursunuz.
Aynı sayfa asıl önemli olan son tarihe de işaret ediyor: .NET 8 ve .NET 9'un desteği 10 Kasım 2026'da sona eriyor, .NET 10 ise 14 Kasım 2028'e kadar LTS. Hâlâ .NET 8 üzerinde servisleriniz varsa, 10 Kasım'dan önce ekibinize borçlu olduğunuz yükseltme bir RC'ye değil, .NET 10'a.
C# 15 union'ları artık preview özelliği değil
RC1 için C# sürüm notlarına göre C# 15, .NET 11'i hedefleyen projelerde varsayılan olarak seçiliyor ve preview döngüsündeki özellikleri kararlı hâle getiriyor: collection expression argümanları, union tipleri, virtual olmayan static interface üyeleri, kapalı sınıf hiyerarşileri, etiketli break ve continue, ve extension indexer'lar.
Domain modelleme şeklimi değiştireceğini düşündüğüm özellik union'lar. Ödeme akışları buna çok uygun, çünkü sonuçlar kapalı bir küme oluşturuyor ve her birinin kendine ait bir işlenme biçimi var:
public record Approved(string TransactionId);
public record Requires3ds(string RedirectUrl);
public record Declined(string Reason);
public union PaymentOutcome(Approved, Requires3ds, Declined);
static IResult ToHttp(PaymentOutcome outcome) => outcome switch
{
Approved(var id) => Results.Ok(id),
Requires3ds(var url) => Results.Redirect(url),
Declined(var reason) => Results.UnprocessableEntity(reason)
};
Bugün bu yapı genellikle bir base class artı bir status enum'ı ya da elle yazılmış bir result tipi olarak yaşıyor. Kendi araçlarını geliştiren ekipler için iki ayrıntı önemli. Kütüphane sürüm notları JSON için closed-type polymorphism ve union desteğini listeliyor; Roslyn de artık ITypeSymbol.UnionCaseTypes API'sini sunuyor, böylece analyzer'lar ve compiler tabanlı diğer araçlar bir union'ın case'lerini görebiliyor. Benim Meridian'da yaptığım gibi bir source generator geliştiriyorsanız, kullanıcılar sormaya başlamadan önce okumanız gereken API bu.
Bir uyarı: Unsafe Evolution çalışması C# 15'ten bağımsız olarak hâlâ bir preview özelliği ve hâlâ <LangVersion>preview</LangVersion> ile birlikte bir feature flag gerektiriyor. "C# 15 kararlı" demek, compiler'daki her şeyin kararlı olduğu anlamına gelmiyor.
Yeniden üretilebilir container imajları
En çok üzerine gideceğim değişiklik bu. SDK sürüm notlarına göre, daha önce aynı uygulamayı iki kez publish etmek farklı imaj digest'leri üretebiliyordu, çünkü zaman damgaları, arşiv header'ları ve dizin listeleme sırası değişkenlik gösteriyordu. Artık SOURCE_DATE_EPOCH değerini sabit bir Unix zaman damgasına ayarlamak, aynı girdilerle yapılan bağımsız publish'lerin aynı digest'i üretmesini sağlıyor:
dotnet publish /t:PublishContainer \
-p:SOURCE_DATE_EPOCH="$(git log -1 --pretty=%ct)"
Commit zamanını kullanmak, zaman damgasının build makinesinin saatinden değil kaynaktan gelmesi demek. Diğer girdiler de sabitlendiğinde bu, "bu imajın o commit'ten geldiğine inanıyoruz" ifadesini ikinci bir pipeline'ın yeniden build edip digest'leri karşılaştırarak doğrulayabileceği bir şeye dönüştürüyor. Tedarik zinciri (supply chain) attestation'larının dayanması gereken şey de tam olarak bu. Ayrıca uzak registry'ye yapılan publish'lerde, hesaplanan manifest hedef repository'de zaten varsa layer işleme adımı atlanıyor, istenen tag'ler ise yine uygulanıyor; ContainerPushNoCache=true bu kontrolü devre dışı bırakıyor.
Pazartesi kontrol edilecek küçük şeyler
- CI yapılandırması. Listelenen bir SDK breaking change:
Configurationortam değişkeni artık--configurationiçin varsayılan değeri sağlıyor. Bu değişkeni ortamda tanımlı bırakan bir pipeline, eskiden Debug build ettiği yerde Release build edebilir.-cparametresini açıkça verin. - SignalR token yenileme. Authentication refresh API'leri kesinleşti: bir client, süresi dolmak üzere olan access token'ı bağlantıyı koparmadan değiştirebiliyor; server tarafında her hub için
EnableAuthenticationRefreshile opt-in yapılıyor, TypeScript client'a isewithAuthenticationRefreshgeliyor. Preview 7'yi denediyseniz, .NET client callback'leriHubConnection.AuthenticationRefreshedveAuthenticationRefreshFailedevent'lerine taşındı. - Test çalıştırmaları. Microsoft.Testing.Platform üzerinde
dotnet test, çalıştırma seviyesinde--timeout(exit code 3) ve--maximum-failed-tests(exit code 13) seçeneklerini ekliyor; ayrıca raporların birbirinin üzerine yazmasını engelleyen--results-directory-layout per-modulegeliyor. - Crash raporları. In-process crash reporter, minidump'lar etkin değilken
DOTNET_EnableCrashReport=1ile artık Linux ve macOS'ta da çalışıyor.
Ben ne yapıyorum
Production .NET 10'da kalıyor. RC1'den istediğim bir deployment değil, bilgi:
- Breaking change'leri erkenden yüzeye çıkarmak için bir branch üzerinde,
-caçıkça ayarlanmış birnet11.0CI adımı eklemek. - Union'ları, sonuçların gerçekten kapalı bir küme olduğu tek bir bounded context içinde denemek ve GA'ya kadar public NuGet API'lerinden uzak tutmak.
SOURCE_DATE_EPOCHadımını container pipeline şablonumuza şimdiden eklemek, böylece geçiş yaptığımız gün hazır olsun.
Go-live, onu production'a çıkarmanıza izin verildiği anlamına gelir. Çıkarmak zorunda olduğunuz anlamına gelmez.
Kaynaklar
Bu yazıda yararlanılan birincil kaynaklar.
- devblogs.microsoft.com/dotnet/dotnet-11-rc-1 (yeni sekmede açılır)
- github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/csharp.md (yeni sekmede açılır)
- github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/libraries.md (yeni sekmede açılır)
- github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/sdk.md (yeni sekmede açılır)
- github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/aspnetcore.md (yeni sekmede açılır)
- github.com/dotnet/core/blob/main/release-notes/11.0/preview/rc1/runtime.md (yeni sekmede açılır)
- dotnet.microsoft.com/platform/support/policy/dotnet-core (yeni sekmede açılır)