Mert Özen Her Satırda Daha İleri
SEO ve GEO Content SEO — #07

İçerik Bakımı: Content Decay, Güncelle–Birleştir–Sil Kararı ve Sürüm Bağımlı Yazıları Yaşatmak

Mert Özen 14 Kas 2026 12 dk 2 görüntülenme
İçerik Bakımı: Content Decay, Güncelle–Birleştir–Sil Kararı ve Sürüm Bağımlı Yazıları Yaşatmak

Content decay, bir sayfanın arama trafiğinin aylar içinde düşmesidir ve çözümü tarihi güncellemek değil, nedeni ayırıp güncelle, yeniden yaz, birleştir ya da kaldır kararını vermektir. Search Console'da yıllık karşılaştırmayla decay'i tespit etmeyi, .NET 10 ile sinyal çıkarmayı, .NET 11 çıkarken sürüm bağımlı yazıları yaşatmayı, dateModified'ı dürüst kullanmayı, spam policy sınırlarını ve annotation ile ölçmeyi anlatıyoruz.

Sürüm notu: Bu yazıdaki kodlar .NET 10 ile derlenip denenmiştir; Google'ın trafik düşüşü, yayın tarihi, sitemap ve spam policy dokümanlarının Kasım 2026 hâlini ve .NET 11'in 10 Kasım 2026'da genel kullanıma açılan (GA) sürümünü esas alır.

Önce Tahmin Et

.NET 11, 10 Kasım 2026'da çıktı. Bookstore blog'unda .NET 10 ile yazılmış yüzlerce yazı var ve Entity Framework Core (EF Core) migration rehberinin tıklamaları geçen yılın aynı dönemine göre %35 düşmüş. Ekipten iki öneri geliyor: (1) bütün yazıların başlığındaki ".NET 10"u ".NET 11" yapalım ve tarihlerini bugüne çekelim; (2) trafiği düşen yazıları toplu olarak silelim, "site temizlensin". Sence bu önerilerden hangisi rehberin trafiğini geri getirir? Hangisi sitenin bütününe zarar verir? Ve %35'lik düşüşün nedeni gerçekten içeriğin eskimesi mi?

Content Decay Nedir ve Nasıl Tespit Edilir?

Content decay, bir sayfanın arama trafiğinin aylar içinde yavaş yavaş düşmesi. Teknik blog'da dört yaygın nedeni var: sorgunun ilgisi başka bir yere kayar (okurlar artık eski sürümü değil yenisini arıyor), daha güncel ya da daha iyi sayfalar öne geçer, sonuç sayfası değişir (bir AI Overview veya video kutusu tıklamaları azaltır) ya da sayfanın içeriği eskir (komutlar, API'ler, ekran adları değişmiştir).

Her düşüş decay değil. Google'ın arama trafiğindeki düşüşleri inceleme dokümanı başka nedenler de sayıyor ve bunların çoğu SEO (arama motorları için yapılan optimizasyon çalışması) işinin içerik dışındaki tarafına ait: algoritma güncellemeleri, crawl ve index sorunları, güvenlik sorunları, spam ihlalleri, mevsimsellik, site taşıma ve raporlama hataları. Bu yüzden ilk adım, düşüşün sayfaya mı, siteye mi ait olduğunu ve mevsimsel olup olmadığını ayırmak.

Dokümanın Search Console için önerdiği yöntem bu yazının da temeli. Önce 16 aylık aralığa bakıp düşüşün her yıl aynı dönemde tekrar edip etmediğini gör. Sonra son üç ayı bir önceki dönemle ya da geçen yılın aynı dönemiyle karşılaştır; mevsimselliği elemek için ikincisi daha güvenilir. Pages tablosunu tıklama farkına göre sırala ve en çok kaybeden sayfalardan başla. Grafiğe de bak: impression ve tıklama birlikte düştüyse sorun görünürlükte; yalnız tıklama düştüyse, doküman title ve snippet'in daha az ilgi çekici hâle gelmiş olabileceğini ya da rakiplerin daha zengin sonuçlar gösterdiğini söylüyor. Son olarak Google Trends'te sorgunun ilgisinin genel olarak düşüp düşmediğine bak.

Kodla Sinyal Çıkarmak

Sayfa başına bu karşılaştırmayı her ay elle yapmak yerine, iki dönemi yan yana koyan küçük bir program yazalım. Girdi, bulk export'tan sayfa bazında toplanmış iki dönem: geçen yılın ağustos–ekim dönemi (prev) ve bu yılın aynı ayları (curr). Search Console verisi 16 ay geriye gittiği için bu aralık kullanılabilir. Satırlar Bookstore blog'u için hazırlanmış örnek veri; gerçek bir sitenin rakamları değil.

url,period,impressions,clicks,sum_position
/blog/ef-core-migration-rehberi,prev,5200,410,16640
/blog/ef-core-migration-rehberi,curr,5050,265,34845
/blog/ef-core-8-yenilikleri,prev,3900,350,6240
/blog/ef-core-8-yenilikleri,curr,1150,96,2415
/blog/dotnet-6-minimal-api,prev,2600,190,11440
/blog/dotnet-6-minimal-api,curr,480,11,5616
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
string path = args.Length > 0 ? args[0] : "gsc-pages-yoy.csv";

// url,period,impressions,clicks,sum_position — period: "prev" (geçen yıl) ya da "curr" (bu yıl), aynı aylar
Dictionary<string, Dictionary<string, PeriodStats>> pages = (await File.ReadAllLinesAsync(path))
    .Skip(1)
    .Select(line => line.Split(','))
    .GroupBy(f => f[0])
    .ToDictionary(g => g.Key, g => g.ToDictionary(f => f[1], f => new PeriodStats(int.Parse(f[2]), int.Parse(f[3]), long.Parse(f[4]))));

Console.WriteLine($"{"url",-36}{"clicks",-10}{"Δ",6}{"impr Δ",8}  {"position",-12}signal");
foreach ((string url, Dictionary<string, PeriodStats> p) in pages.OrderBy(x => x.Key))
{
    PeriodStats curr = p["curr"];
    if (!p.TryGetValue("prev", out PeriodStats? prev))
    {
        Console.WriteLine($"{url,-36}{"-> " + curr.Clicks,-10}{"",6}{"",8}  {"-> " + curr.Position.ToString("F1"),-12}NEW (no baseline)");
        continue;
    }

    double clicks = Change(prev.Clicks, curr.Clicks);
    double impressions = Change(prev.Impressions, curr.Impressions);
    double positionDelta = curr.Position - prev.Position;

    // Sinyal karar değildir; yalnız hangi soruyu soracağını söyler
    string signal = (clicks, impressions, positionDelta) switch
    {
        _ when curr.Impressions < 100 && prev.Impressions < 100 => "LOW VISIBILITY -> merge or remove?",
        ( > -0.2, _, _) => "ok",
        (_, <= -0.5, _) => "DEMAND DROP -> query interest moved?",
        (_, _, >= 2) => "RANKING DROP -> refresh?",
        _ => "CTR DROP -> check the SERP",
    };
    Console.WriteLine($"{url,-36}{$"{prev.Clicks}->{curr.Clicks}",-10}{clicks * 100,5:+0;-0}%{impressions * 100,7:+0;-0}%  {$"{prev.Position:F1}->{curr.Position:F1}",-12}{signal}");
}

static double Change(int before, int after) => (after - before) / (double)before;

record PeriodStats(int Impressions, int Clicks, long SumPosition)
{
    public double Position => (double)SumPosition / Impressions + 1;
}

dotnet run çıktısı:

url                                 clicks         Δ  impr Δ  position    signal
/blog/aspnet-core-di-lifetime       300->176    -41%     +4%  3.0->3.1    CTR DROP -> check the SERP
/blog/cs8618-nullable-uyarisi       520->690    +33%    +24%  2.4->2.0    ok
/blog/dependency-injection-nedir    640->655     +2%     +4%  4.0->3.8    ok
/blog/dotnet-6-minimal-api          190->11     -94%    -82%  5.4->12.7   DEMAND DROP -> query interest moved?
/blog/ef-core-10-yenilikleri        -> 310                    -> 2.5      NEW (no baseline)
/blog/ef-core-8-yenilikleri         350->96     -73%    -71%  2.6->3.1    DEMAND DROP -> query interest moved?
/blog/ef-core-migration-rehberi     410->265    -35%     -3%  4.2->7.9    RANKING DROP -> refresh?
/blog/kitap-onerileri-2023          3->1        -67%    -43%  18.0->22.5  LOW VISIBILITY -> merge or remove?

Program karar vermiyor, her sayfa için hangi soruyu soracağını söylüyor. Satırları okuyalım:

ef-core-migration-rehberi: impression neredeyse aynı, sıra 4,2'den 7,9'a düşmüş, tıklama %35 eksik. Okurlar hâlâ aynı şeyi arıyor ama başka sayfalar öne geçmiş. Klasik bir "güncelle" adayı: sayfanın komutları ve örnekleri güncel mi, rakip sayfalarda olup bu sayfada olmayan bir şey var mı?

aspnet-core-di-lifetime: sıra ve impression yerinde, tıklama %41 düşmüş. Bu decay değil, sonuç sayfası değişimi. Sorguya gizli pencerede bakmak gerekiyor: bir AI Overview mı çıkmaya başladı, rakipler daha zengin bir görünüm mü aldı, yoksa bizim title'ımız mı eskidi?

ef-core-8-yenilikleri ve dotnet-6-minimal-api: impression %70–80 düşmüş. Okurlar bu sürümleri artık aramıyor. Bu iki sayfanın durumu farklı ve aşağıda sürüm bağımlı içerik başlığında ayrı ayrı ele alıyoruz.

kitap-onerileri-2023: iki dönemde de 100'ün altında impression. Bu, serinin dördüncü yazısında gördüğümüz thin sayfa; birleştirme ya da kaldırma adayı.

ef-core-10-yenilikleri: yeni sayfa, geçen yıl verisi yok; karşılaştırma için bir yıl beklemek ya da yayından sonraki ilk üç ayı referans almak gerekiyor.

Karar Ağacı: Güncelle, Yeniden Yaz, Birleştir, Kaldır

Sinyalden karara geçerken her sayfa için aynı soruları sor: Okurun bu sayfadaki sorusu hâlâ var mı? Sayfa bu soruya bugün doğru cevap veriyor mu? Sitede aynı soruya cevap veren başka bir sayfa var mı? Cevaplara göre dört seçenek kalıyor:

DurumKararTeknik adım
Soru var, sayfa kısmen eskimişGüncelle: komutları, örnekleri, ekran adlarını yenileAynı URL; görünür "Güncellendi" tarihi ve dateModified
Soru var, sayfanın yaklaşımı artık yanlışYeniden yazAynı URL, yeni içerik; değişikliği sayfada açıkça belirt
Aynı soruya cevap veren daha iyi bir sayfa varBirleştirİçeriği güçlü sayfaya taşı, eski URL'yi 301 ile yönlendir
Soru yok ve sayfanın karşılığı da yokKaldır404 veya 410; sitedeki link'leri temizle

Kaldırma son seçenek. 404 ile 410 arasındaki farkı ve Google'ın bunları nasıl yorumladığını Teknik SEO serisinin HTTP status code yazısında, 301'in doğru kurulumunu redirect yazısında anlattık. Bookstore'un ikinci önerisi, trafiği düşen yazıları toplu silmek, bu ağacın en sonundaki seçeneği hiçbir soru sormadan bütün sayfalara uygulamak demek.

Sürüm Bağımlı Yazıları Yaşatmak

Teknik blog'un en özel bakım sorunu sürümler. mertozen.com bunun için bir kural kullanıyor: teknoloji serilerinde önce kararlı bir taban sürüm üzerinde "kemik yapı" yazıları gelir, sonraki sürümlerin getirdikleri ayrı "sürümlerle gelen yenilikler" yazılarında anlatılır. Bugün taban .NET 10; LTS (uzun süreli destek) sürümü ve Kasım 2028'e kadar destekleniyor. .NET 11 10 Kasım 2026'da çıktı ve STS (standart süreli destek) sürümü olarak iki yıl destek alıyor. Bu kurala göre .NET 11'in çıkması, yüzlerce kemik yazının yeniden yazılması demek değil; .NET 11'in getirdiklerini anlatan yeni yazılar ve gerektiği yerde kemik yazılara "bu konuda .NET 11'de şu değişti" diye bir paragraf ve link demek.

Bu model iki tür sayfa üretiyor ve ikisinin bakımı farklı. Kemik yazının başlığında sürüm yok ve sorgusu kalıcı ("ef core migration nasıl yapılır"); bu sayfalar güncellenerek yaşatılır. Sürüm yazısının başlığında sürüm var ve sorgusu o sürüme bağlı ("ef core 8 yenilikleri"); bu sayfalar o sürümün kaydı olarak kalır. Örnekteki iki düşüşü bu ayrımla okuyalım:

ef-core-8-yenilikleri: .NET 8 ve EF Core 8'in desteği 10 Kasım 2026'da bitti, okurlar artık EF Core 10'u arıyor. Sayfa yanlış değil, yalnız tarihsel. Doğru adım sayfayı silmek ya da başlığını "EF Core 10" yapmak değil; sayfanın başına güncel sürüm yazısına link veren kısa bir not eklemek ve sayfayı kayıt olarak bırakmak. Sürümü değiştirip içeriği bir sonraki sürüme uyarlamak, eski sürümü arayan az sayıdaki okuru da kaybettirir ve iki sürüm sayfasını aynı sorguda yarıştırır.

dotnet-6-minimal-api: bu farklı bir durum. .NET 6'nın desteği Kasım 2024'te bitti ve sayfa aslında bir sürüm yazısı değil, sürümü başlığına almış bir kemik yazı. Sitede aynı konuyu .NET 10 ile anlatan bir yazı varsa doğru adım birleştirmek ve eski URL'yi 301 ile yönlendirmek; yoksa yazıyı .NET 10'a güncellemek. URL'de sürüm olduğu için güncellemede yeni bir URL'ye taşımak ve eskisini yönlendirmek daha temiz. Bu, kemik yazılarda URL'ye ve başlığa sürüm koymamanın neden önemli olduğunu da gösteriyor.

"Güncellendi" Notu ve dateModified

Google'ın yayın tarihi dokümanı, sayfada açıkça etiketlenmiş görünür bir tarih (yayın tarihi, güncelleme tarihi veya ikisi), structured data'da datePublished ve dateModified ve bu ikisinin birbiriyle tutarlı olmasını öneriyor. Sitemap dokümanı da lastmod'u ancak tutarlı ve doğrulanabilir biçimde doğruysa kullandığını, değerin ana içerikte, structured data'da veya link'lerde yapılan anlamlı son değişikliği yansıtması gerektiğini söylüyor; footer'daki telif yılını güncellemek bu anlamda bir değişiklik değil. Serinin ilk yazısında gördüğümüz people-first rehberi ise içerikte esaslı bir değişiklik yapmadan tarihi değiştirmeyi doğrudan bir uyarı işareti olarak sayıyor.

mertozen.com'un bugünkü durumu bu açıdan iyi bir örnek. JSON-LD'deki dateModified ve sitemap'teki lastmod CMS'in (içerik yönetim sistemi) güncelleme zamanından geliyor ve kod dateModified'ın yayın tarihinden önce olmamasını garanti ediyor. Ama sayfada yalnız yayın tarihi görünüyor; yani okur ve structured data farklı bilgi alıyor. Ayrıca CMS'in güncelleme zamanı her kayıtta ilerlediği için bir yazım hatası düzeltmesi de dateModified'ı ve lastmod'u ileri taşıyor. Dürüst bir düzen için iki adım yeterli: CMS'e editörün yalnız esaslı değişikliklerde güncellediği ayrı bir "içerik güncellendi" alanı eklemek ve bu alan doluysa sayfada "Güncellendi: ..." etiketiyle göstermek; dateModified ve lastmod da bu alandan beslensin.

Bookstore'un birinci önerisi bu yüzden iki kez yanlış: başlığa sürüm adı yazıp tarihi çekmek, içerik değişmeden tazelik göstermek ve sürüm bağımlı olmayan kemik yazıları sürüm bağımlı hâle getirmek demek.

Spam Policy Sınırları

İçerik bakımı yaparken üç spam policy'ye özellikle dikkat etmek gerekiyor, çünkü üçü de "eski siteyi canlandırma" fikirleriyle karışabiliyor.

Scaled content abuse. Eski yazıları AI ile toplu olarak yeniden ürettirip okura bir şey katmadan yeniden yayımlamak, policy'nin tarif ettiği şey: nasıl üretildiğinden bağımsız olarak, az değer katan çok sayıda sayfa. Güncellemenin ölçütü değişen kelime sayısı değil, okurun sorusuna verilen cevabın iyileşmesi.

Site reputation abuse. Sitenin kazandığı itibarı kullanmak için, sitenin konusuyla ilgisi olmayan üçüncü taraf içeriği (sponsorlu yorumlar, kupon sayfaları gibi) yayımlamak. Google 28 Ağustos 2026'da bu policy'nin AEA'daki (Avrupa Ekonomik Alanı) uygulamasını değiştirdi: AEA'daki kullanıcılar için manual action'ın etkisi uygulanmıyor, etkilenen bölüm zamanla sitenin geri kalanından bağımsız sıralanabilecek şekilde ayrılabiliyor; policy'nin tanımı ise değişmedi. Teknik blog'a "konuk yazı" alırken soru, içeriğin sitenin okuruna hizmet edip etmediği.

Expired domain abuse. Süresi dolmuş bir domain'i satın alıp eski itibarından yararlanmak için az değer katan içerikle yeniden kullanmak. Bakım bağlamında bu, eski bir proje domain'ini alıp kendi blog'una yönlendirmeyi ya da orada ilgisiz içerik yayımlamayı düşünürken akılda tutulmalı.

Ölçmek: Annotation ve Önce/Sonra Karşılaştırması

Bir güncellemenin işe yarayıp yaramadığını bilmenin tek yolu ölçmek. Search Console Kasım 2025'ten beri Performance grafiğine özel annotation eklemeye izin veriyor: grafiğe sağ tıklayıp bir tarih ve en fazla 120 karakterlik bir not ekleyebiliyorsun. Notu property'ye erişimi olan herkes görüyor, bu yüzden kişisel bilgi yazmamak gerekiyor. Google'ın önerdiği örnekler arasında template değişiklikleri, site taşıma ve içeriğin farklı bir niyete göre yeniden düzenlenmesi var.

Önce/sonra karşılaştırmasında üç kural: dönemler aynı uzunlukta olsun, değişiklikten sonra birkaç hafta bekle (Google'ın trafik dokümanı da değişikliklerin etkisinin günlerden aylara uzanabileceğini söylüyor) ve mümkünse değişiklik yapılmayan benzer sayfaları kontrol grubu olarak aynı dönemde izle. Kontrol grubu da aynı oranda düştüyse, düşüşün nedeni senin değişikliğin değil. Ölçüm tarafının derinliği, yani GA4 (Google'ın web analitik aracı Google Analytics 4) ile Search Console'u birleştirmek ve etkiyi istatistiksel olarak okumak, bu turda yazılan Analytics ve Ölçüm serisinin konusu.

Üretimde Dikkat

Denetimi takvime bağla. Decay raporunu ayda bir, büyük sürüm çıkışlarından sonra da ayrıca çalıştır; sonuçları bir içerik denetim tablosunda (sayfa, sinyal, karar, sorumlu, tarih) tut.

Verini 16 aydan uzun sakla. Yıllık karşılaştırma ve sürüm döngülerini görmek için bulk export ya da API ile kendi deponda geçmiş tut.

İyi performans gösteren URL'yi koru. Güncellemede URL'yi değiştirmek zorunda değilsin ve çoğu zaman değiştirmemelisin; URL değişikliği kaçınılmazsa 301 ve internal link güncellemesi aynı deploy'da olsun.

Toplu işlemleri parça parça yap. Birleştirme veya kaldırma kararlarını küçük gruplarla uygula ve her grubun etkisini ölçtükten sonra devam et.

Sık Düşülen Tuzaklar

Yalnız tarih güncellemek. İçerik değişmeden tarihi ileri almak okuru yanıltır, Google'ın tarih ve sitemap dokümanlarının tarif ettiği tutarlılığı bozar ve people-first rehberinin uyarı işaretlerinden biri.

İyi performans gösteren bir URL'yi değiştirmek. "Slug daha temiz olsun" diye trafik alan bir URL'yi değiştirmek, redirect doğru kurulsa bile bir süre dalgalanmaya yol açar ve kazancı belirsizdir.

Toplu silmenin trafik etkisini ölçmeden yapmak. Tek tek az trafik alan yüzlerce sayfa, toplamda ciddi bir long-tail trafiği taşıyabilir; ayrıca bu sayfalara giden internal link'ler kırılır.

Her düşüşü decay sanmak. Mevsimsellik, bir sonuç sayfası değişimi ya da teknik bir sorun aynı grafiği üretebilir; "güncelle" kararından önce nedeni ayır.

Küçük Bir Deneme

★☆☆ Search Console'da son üç ayı geçen yılın aynı dönemiyle karşılaştır ve Pages tablosunu tıklama farkına göre sırala. En çok kaybeden beş sayfada impression mı, tıklama mı, sıra mı düşmüş?

★★☆ ContentDecay'e, iki dönemin sorgu listesini karşılaştırıp her sayfa için "kaybedilen sorgular" listesini yazdıran bir adım ekle (girdi olarak serinin ikinci yazısındaki sorgu ve URL bazlı export'u kullan).

★★★ Kendi CMS'inde veya bir ASP.NET Core modelinde "içerik güncellendi" alanını ayrı tut: sayfada "Güncellendi" etiketini, JSON-LD'de dateModified'ı ve sitemap'te lastmod'u bu alandan üret; yazım hatası düzeltmesinin bu alanı değiştirmediğini bir testle göster.

Content SEO serisi burada bitiyor. Ne yazacağımıza karar verdik (keyword research ve search intent), yazdıklarımızı organize ettik (content architecture ve internal linking), cevap veren sayfalar yazdık ve onları zaman içinde yaşatmayı gördük. Buradan sonraki doğal adım, bu kararların etkisini daha ayrıntılı ölçmek: bu turda yazılan Analytics ve Ölçüm serisi Search Console ve GA4 verisini birlikte okumayı ele alıyor.