<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Kodcular - Medium]]></title>
        <description><![CDATA[“Kodcular, Türkiye’nin En Büyük Medium Yazılım Topluluğudur.” Bu yayın yazılım üzerine üretilen blogların bir araya geldiği bir Türkçe yayındır. - Medium]]></description>
        <link>https://medium.com/kodcular?source=rss----ff2de8699413---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Kodcular - Medium</title>
            <link>https://medium.com/kodcular?source=rss----ff2de8699413---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 12 Nov 2026 19:32:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/kodcular" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Bir Liderin En Önemli Sorusu: Neden?]]></title>
            <link>https://medium.com/kodcular/bir-liderin-en-%C3%B6nemli-sorusu-neden-0beb04226642?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/0beb04226642</guid>
            <category><![CDATA[motivation]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[personal-development]]></category>
            <category><![CDATA[team-management]]></category>
            <dc:creator><![CDATA[Naci Mert ERCAN]]></dc:creator>
            <pubDate>Sun, 01 Nov 2026 12:42:47 GMT</pubDate>
            <atom:updated>2026-11-01T12:42:46.139Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3hTrLGrI2Ww7ftn5BfU2VQ.png" /></figure><p>Bir ekipte motivasyonun düştüğünü fark ettiğimizde ne yapıyoruz?</p><p>Ekip arkadaşlarımızla bire bir görüşmeler yapıyor, onları dinliyor, birlikte kahve molaları veriyor veya yeniden motive olmalarını sağlayacak konuşmalar yapıyoruz. Bunların hepsi önemli. Ancak bazen ne kadar çabalarsak çabalayalım, bir süre sonra aynı motivasyon kaybının tekrar yaşandığını görebiliyoruz.</p><p>Peki, bunun nedeni ne olabilir?</p><p>Bazen sorun, insanların ne yapacaklarını bilmemeleri değil, yaptıkları işe gerçekten inanmamaları olabilir.</p><p>Bir lider olarak yol gösterebilir, karşılaşılan engelleri kaldırabilir ve ekibimize destek olabiliriz. Ancak insanların yaptıkları işin neden önemli olduğunu anlamalarını sağlamak da en az bunlar kadar önemli.</p><h3>Önce Lider İnanmalı</h3><p>Ekibimizin bir hedefe inanmasını bekliyorsak önce kendimize şu soruyu sormalıyız: Biz o hedefe gerçekten inanıyor muyuz?</p><p>Yalnızca teslim tarihlerini takip ederek, görevleri dağıtarak veya hedefleri hatırlatarak insanların aynı heyecanı paylaşmasını bekleyebilir miyiz?</p><p>Bence liderin önce kendisinin yaptığı işe inanması gerekiyor. Üstelik bunu yalnızca konuşarak değil, davranışlarıyla da göstermesi önemli.</p><p>Gerektiğinde ekstra sorumluluk almak, zor zamanlarda ekibin yanında olmak ve yapılan işin neden değerli olduğunu anlatmak, insanların liderlerinden ilham alabilmesini sağlıyor.</p><p>Bir liderin söyledikleriyle yaptıkları birbirini desteklemediğinde, ekibin de anlatılan amaca inanmasını beklemek pek gerçekçi değil.</p><h3>Golden Circle: Neden ile Başlamak</h3><p>Simon Sinek, <em>Start With Why</em> kitabında Golden Circle adını verdiği bir modelden bahsediyor. Bu model üç temel sorudan oluşuyor:</p><ul><li><strong>WHY:</strong> Bu işi neden yapıyoruz? Amacımız ne?</li><li><strong>HOW:</strong> Bu amacı nasıl hayata geçiriyoruz? Hangi değerler ve yaklaşımlarla hareket ediyoruz?</li><li><strong>WHAT:</strong> Sonuç olarak ne yapıyoruz, ne üretiyoruz?</li></ul><p>Sinek’in yaklaşımında ilham veren liderler, iletişime ne yaptıklarını anlatarak değil, neden yaptıklarını açıklayarak başlıyor.</p><p>Bunu ekip yönetimi açısından düşündüğümüzde önemli bir fark ortaya çıkıyor.</p><p>Bir ekip arkadaşımıza hangi görevi tamamlaması gerektiğini ve ne zamana kadar bitirmesini beklediğimizi anlatabiliriz. Ancak bu görevin hangi probleme çözüm getirdiğini, neden önemli olduğunu ve ortaya çıkacak sonucun neyi değiştireceğini anlatmadığımızda, iş yalnızca tamamlanması gereken bir görev olarak kalabiliyor.</p><p>Oysa insanların yaptıkları işi önemsemeleri, o işi sahiplenmelerinin de başlangıcı olabilir.</p><h3>Bir İşi Tamamlamak ile Sahiplenmek Aynı Şey mi?</h3><p>Bir ekip arkadaşımızın kendisine verilen görevi tamamlaması elbette önemli. Ancak o işi gerçekten sahiplenmesi farklı bir şey.</p><p>Yaptığı işin neden önemli olduğunu bilen bir kişi, yalnızca kendisinden bekleneni yerine getirmekle yetinmeyebilir. Karşılaştığı sorunlara çözüm arar, gerektiğinde sorumluluk alır ve yaptığı işin üzerine bir şeyler koymaya çalışır.</p><p>Ancak burada yalnızca işin neden önemli olduğunu anlatmanın yeterli olduğunu düşünmüyorum.</p><p>Bir insan yaptığı işe inanabilir ama kararlarının sürekli sorgulandığı, hata yapmaktan çekindiği veya her adımda onay almak zorunda kaldığı bir ortamda ne kadar sorumluluk alabilir?</p><p>Lider olarak bazen ekip arkadaşlarımızın daha fazla inisiyatif almasını bekliyoruz. Fakat farkında olmadan onların hareket alanını da kendimiz sınırlandırabiliyoruz.</p><p>Bir görev verdiğimizde yalnızca beklediğimiz sonucu mu anlatıyoruz, yoksa o sonuca nasıl ulaşılması gerektiğini de baştan sona tarif ediyor muyuz?</p><p>Bence burada bir denge kurmamız gerekiyor. İnsanların yaptıkları işin neden önemli olduğunu anlamaları bir başlangıç. Ancak bu işi gerçekten sahiplenebilmeleri için onlara güvenmemiz ve kendi kararlarını verebilecekleri alanlar oluşturmamız gerekiyor.</p><h3>Her Sorunun Çözümünü Biz mi Bulmalıyız?</h3><p>Bir ekip arkadaşımız karşılaştığı sorunla bize geldiğinde, çözümü doğrudan söylemek çoğu zaman en kolay yol.</p><p>Elbette her durumda aynı yaklaşımı benimseyemeyiz. Projenin teslim tarihi, işin önceliği ve kişinin yetkinliği nasıl hareket edeceğimizi değiştirebilir.</p><p>Ancak şartlar uygunsa, önce kişinin kendi çözümünü geliştirmesine fırsat vermek gerektiğini düşünüyorum.</p><p>Çünkü bazen bizim aklımıza gelmeyen, hatta bizim tercih edeceğimiz yöntemden daha hızlı ve etkili çözümler ortaya çıkabiliyor.</p><p>Çözümü en baştan tarif ettiğimizde ise ekip arkadaşımız kendi yöntemini geliştirmek yerine bizim anlattığımız yolu takip edebiliyor.</p><p>İş tamamlanıyor. Peki, o kişinin farklı bir çözüm üretme fırsatını da ortadan kaldırmış olabilir miyiz?</p><p>Deneyimli olmak, her zaman en iyi çözümü bizim bildiğimiz anlamına gelmiyor.</p><p>Golden Circle’ın HOW kısmını liderlik açısından düşündüğümüzde, insanlara güvenmek, sorumluluk vermek ve kendi yöntemlerini geliştirmelerine alan açmak da bu yaklaşımın bir parçası olabilir.</p><p>Tabii ki bu, ekibi kendi haline bırakmak anlamına gelmiyor. Gerektiğinde destek olmak, riskleri takip etmek ve doğru zamanda müdahale etmek hâlâ bizim sorumluluğumuz.</p><p>Önemli olan, ne zaman yol göstermemiz ve ne zaman geri çekilmemiz gerektiğini bilmek.</p><h3>Sorumluluk Vermek Kadar, Sorumluluk Almak da Önemli</h3><p>Bir lider olarak ekip arkadaşlarımıza güvenebilir, onlara alan açabilir ve yaptıkları işin neden önemli olduğunu anlatabiliriz. Ancak bütün bunlar, karşı tarafın da aynı sorumluluğu almaya istekli olduğu anlamına gelmiyor.</p><p>Bence burada sorumluluk yalnızca lidere ait değil.</p><p>Bir kişinin yaptığı işin neden önemli olduğunu anlaması, o işi sahiplenmesi için iyi bir başlangıç. Ancak bunun yanında öğrenmeye, çözüm aramaya ve gerektiğinde kendi yöntemlerini sorgulamaya da istekli olması gerekiyor.</p><p>Diyelim ki ekip arkadaşımızın geliştirdiği bir çözüm beklediğimiz gibi çalışmadı.</p><p>Böyle bir durumda çözümü doğrudan devralmak yerine, neden işe yaramadığını birlikte değerlendirmek daha anlamlı geliyor. Hangi noktada sorun olduğunu anlatabilir, farklı bir yaklaşım önerebiliriz. Ancak sonrasında çözümü geliştirme sorumluluğunun yine o kişide kalması önemli.</p><p>Aynı durum, geliştirdiğimiz bir işte sonradan ortaya çıkan hatalar için de geçerli. Her sorunu liderin çözmesi yerine, işi geliştiren kişinin önce sorunu incelemesi ve çözüm üretmeye çalışması gerektiğini düşünüyorum.</p><p>Çünkü sorumluluk almak yalnızca bir görevi tamamlamak değil, o görevle ilgili karşılaşılan problemlerin de arkasında durabilmek.</p><h3>Liderlik Yalnızca İşleri Yönetmek mi?</h3><p>Bir ekibi yönetmekten bahsettiğimizde çoğu zaman aklımıza işlerin planlanması, görevlerin dağıtılması, teslim tarihlerinin takip edilmesi ve ortaya çıkan sorunların çözülmesi geliyor.</p><p>Ancak liderliğin yalnızca bunlardan ibaret olduğunu düşünmüyorum.</p><p>Bir lider olarak ekibimizdeki insanların nasıl geliştiğini de takip etmemiz gerekiyor. Hangi konularda güçlü olduklarını, nerelerde zorlandıklarını, zaman içerisinde ne kadar ilerlediklerini ve hangi alanlarda desteğe ihtiyaç duyduklarını anlamak da sorumluluklarımızın bir parçası.</p><p>Örneğin bir ekip arkadaşımızın birkaç ay önce tek başına çözmekte zorlandığı bir problemi bugün kendi başına çözebildiğini görmek, yalnızca o kişinin değil, ekibin de geliştiğini gösteriyor.</p><p>Bu gelişim her zaman kendiliğinden gerçekleşmiyor. Bazen daha fazla sorumluluk vermek, bazen geri bildirimde bulunmak, bazen de kişinin henüz hazır olmadığı bir konuda daha fazla destek sağlamak gerekiyor.</p><p>Ancak burada kişinin kendi isteğinin de önemli olduğunu düşünüyorum.</p><p>Öğrenmeye meraklı, sorumluluk almaya istekli ve kendini geliştirmek isteyen biriyle bu süreç çok daha hızlı ilerleyebiliyor. Karşılaştığı problemleri yalnızca aşılması gereken engeller olarak değil, yeni şeyler öğrenebileceği fırsatlar olarak da görebiliyor.</p><p>Diğer taraftan, gelişim için yalnızca liderin çaba göstermesi yeterli olmayabiliyor. Ne kadar destek verirsek verelim, kişinin de bu sürecin bir parçası olmayı istemesi gerekiyor.</p><p>Bu nedenle liderin görevi yalnızca gelişim fırsatları sunmak değil, kişinin motivasyonunu, performansını ve ihtiyaçlarını da anlamak. Herkes aynı hızda öğrenmeyebilir veya aynı dönemde aynı motivasyona sahip olmayabilir. Önemli olan, bu farklılıkları görerek doğru zamanda doğru desteği sağlayabilmek.</p><p>Belki de lider olarak yapabileceğimiz en önemli şeylerden biri, insanlara gelişebilecekleri bir ortam sunmak ve bu gelişimin sorumluluğunu onlarla paylaşmak.</p><h3>Peki, Biz Nasıl Bir Ekip Oluşturmak İstiyoruz?</h3><p>Her sorunda liderinin karar vermesini bekleyen bir ekip mi, yoksa karşılaştığı sorunları sahiplenip çözüm üretebilen bir ekip mi?</p><p>Aslında çoğumuz ikinci seçeneği tercih ederiz. Ancak günlük hayatta aldığımız kararlarla her zaman bunu destekliyor muyuz?</p><p>Bir yandan ekip arkadaşlarımızın daha fazla sorumluluk almasını isterken diğer yandan her çözümü kendimiz belirliyorsak, onlara ne kadar alan bırakıyoruz?</p><p>Golden Circle’a yeniden döndüğümüzde, WHY ile başlamak insanların yaptıkları işin amacını anlamalarına yardımcı oluyor. Ancak bu amacı günlük hayata taşıyabilmek için güven, sorumluluk ve gelişim fırsatlarıyla desteklememiz gerekiyor.</p><p>Bir lider olarak yalnızca bugün hangi işleri tamamladığımıza değil, ekibimizin zaman içerisinde nasıl geliştiğine de bakmalıyız.</p><p>Çünkü bir ekibin başarısı yalnızca ortaya çıkardığı sonuçlarla değil, o sonuçlara ulaşırken insanların kazandığı yetkinliklerle de ilgili.</p><p>Belki de lider olarak kendimize daha sık sormamız gereken soru şu:</p><p><strong>Ekibimizin bize daha fazla ihtiyaç duymasını mı sağlıyoruz, yoksa zamanla bize daha az ihtiyaç duyacak kadar gelişmelerine mi yardımcı oluyoruz?</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0beb04226642" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/bir-liderin-en-%C3%B6nemli-sorusu-neden-0beb04226642">Bir Liderin En Önemli Sorusu: Neden?</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[İkinci El Stres:
Başkalarının Duyguları Bize Neden Bu Kadar Hızla Bulaşıyor?]]></title>
            <link>https://medium.com/kodcular/i%CC%87kinci-el-stres-ba%C5%9Fkalar%C4%B1n%C4%B1n-duygular%C4%B1-bize-neden-bu-kadar-h%C4%B1zla-bula%C5%9F%C4%B1yor-1a73948c52dd?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/1a73948c52dd</guid>
            <category><![CDATA[stress]]></category>
            <category><![CDATA[ikincil-stres]]></category>
            <category><![CDATA[uzaktan-çalışma]]></category>
            <dc:creator><![CDATA[Naci Mert ERCAN]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 18:50:47 GMT</pubDate>
            <atom:updated>2026-10-29T18:50:46.101Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Oi0Xn_pNeVziUI9B4i9__Q.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@silverkblack?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Vitaly Gariev</a> on <a href="https://unsplash.com/photos/man-rubbing-his-face-in-front-of-laptop-bl7h_R-PKpU?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Sabah mutlu bir şekilde uyandınız; içinizde tatlı bir enerji, “Bugün işler yolunda gidecek” hissi vardı. İşe geldiğinizde ne yapacağınız belliydi, motiveydiniz. Ofise yayılmış o mis gibi taze kahve kokusu dayanılmaz gelince hemen makinenin başına yöneldiniz.</p><p>Tam o sırada, uzun zamandır aynı şirkette çalıştığınız takım arkadaşınız da oradaydı. Kahvesini alıyordu ama yüzünde alışılmadık bir yorgunluk, hafif bir dert hali vardı.</p><p>Biraz sohbet edince içini döktü: Yöneticisiyle konuşmuş, beklediği terfiyi alamamış. Üstüne bir de başka şirketlerden aldığı iş tekliflerini düşündüğünü söyledi.</p><p>Masanıza geçtiğinizde onun adına gerçekten üzüldünüz. İçinizde ufak bir sıkıntı belirdi. Ama bir yandan da şaşırdınız: Çünkü sizin yöneticinizle böyle bir sorununuz yoktu, üstelik güne gayet keyifli başlamıştınız.</p><p><strong>Peki nasıl oldu da arkadaşınızın yaşadığı hayal kırıklığının tadı sizin ruh halinize de karıştı?</strong></p><p>Aslında hepimizin yaşadığı çok tanıdık bir durum bu. Sabah trafikte yaşadığınız minicik bir gerginliğin gün boyu peşinizi bırakmaması ya da takım liderinizin yaklaşan deadline için yaşadığı paniğin size de bulaşması gibi…</p><p>Aslında yaşadığınız şeyin bir adı var: <strong>İkinci el stres</strong>. Kulağa bir hastalık gibi gelebilir ama değil; bu bir psikolojik bozukluk ya da tıbbi bir teşhis değil. Daha çok duyguların insandan insana nasıl geçtiğini anlatan nörolojik bir durum. Yani birinin yaşadığı kaygı, farkında bile olmadan sizde de karşılık buluyor.</p><h4>İkincil Stresin Etkileri: Neden Bu Kadar Hızlı Bulaşıyor?</h4><p>İkincil stres, çoğu zaman farkında olmadan maruz kaldığımız ve düşündüğümüzden çok daha geniş bir alana yayılan bir etki yaratıyor. Bunun temel nedeni, beynimizin sosyal sinyalleri işleme biçiminde yatıyor. İnsan beyni, özellikle de ayna nöron sistemi, karşısındaki kişinin duygusal durumunu “okumakla” kalmaz, onu otomatik olarak taklit etmeye eğilimlidir. Bu biyolojik eğilim, sosyal uyum için oldukça faydalıdır; ancak yoğun stres yüklü ortamlarda bizi savunmasız hâle getirir.</p><p>Örneğin, ekip arkadaşınız baskı altındaysa, sürekli kötü haber veriliyorsa, müşteri bekliyorsa veya yöneticisinden olumsuz geri bildirim almışsa, bu gerginlik yalnızca ona ait kalmaz. Yüzündeki mikro ifadelerden, ses tonundaki sertlikten, hızlı nefes alışından veya mesajlarında kullandığı kelime seçimlerinden bile stres size taşınabilir. Bazen gün sonunda “Bugün neden bu kadar yorgunum?” diye sorarken, bunun kaynağı doğrudan yaşadığınız bir durum değil; çevrenizden üzerinize geçen stres olabilir.</p><p>İkincil stresin etkileri yalnızca duygu durumumuzu değil, karar alma kapasitemizi, üretkenliğimizi, ilişkilerimizi ve hatta fizyolojik tepkilerimizi de değiştirir. Sanki zihinsel bir ekranın parlaklığı azalır: Daha çabuk sinirlenir, daha zor odaklanır, daha az empati gösterir ve küçük sorunları büyükmüş gibi algılamaya başlarız.</p><p>En tehlikeli yanı ise <strong>normalleşmesi</strong>. Zamanla bu duygu hâli bir arka plan sesi gibi sürekli çalışır ve siz fark etmiyorsunuzdur bile. Bu nedenle ikincil stresi fark etmek, isimlendirmek ve yönetmek; yalnızca bireysel iyilik hâli için değil, ekip dinamikleri ve iş kalitesi için de kritik bir beceri hâline gelir.</p><h4><strong>Çevresel Faktörlerin Etkisi</strong></h4><p>İkinci el stresi tetikleyen bir diğer unsur da <strong>fiziksel çevremiz</strong>. Ortada sizi strese sokacak gerçek bir durum olmasa bile, kaotik, kalabalık ya da sürekli gürültülü bir ortamda bulunmak bile bu stresin size bulaşmasına yetiyor.</p><p>Hatta işin ilginci, birinin stresini hissetmek için o kişiyi görmeniz ya da duymanız bile gerekmiyor; <strong>kokusundan</strong>¹<strong> </strong>bile size geçebiliyor. Araştırmalar, stres altındaki insanların vücutlarının, aynı ortamda bulunan kişiler tarafından fark edilmeden algılanabilen özel bir hormon salgıladığını gösteriyor. Yani yaşadığınız olumsuzluk ve gerginlik, farkında olmadan tüm ortama yayılıyor.</p><p>Yüksek etkileşimin olduğu yaşantımızda diğer insanlara çok fazla maruz kalabiliyoruz. Bu da olumsuz duyguların ve stresin daha da bulaşıcı olmasına sebep oluyor. Sosyal medyada gördüğünüz bir haber gün içinde size ve dolaylı olarak çevrenizdeki kişilere yansıyor. Sürekli bildirim alan, haber tüketen ve dijital yorulma yaşayan bir dünyada duygusal mesafe korumak zorlaştı.</p><h4>İkincil strese nasıl bağışıklık kazanabiliriz?</h4><p>Araştırmalar, strese yönelik daha olumlu bir zihinsel çerçeve benimsendiğinde ve stresle sürekli çatışma hâlinin bırakıldığında, stresin olumsuz etkilerinin %23 oranında azaldığını² ortaya koyuyor. Bu bakış açısını geliştirmek başlangıçta kolay olmayabilir; ancak elde edilen sonuçlar, bu yaklaşımın denenmeye değer ve etkili bir yöntem olduğunu açıkça gösteriyor.</p><p>İkincil strese tamamen “duvar örmek” mümkün olmasa da, ona karşı güçlü bir duygusal bağışıklık sistemi geliştirmek mümkün. Bunun ilk adımı, başkalarının duygularını kendi duygularınız gibi içselleştirdiğiniz anları fark edebilmek.</p><p>Birinin gerginliği size geçtiğinde bedeninizin verdiği sinyalleri — omuzlarda kasılma, nefesin hızlanması, zihnin daralması — yakalamak, bu döngüyü kırmanın en etkili yollarından biridir.</p><p>Sonraki adım ise zihinsel bir mesafe yaratmak: <strong>“Bu duygu bana ait değil.”</strong> diyebilmek. Bu farkındalık, başkalarının stresine otomatik olarak kapılma eğiliminizi belirgin şekilde azaltır.</p><p>Ayrıca gün içinde kısa odaklanma molaları vermek, nefes egzersizleriyle sinir sistemini düzenlemek ve iş ortamındaki duygusal yoğunlukları bilinçli olarak sınırlamak; ikincil strese karşı gerçek bir kalkan oluşturur.</p><p>Başkalarının duygularını anlamaya devam ederken, kendinizi duygusal olarak sağlam bir zeminde tutabilmek ise uzun vadede en etkili bağışıklık yöntemidir.</p><p><strong>Bu noktada tüm bu kavramların benim hayatımdaki karşılığını çalışma düzenim değiştiğinde çok daha net anladım.</strong></p><p>Ben de çalışma hayatımın yaklaşık yedi yılını tam zamanlı ofiste geçirdim. Sonradan fark ettim ki, o dönemde sık sık hissettiğim ama adını bir türlü koyamadığım şey aslında ikinci el stresti. Ofiste fark etmeden yüklendiğim stresin, evden çalışmaya geçtiğimde bir anda azalması aslında bu kavramı daha iyi anlamamı sağladı.</p><p>Son üç yıldır evden çalışırken fark ettiğim en belirgin değişim, yaşadığım olaylara çok daha sakin tepki vermem oldu. Çünkü artık yöneticimin gergin bir sunumunun ortasında kalmıyor, yan masamdaki arkadaşımın aile sorunlarının yarattığı gerginlikten etkilenmiyor, kimsenin stresini istemeden üzerime almıyordum.</p><p>Elbette uzaktan çalışmanın herkes için farklı sonuçları var; kimi için avantaj, kimi için dezavantaj. Ancak en azından benim için bu konu özelinde olumlu tarafı çok daha baskın oldu.</p><p>Bu yazıda bahsettiğim bilgiler ilginizi çekiyorsa, <a href="https://store.hbr.org/books/hbr-emotional-intelligence-series/?srsltid=AfmBOorY7nlD9zN2nuMJVXpn0befpOOM9WSFCauAfmWtcp5ZpL_DraWh">Harvard Business Review’ın Emotional Intelligence Series</a> kitaplarını incelemenizi öneririm. Özellikle ikincil stres konusunda daha derin bir perspektif arıyorsanız, <a href="https://store.hbr.org/product/positivity-and-growth-hbr-emotional-intelligence-series/10762"><em>Positivity and Growth</em></a> kitabındaki ‘Make Yourself Immune to Secondhand Stress and Negativity’ bölümü merak ettiğiniz konuları kapsamlı bir şekilde ele alıyor.</p><p>Sonuç olarak, başkalarının stresinin bize bulaşması doğal; ancak bunu ne kadar taşıyacağımız bizim elimizde. Kendiniz için bir alan açmanız, sadece sizin için değil, çevrenizdekiler için de en değerli iyilik olabilir.</p><p>🧩 Mini Öneriler: İkincil Strese Karşı Günlük Kalkanlar</p><h4>1. Duyguyu Sahiplik Testi Yap</h4><p><em>“Bu duygu bana mı ait, yoksa başkasından mı geliyor?”<br>Günde birkaç kez bu soruyu kendine hatırlatmak, duygusal sınır çizmeye yardımcı olur.</em></p><h4>2. Kısa molalar ver</h4><p><em>2–3 dakikalık kısa yürüyüşler, pencereden dışarı bakmak, derin nefes almak…<br>Bu küçük aralar sinir sistemini sıfırlar.</em></p><h4>3. Tetikleyicileri Tanı</h4><p><em>Kimlerin veya hangi ortamların sende gerginlik yarattığını fark et.<br>Farkında olmak, etkilenmeyi otomatik olarak azaltır.</em></p><h4>4. Dijital Gürültüyü Azalt</h4><p><em>Sosyal medya, Slack, Whatsapp grupları…<br>Yazılan her mesaja hemen dönmek zorunda değilsin öncelikleri yönetebilirsin. Gün içinde bilgi akışını sınırlamak ikincil stresi ciddi oranda azaltır.</em></p><h4>5. Kontrollü Empati Kullan</h4><p><em>Empati kur, ancak duyguyu üstüne alma.<br>“Anlıyorum ama taşımak zorunda değilim” prensibi burada çok işe yarar.</em></p><p>📌 <strong>Kaynakça</strong></p><p><em>¹</em><strong><em>Dalton, P., Mauté, C., Jaén, C., &amp; Wilson, T. (2013).</em></strong><em> Chemosignals of Stress Influence Social Judgments. PLOS ONE, 8(10).<br> </em><a href="https://doi.org/10.1371/journal.pone.0077144"><em>https://doi.org/10.1371/journal.pone.0077144</em></a></p><p><em>² </em><strong><em>Crum, A. J., Salovey, P., &amp; Achor, S. (2013).</em></strong><em> Rethinking Stress: The Role of Mindsets in Determining the Stress Response. Journal of Personality and Social Psychology, 104(4), 716–733.<br></em><a href="https://doi.org/10.1037/a0031201"><em>https://doi.org/10.1037/a0031201</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a73948c52dd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/i%CC%87kinci-el-stres-ba%C5%9Fkalar%C4%B1n%C4%B1n-duygular%C4%B1-bize-neden-bu-kadar-h%C4%B1zla-bula%C5%9F%C4%B1yor-1a73948c52dd">İkinci El Stres:
Başkalarının Duyguları Bize Neden Bu Kadar Hızla Bulaşıyor?</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Korku Beyni Savunmaya Alır, Heyecan Üretime: İş Hayatında Duyguların Performansa Etkisi]]></title>
            <link>https://medium.com/kodcular/korku-beyni-savunmaya-al%C4%B1r-heyecan-%C3%BCretime-i%CC%87%C5%9F-hayat%C4%B1nda-duygular%C4%B1n-performansa-etkisi-241ac5d2bc46?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/241ac5d2bc46</guid>
            <category><![CDATA[ekip-yönetimi]]></category>
            <category><![CDATA[i̇ş-hayatı]]></category>
            <category><![CDATA[motivasyon]]></category>
            <category><![CDATA[liderlik]]></category>
            <category><![CDATA[performans]]></category>
            <dc:creator><![CDATA[Naci Mert ERCAN]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 18:50:39 GMT</pubDate>
            <atom:updated>2026-10-29T18:50:37.887Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8r7HFKst_4cfE8KDGt0aVA.jpeg" /></figure><h3>İş Hayatında Heyecan Kaybı</h3><p>En tehlikeli kayıp, bir çalışanın işten ayrılması değildir.<br>Hâlâ masasında otururken içindeki heyecanın sessizce sönmesidir.</p><p>Okuldan mezun olup ilk işimize başladığımızda iş hayatının dinamiklerini çoğu zaman tam olarak öngöremeyiz. Üniversitede projelerdeki en büyük kısıt genellikle zamandır. Oysa iş dünyasında sorumluluklarımız yalnızca kendi görev tanımımızla sınırlı değildir; ekip dinamikleri, organizasyonel hedefler ve süreç beklentileriyle iç içedir.</p><p>Başlangıçta hevesle katkı sunmak isteyen bir çalışan, fikirleri sürekli geri çevrildiğinde yavaş yavaş savunmaya geçer. Artık yenilik üretmeye değil, hata yapmamaya odaklanır. Görünmez olmak, risk almaktan daha güvenli hissettirmeye başlar.</p><p>Beynimizdeki keşfetme (seeking) sistemi merakı, öğrenme arzusunu ve üretme motivasyonunu tetikler. Ancak sürekli baskı ve yoğun eleştiri, korku sistemini aktive eder. Korku devreye girdiğinde çalışan yeni fikirler denemekten çekinir; risk almak tehdit gibi algılanır. Merak sessizleşir, motivasyon azalır.</p><p>Bu noktada kurumlar potansiyel dolu bir enerjiyi kaybeder.<br>Çalışanlar ise iş saatlerini anlam üretmeden geçirmeye başlar.</p><p>Kaybedilen yalnızca performans değildir. Kaybedilen; enerji, motivasyon ve yapılan işten alınan tatmindir.</p><p><strong>Keşfetme vs. Korku: Beynimizdeki İki Güç ve Farelerden İnsanlara Panksepp’in Öğrettiği Dersler</strong></p><p>Nörobilimci Jaak Panksepp², fareler üzerinde yaptığı çalışmalarla beynimizdeki temel duygusal sistemleri ortaya koymuştur. Bu sistemlerden biri keşfetme (seeking), diğeri ise korkudur.</p><p>Laboratuvarda iki genç fare bir araya getirildiğinde neredeyse her zaman oyun başlar: kovalamaca, güreş, karşılıklı davetler… Araştırmalara göre bu oyun sırasında fareler 50 kHz frekansında cıvıldamalar çıkarır. Bu sesler onların “gülmesi” olarak yorumlanır. Yani oyun anında ölçülebilir bir heyecan ve pozitif uyarılma vardır.</p><p>Oyun aslında bir deneme-yanılma alanıdır. Fareler burada güçlerini, çevikliklerini ve sosyal sınırlarını keşfederler. Benzer şekilde bebekler de dünyayı oyun yoluyla öğrenir; fiziksel ve sosyal becerilerini geliştirir. Çoğu memelide oyun dürtüsü vardır. Oyun, potansiyeli keşfetmenin doğal yoludur.</p><p>Ancak deneyin en çarpıcı kısmı bundan sonradır.</p><p>Araştırmacılar oyun alanına küçük bir kedi tüyü koyarlar. Gerçek bir tehdit yoktur; yalnızca koku vardır. Fakat bu koku farelerin içgüdüsel korku sistemini aktive etmeye yeter.</p><p>Sonuç çarpıcıdır:</p><ul><li>Deney öncesinde beş dakikalık bir seansta ortalama 50 oyun daveti yapılırken,</li><li>Kedi tüyü konduktan sonra bu sayı sıfıra düşer.</li><li>Kontrol grubunda ise oyun davranışı aynı şekilde devam eder.</li></ul><p>Daha da dikkat çekici olan, tüy kaldırıldıktan sonra bile oyunun hemen geri dönmemesidir. Üç gün sonra fareler tekrar oynamaya başlar; ancak hiçbir zaman eski seviyeye tam olarak ulaşamazlar.</p><p>Korku devreye girdiğinde keşif ve oyun sistemi baskılanır ve güven geri gelse bile toparlanmak zaman alır.</p><p>İnsan beyni de benzer bir mekanizmayla çalışır. Korku, merak ve yaratıcılığı anında durdurabilir.</p><h3>Gaz ve Fren: İş Hayatında Duygusal Denge</h3><p>Beynimizdeki keşfetme ve korku sistemini bir arabanın gaz ve fren pedalları gibi düşünebiliriz. Bu analoji Alive at Work¹ kitabında da etkileyici bir şekilde anlatılır.</p><ul><li>Gaz pedalı bizi ileri taşır: yeni fikirler, denemeler, yenilik.</li><li>Fren pedalı bizi korur: risk yönetimi, dikkat, kontrol.</li></ul><p>Her ikisi de gereklidir.</p><p>Bir yazılım ekibini düşünelim. Eğer ekip sürekli baskı altındaysa, hatalardan dolayı eleştiriliyor ve risk almak cezalandırılıyorsa frenler sürekli devrededir. Kimse yeni teknolojileri denemek istemez. Kimse radikal bir fikir önermez. Herkes güvenli alanında kalmayı tercih eder.</p><p>Gaz pedalı çalışsa bile fren basılıyken araç ilerlemez.</p><p>Tersine, güvenli ve destekleyici bir ortamda keşfetme sistemi aktive olur. İnsanlar fikir paylaşır, yeni çözümler dener, öğrenir. Ancak fren tamamen kaldırılırsa bu da kontrolsüz risklere yol açabilir.</p><p>Yüksek performans; korkunun yokluğu değil, keşfetme ile korunma arasındaki sağlıklı dengedir.</p><h3>Liderliğin Asıl Rolü</h3><p>Bu noktada liderliğin rolü kritik hale gelir.</p><p>Etkili bir lider, ekibinin korku sistemini gereksiz yere tetiklemez. Psikolojik güven inşa eder. İnsanların hata yapma korkusu olmadan fikir paylaşabileceği bir alan yaratır.</p><p>Merakı ve heyecanı harekete geçiren liderler ekiplerinde şunları gözlemler:</p><ul><li>Daha fazla yaratıcı çözüm</li><li>Daha hızlı problem çözme</li><li>Daha yüksek iç motivasyon</li><li>Daha sürdürülebilir performans</li></ul><p>Çünkü insanlar korktuklarında korunurlar.<br>Heyecanlandıklarında ise üretirler.</p><p>İş hayatında gerçek kayıp, bir çalışanın ayrılması değildir.<br>Gerçek kayıp, onun hâlâ oradayken içindeki keşfetme isteğinin sönmesidir.</p><h3>Sorumluluk Verdikçe Artan Performans</h3><p>Liderlik yolculuğumda fark ettiğim en net şeylerden biri şu oldu:</p><p>Kontrol ettikçe değil, <strong>sorumluluk verdikçe ekip büyüyor.</strong></p><p>Başta refleksim, kritik işleri kendimden geçirmek, son kararı ben vermek ve riskli gördüğüm noktaları sıkı takip etmekti. Bunun kaliteyi koruyacağını düşünüyordum.</p><p>Ama bir noktada bilinçli bir deneme yaptım.</p><p>Bazı kararları ekip arkadaşlarıma bıraktım.<br>Bazı projelerde yönü ben çizmek yerine çerçeveyi çizip alan açtım.<br>Sadece “nasıl yapılacağına” değil, “neden yaptığımıza” odaklandım.</p><p>Ve şunu gözlemledim:</p><p>Sorumluluk alan insanlar daha fazla sahipleniyor.<br>Sahiplenen insanlar daha yaratıcı düşünüyor.<br>Yaratıcı düşünen insanlar daha yüksek performans gösteriyor.</p><p>En ilginci ise şuydu:<br>Hata oranı artmadı. Aksine, insanlar kendi kararlarının sonuçlarını gördükçe daha dikkatli ve bilinçli hareket etmeye başladı.</p><p>O an şunu anladım:</p><p>Mikro yönetim fren sistemini güçlendiriyor.<br>Yetki ve sorumluluk ise keşfetme sistemini aktive ediyor.</p><p>İnsanlara alan açtığınızda sadece işi devretmiş olmuyorsunuz; aynı zamanda onların potansiyeline güvendiğinizi de göstermiş oluyorsunuz. Bu güven, performansın en görünmez ama en güçlü tetikleyicilerinden biri.</p><h3><strong>Sonuç: Bir Ekibi Hızlandıran Şey Baskı Değil, Meraktır</strong></h3><p>Bugün şunu çok daha net görüyorum: insanlar kapasite eksikliğinden değil, güven eksikliğinden yavaşlıyor.</p><p>Korku düzen sağlar ama potansiyel üretmez.<br>Heyecan ise risk içerir ama değer üretir.</p><p>Bir liderin asıl görevi her şeyi kontrol etmek değil; insanların içindeki keşfetme sistemini canlı tutmaktır.</p><p>Ve liderlik aslında şudur:<br><strong>Ekibin gaz pedalına basmasını sağlamak değil; frene basma ihtiyacı hissetmeden çalışabilecekleri bir ortam yaratmaktır.</strong></p><p>Çünkü bir ekipte performans düşmeden önce merak azalır.<br>Ve liderlik, bu merakı canlı tutabilme sanatıdır.</p><p>📌 <strong>Kaynakça</strong></p><p><em>¹ </em>Cable, Daniel M. <em>Alive at Work: The Neuroscience of Helping Your People Love What They Do</em>. Wiley, 2019. <a href="https://www.oreilly.com/library/view/alive-at-work/9781633697676/">https://www.oreilly.com/library/view/alive-at-work/9781633697676/</a></p><p>²Wright, Jason &amp; Panksepp, Jaak. (2012). An Evolutionary Framework to Understand Foraging, Wanting, and Desire: The Neuropsychology of the SEEKING System. Neuropsychoanalysis. 14. 5–39. 10.1080/15294145.2012.10773683.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=241ac5d2bc46" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/korku-beyni-savunmaya-al%C4%B1r-heyecan-%C3%BCretime-i%CC%87%C5%9F-hayat%C4%B1nda-duygular%C4%B1n-performansa-etkisi-241ac5d2bc46">Korku Beyni Savunmaya Alır, Heyecan Üretime: İş Hayatında Duyguların Performansa Etkisi</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Daha İyi Kod Yazmak Değil, Daha Fazla Etki Yaratmak]]></title>
            <link>https://medium.com/kodcular/daha-i%CC%87yi-kod-yazmak-de%C4%9Fil-daha-fazla-etki-yaratmak-b344ae5f9f59?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/b344ae5f9f59</guid>
            <category><![CDATA[career-development]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[engineering-leadership]]></category>
            <category><![CDATA[teamwork]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Naci Mert ERCAN]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 18:50:23 GMT</pubDate>
            <atom:updated>2026-10-29T18:50:22.009Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bcq9VJNHVBxQSLeopCZn1A.png" /></figure><p>Yazılım kariyerimin büyük bölümünde gelişmenin yolunun daha iyi bir mühendis olmaktan geçtiğini düşündüm.</p><p>Daha temiz kod yazmak, daha karmaşık problemleri çözmek, sistemleri daha iyi tasarlamak, yeni teknolojiler öğrenmek…</p><p>Junior’dan Mid’e, Mid’den Senior’a ilerlerken bunların hepsi beni ileri taşıdı. Fakat bir noktadan sonra bir şeylerin değiştiğini fark etmeye başladım.</p><p><strong>Daha iyi kod yazmak artık tek başına yeterli değildi.</strong></p><p>Çünkü karşıma çıkan problemler artık yalnızca kodla çözülebilen problemler değildi. Bir projenin neden ilerlemediğini anlamak, takım arkadaşımın önündeki engeli kaldırmak, belirsiz bir konuda yön göstermek veya henüz ortaya çıkmamış bir riski fark etmek de işin bir parçasıydı.</p><p>Asıl değişen ise kendime sorduğum soruydu.</p><p><strong>“Bu problemi nasıl çözerim?”</strong></p><p>yerine zamanla:</p><p><strong>“Bu problemi takım olarak daha iyi çözmemizi nasıl sağlayabilirim?”</strong></p><p>diye düşünmeye başladım.</p><p>Kariyerimizin bir noktasından sonra mesele yalnızca kendi çıktımızı artırmak değil, çevremizdeki insanların etkisini de artırabilmek.</p><p>Bunun için benim de çok sevdiğim bir kavram var: <strong>Team Multiplier.</strong></p><p>Bir <em>team multiplier</em> yalnızca kendi üretkenliğine odaklanmıyor. Takım arkadaşlarının önündeki engelleri kaldırıyor, daha iyi kararlar almalarına yardımcı oluyor ve takımın bir bütün olarak daha iyi sonuç üretmesine katkı sağlıyor.</p><p>Kendi deneyimlerime baktığımda bu konuda beş özellik benim için öne çıkıyor:</p><p><strong>Credibility. Reliability. Proactivity. Pragmatism. Resourcefulness.</strong></p><p>Bunları yalnızca Senior veya Lead Developer’ların sahip olması gereken özellikler olarak görmüyorum. Kariyerimizin hangi noktasında olursak olalım, çevremizde yarattığımız etkiyi artırmak için geliştirebileceğimiz özellikler olduğunu düşünüyorum.</p><h3>1. Credibility — Güven Kazanmak</h3><p>Credibility’nin yalnızca teknik olarak doğru kararlar vermekle oluştuğunu düşünmüyorum. Benim için güven, zamanla takım arkadaşlarımla kurduğum ilişkilerde ve özellikle zor anlarda verdiğim tepkilerde oluşmaya başladı.</p><p>Bunu en çok birkaç durumda fark ettim.</p><p>Takım arkadaşlarım bana bir konuda fikir danıştığında kendi fikrimi paylaşsam da son kararı onlara bırakmaya çalışıyorum. Benim önerdiğim yolu seçmelerini beklemek yerine, kendi kararlarını almaları ve uygulamaları için alan tanımanın daha değerli olduğunu gördüm.</p><p>Çünkü birinin sana fikrini sorması, kararı senin vermeni istediği anlamına gelmiyor. Bazen sadece düşüncesini birlikte değerlendirebileceği birine ihtiyaç duyuyor.</p><p>Bir diğer nokta ise bir problem yaşandığında nasıl tepki verdiğimiz.</p><p>Bir hata yapıldığında:</p><p><strong>“Bunu neden böyle yaptın?”</strong></p><p>demek yerine:</p><p><strong>“Ortada bir problem var, bunu beraber çözebiliriz.”</strong></p><p>diyebilmenin çok şeyi değiştirdiğini gördüm.</p><p>Yazılım geliştirirken hata yapmak kaçınılmaz. Böyle anlarda hatayı yapan kişiyi aramak yerine çözümün bir parçası olmaya çalışmak, insanların bir problem yaşadıklarında bunu saklamak yerine daha rahat paylaşmalarını sağlıyor.</p><p>Stresli anlarda verdiğimiz tepkinin de düşündüğümüzden daha önemli olduğunu düşünüyorum. Production’da bir problem olabilir, proje yetişmiyor olabilir veya yanlış bir karar verilmiş olabilir. Böyle zamanlarda sakin kalabilmek ve insanlara karşı aynı şekilde kibar davranmaya devam etmek zaman içerisinde güven oluşturuyor.</p><p>Tanya Reilly’nin <em>The Staff Engineer’s Path</em> kitabını okurken de benzer bir nokta dikkatimi çekmişti. Reilly, credibility’nin yalnızca teknik yetkinlikle oluşmadığından; insanlarla kurduğumuz iletişimin ve zor durumlarda nasıl davrandığımızın da güven üzerinde önemli bir etkisi olduğundan bahsediyor.</p><p>Bu kısmı okuduğumda kendi deneyimlerimle oldukça örtüştüğünü düşündüm. Her şey yolundayken sakin ve destekleyici olmak kolay. İnsanların bize gerçekten güvenmesini sağlayan şeylerden biri, işler ters gittiğinde nasıl davranacağımızı bilmeleri.</p><p><strong>Asıl güven, işler yolunda gitmediğinde insanların bizim nasıl davranacağımızı bilmesiyle oluşuyor.</strong></p><p>Credibility’yi kendim için şöyle özetliyorum:</p><p><strong>“Onun fikrine ve yaklaşımına güveniyorum.”</strong></p><h3>2. Reliability — Sen Orada Olmadığında da Güvenebilmek</h3><p>Credibility ve reliability ilk bakışta birbirine oldukça yakın geliyor. Ben aralarındaki farkı şu iki cümleyle ayırmayı seviyorum:</p><p><strong>Credibility: “Onun fikrine ve yaklaşımına güveniyorum.”</strong></p><p><strong>Reliability: “Ben orada olmasam bile ona güveniyorum.”</strong></p><p>Reliability denildiğinde ilk akla gelen verilen işi zamanında tamamlamak veya verilen sözleri tutmak olabilir. Bunlar tabii ki önemli. Ama benim için zamanla biraz daha farklı bir anlam kazandı.</p><p>Bir sorunun çözüleceği veya yeni bir feature’ın konuşulacağı toplantıyı düşünelim.</p><p>Katılımcıları gördüğümde:</p><p><strong>“X zaten bu toplantıda olacak. Konuyu çok iyi anlayacaktır, benim katılmama gerek yok.”</strong></p><p>diyebiliyorsam, benim için reliability’nin en güzel örneklerinden biri bu.</p><p>Birine gerçekten güvenmek, yalnızca ona bir task verip tamamlamasını beklemek değil. Sen orada olmadığında da konuyu sahipleneceğini, gerekli soruları soracağını ve gerektiğinde aksiyon alacağını bilmek.</p><p>Bu güven her yerde bulunma ihtiyacımızı da azaltıyor. Her toplantıya katılmak, her teknik kararın içinde olmak veya her problemi kendimiz takip etmek zorunda değiliz. Takımdaki insanlara gerçekten güvenebildiğimizde sorumluluğu da paylaşabiliyoruz.</p><p>Reliability’nin değerini işler ters gittiğinde de daha net görüyorum.</p><p>Bir hata veya production problemi yaşandığında, o alanı çok iyi bilmese bile bir takım arkadaşının:</p><p><strong>“Bu benim yaptığım bir şey değil.”</strong></p><p>deyip kenara çekilmek yerine takıma destek olması benim için çok değerli.</p><p>Her zaman çözümü bilmek zorunda değiliz. Bazen doğru kişiye ulaşmak, araştırmaya yardımcı olmak veya sadece problemin çözümünde takımın yanında olmak bile yeterli.</p><p>Bir de çok basit görünen ama benim önem verdiğim bir davranış var: <strong>takip edilmeyi beklememek.</strong></p><p>Bir işi sahiplendiğinde insanların sürekli “Ne oldu?”, “Bir gelişme var mı?” veya “Konuşabildin mi?” diye sormasına gerek kalmadan gelişmeleri paylaşmak. Bir engel veya risk varsa bunu son ana bırakmadan söylemek.</p><p>Sonuçta reliability benim için şu noktaya geliyor:</p><p><strong>Sen orada olmasan bile insanların konunun güvende olduğunu bilmesi.</strong></p><h3>3. Proactivity — Verilen Görevin Ötesini Görebilmek</h3><p>Proaktif olmak bazen yanlış anlaşılıyor.</p><p>Her işe gönüllü olmak, sürekli yeni sorumluluk almak veya karşılaşılan her problemi kendi başına çözmeye çalışmak proaktif olmak gibi görülebiliyor.</p><p>Ben ise biraz farklı düşünüyorum.</p><p><strong>Proaktif olmak, daha fazla iş yapmak değil; verilen görevin ötesine bakıp büyük resmi görebilmek.</strong></p><p>Bir feature geliştirirken sana verilen task’ı tamamlamış olabilirsin. Ama yaptığın değişikliğin başka bir sistemi nasıl etkileyeceğini, başka bir takımın aksiyon alması gerekip gerekmediğini veya bugün aldığımız bir kararın birkaç hafta sonra nasıl bir probleme dönüşebileceğini de düşünmek gerekiyor.</p><p>Aynı şekilde <strong>“Burası beni ilgilendirmiyor.”</strong> demeden önce bir adım daha düşünmek gerektiğine inanıyorum.</p><p>Takım olarak bir sorumluluk aldıysak, yalnızca bize atanmış parçaya değil, ortaya çıkacak sonuca da bakmamız gerekiyor.</p><p>Ama burada önemli bir ayrım var.</p><p><strong>Proaktif olmak, her işi üzerine almak değil.</strong></p><p>Her problemi kendimiz çözmeye çalışırsak bir süre sonra hem odağımızı kaybederiz hem de takımın önünde bir darboğaz haline gelebiliriz.</p><p>Bazen proaktif davranmak problemi kendin çözmek değil, <strong>problemin çözülmesini sağlamak</strong> demek.</p><p>Bunu özellikle belirsizliğin yüksek olduğu işlerde daha fazla hissediyorum.</p><p>Yeni bir proje veya feature geldiğinde requirement’lar eksik olabilir, başka takımlara bağımlılıklar olabilir veya bazı teknik kararlar henüz alınmamış olabilir.</p><p>Böyle zamanlarda <strong>“Bunlar netleştiğinde başlayabiliriz.”</strong> deyip beklemek yerine belirsizliği biraz sahiplenmek gerekiyor.</p><p>Neler net değil? Hangi soruların cevaplanması gerekiyor? Kimlerle konuşmalıyız? Başka bir takımın desteğine ihtiyacımız var mı?</p><p>Bazen yapılması gereken tek şey doğru insanları bir araya getirmek oluyor.</p><p>Bütün cevapları bilmek gerekmiyor. Ama hangi soruların cevaplanması gerektiğini görebilmek çok şey değiştiriyor.</p><p>Proaktif bir insanı düşündüğümde aklıma gelen en basit cümle şu:</p><p><strong>“Söylenmesini beklemeden yapılması gerekeni görebiliyor.”</strong></p><h3>4. Pragmatism — Mükemmel Olanı Değil, Doğru Olanı Seçebilmek</h3><p>Yazılım geliştirirken çoğumuz yaptığımız işin mümkün olduğunca iyi olmasını istiyoruz.</p><p>Daha temiz bir mimari, daha güncel teknolojiler, daha iyi testler, daha az teknik borç…</p><p>Ama gerçek hayatta kararları yalnızca teknik doğrular üzerinden veremiyoruz.</p><p>Özellikle son dönemde üzerinde çalıştığım işlerde en sık karşılaştığım kısıtlardan biri <strong>zaman</strong>.</p><p>Yıllardır çalışan bir ürünü yeni teknolojilere dönüştürürken her parçayı en baştan kusursuz şekilde tasarlamak isteyebiliriz. Teknik açıdan bunun için birçok haklı sebebimiz de olabilir.</p><p>Ama bir taraftan ürünün de dönüşmeye devam etmesi gerekiyor.</p><p>Bu yüzden kendime sorduğum soru zamanla değişti:</p><p><strong>“Bunu en mükemmel şekilde nasıl yapabiliriz?”</strong></p><p>yerine:</p><p><strong>“Elimizdeki zaman içerisinde ürünü güvenli, anlamlı ve sürdürülebilir şekilde nasıl ileri taşıyabiliriz?”</strong></p><p>Ben pragmatizmi en çok böyle durumlarda düşünmeye başladım.</p><p>Pragmatik olmak kaliteyi önemsememek veya her zaman en hızlı çözümü seçmek değil. Hangi noktada kaliteden taviz veremeyeceğimizi, hangi noktada daha basit bir çözümün yeterli olduğunu ve bugün verdiğimiz kararın gelecekte bize neye mal olacağını görebilmek.</p><p>Bazen teknik olarak çok daha iyi bir çözümün hayata geçmesi aylar sürebilir. Daha basit bir çözüm ise ürünün birkaç hafta içerisinde anlamlı bir adım atmasını sağlayabilir.</p><p>Burada kendime sorduğum şey şu: <strong>Neyi kazanıyoruz, neyden vazgeçiyoruz ve bugün aldığımız karar yarın karşımıza nasıl çıkacak?</strong></p><p>Bence iyi mühendislik her zaman en sofistike veya en kusursuz çözümü üretmek değil. Bazen koşulları doğru okuyup <strong>yeterince iyi olan çözümün gerçekten yeterli olduğu noktayı görebilmek.</strong></p><p>Sonuçta geliştirdiğimiz teknoloji bir amaca hizmet ediyor. Ürünün, kullanıcının ve organizasyonun ilerlemesini sağlamıyorsa teknik olarak ne kadar kusursuz olduğunun tek başına çok fazla anlamı kalmıyor.</p><p>Pragmatizmi kendim için şöyle özetliyorum:</p><p><strong>“Koşulları ve sonuçlarını anlayarak doğru trade-off’u yapabiliyor.”</strong></p><h3>5. Resourcefulness — Cevabı Bilmesen de Bir Yol Bulabilmek</h3><p>Yazılım dünyasında kimsenin cevabını bilmediği problemlerle sık sık karşılaşıyoruz.</p><p>Daha önce kullanılmamış bir teknoloji, dokümantasyonu yetersiz bir sistem veya takımda kimsenin deneyimlemediği bir problem…</p><p>Ben de bu durumlarla birçok kez karşılaştım.</p><p>Böyle zamanlarda genellikle hızlıca bir şeyler denemeye çalışıyorum. Bulabildiğim örnekleri ve dokümantasyonları inceliyorum, küçük denemeler yapıyorum ve öğrendiklerimi mümkün olduğunca basit ve anlaşılır bir hale getirmeye çalışıyorum.</p><p>Bazen ortaya küçük bir proof of concept çıkıyor. Bazen basit bir kod örneği. Bazen de takımın kullanabileceği birkaç sayfalık bir dokümantasyon.</p><p>İlk hedefim mükemmel bir çözüm çıkarmak olmuyor.</p><p><strong>Önce belirsiz olan şeyi, üzerine konuşabileceğimiz kadar somut hale getirmeye çalışıyorum.</strong></p><p>Tabii ki her şeyi kendi başımıza çözmemiz gerekmiyor.</p><p>Konuyu bilmediğim durumlarda organizasyon içerisinde bunu bilebilecek insanları bulup onlarla konuşmanın bana çok şey kazandırdığını gördüm.</p><p>Ama burada sadece doğru kişiyi bulmak da yetmiyor.</p><p><strong>Doğru soruyu sormak gerekiyor.</strong></p><p>Neyi çözmeye çalıştığımızı gerçekten anlamadıysak doğru soruyu sormak da zorlaşıyor. Bu yüzden önce problemi anlamak, neyi bilmediğimizi fark etmek ve sonra doğru sorularla ilerlemek benim için önemli.</p><p>Bütün bunların arkasında ise çok basit bir şey olduğunu düşünüyorum:</p><p><strong>Merak.</strong></p><p>Yeni bir şey öğrenirken deneyiminin veya title’ının çok fazla önemi kalmıyor. Senior, Lead veya kariyerinin daha başında olabilirsin. Daha önce hiç karşılaşmadığın bir konu önüne geldiğinde hepimiz aynı yerden başlıyoruz:</p><p><strong>Bilmiyoruz.</strong></p><p>Zamanla şunu fark ettim: Asıl fark, cevabı bilmekten çok <strong>cevaba nasıl ulaşacağını bilmekte.</strong></p><p>Araştırmak, denemek, doğru insanlara ulaşmak, doğru soruları sormak ve öğrendiklerimizi başkalarının da kullanabileceği kadar basit hale getirmek…</p><p>Resourcefulness’ı kendim için şöyle özetliyorum:</p><p><strong>“Nasıl yapılacağını bilmese bile, nasıl ilerleyeceğini bulabiliyor.”</strong></p><h3>Daha Fazla Etki Yaratmak</h3><p>Bu beş özelliği yan yana koyduğumda ortak bir noktada birleşiyorlar:</p><p><strong>Credibility:</strong> <em>Onun fikrine ve yaklaşımına güveniyorum.</em></p><p><strong>Reliability:</strong> <em>Ben orada olmasam bile ona güveniyorum.</em></p><p><strong>Proactivity:</strong> <em>Söylenmesini beklemeden yapılması gerekeni görebiliyor.</em></p><p><strong>Pragmatism:</strong> <em>Koşulları ve sonuçlarını anlayarak doğru trade-off’u yapabiliyor.</em></p><p><strong>Resourcefulness:</strong> <em>Nasıl yapılacağını bilmese bile, nasıl ilerleyeceğini bulabiliyor.</em></p><p>Bunların hiçbiri doğrudan kaç satır kod yazdığımızla ilgili değil. Ama hepsi çevremizdeki insanların ve takımın daha etkili olmasına katkı sağlıyor.</p><p>Bence <em>team multiplier</em> olmanın özü de burada.</p><p>Her problemi çözen kişi olmak, her kararın içinde bulunmak veya her toplantıya katılmak değil.</p><p>Bazen takım arkadaşının önündeki engeli kaldırmak. Bazen fikrini söyledikten sonra geri çekilip kendi kararını vermesine izin vermek. Bazen zor bir anda <strong>“Beraber çözeriz.”</strong> diyebilmek.</p><p>Bazen henüz problem haline gelmemiş bir şeyi fark etmek, mükemmel çözümden vazgeçip o an için doğru çözümü seçmek veya hiçbirimizin cevabı bilmediği bir konuda ilk adımı atmak.</p><p>Bunların birçoğu GitHub contribution grafiğinde görünmüyor. Ama takımın başarısı üzerinde yazdığımız birçok satır koddan daha büyük bir etki yaratabiliyor.</p><p>Kariyerimizin başlarında kendimize sıklıkla şu soruyu soruyoruz:</p><p><strong>“Nasıl daha iyi bir developer olabilirim?”</strong></p><p>Belki kariyerimizin bir noktasından sonra bu sorunun yanına başka bir soru daha eklemek gerekiyor.</p><p><strong>“Bugün ne kadar iş yaptım?”</strong></p><p>yerine:</p><p><strong>“Bugün çevremdeki insanların daha etkili olmasına nasıl katkı sağladım?”</strong></p><p>Belki de bireysel katkıdan <em>team multiplier</em> olmaya geçiş tam olarak bu soruyla başlıyor.</p><p>Çünkü kariyerimizin bir noktasından sonra gelişmek yalnızca daha zor problemleri çözebilmekten geçmiyor. Bazen başkalarının o problemleri çözebilmesini sağlamaktan geçiyor.</p><p>Bazen de başarı birimini değiştirmek gerekiyor:</p><p><strong>Kendimizden, takımımıza.</strong></p><p>Çünkü bir sonraki seviye her zaman daha iyi kod yazmak değil.</p><p><strong>Daha fazla etki yaratmak.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b344ae5f9f59" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/daha-i%CC%87yi-kod-yazmak-de%C4%9Fil-daha-fazla-etki-yaratmak-b344ae5f9f59">Daha İyi Kod Yazmak Değil, Daha Fazla Etki Yaratmak</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RabbitMQ Reliability — Part 3: Retry, DLQ, Exponential Backoff & Jitter]]></title>
            <link>https://medium.com/kodcular/rabbitmq-reliability-part-3-retry-dlq-exponential-backoff-jitter-cad524a36c84?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/cad524a36c84</guid>
            <category><![CDATA[message-broker]]></category>
            <category><![CDATA[reliability]]></category>
            <category><![CDATA[rabbitmq]]></category>
            <category><![CDATA[broker]]></category>
            <dc:creator><![CDATA[Görkem Kaya]]></dc:creator>
            <pubDate>Sat, 12 Sep 2026 18:42:08 GMT</pubDate>
            <atom:updated>2026-09-12T18:42:06.847Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/766/0*raBPsAnl_zUXukIh" /></figure><h4>Mesaj Broker’a Ulaştı, Peki Ya Consumer İşleyemezse?</h4><h4>Consumer tarafında başarısız olan mesajların kontrollü şekilde nasıl yeniden denendiğini, retry mekanizmalarının nasıl tasarlandığını ve kalıcı hataların DLQ ile nasıl yönetildiğini ele alıyoruz.</h4><p>Yazı serimizin ilk bölümünde Publisher’ın mesajı Broker’a güvenilir şekilde ulaştırması sağlamış, ikinci bölümünde ise duplicate mesajların consumer tarafında idempotency ile kontrol altına alınmasını sağlamıştık. Dolayısıyla <strong>Publisher → Broker → Consumer</strong> akışını temel olarak tamamlamıştık</p><p>Yazı serimizin son bölümü olacak olan bu bölümde ise “<strong>consumer aldığı mesajları işleyemezse ne olur?</strong>” sorusuna cevap bulmaya çalışacağız.</p><h3>1. Consumer Mesajı İşleyemezse Ne Olur?</h3><p>Consumer’lar her zaman istediğimiz şekilde çalışmayabilir. Bazen database’e olan erişim geçici olarak kesilebilir, bazen ilgili consumer’ın kullandığı harici servis cevap vermeyi durdurabilir veya network kaynaklı çeşitli problemler yaşanabilir.</p><p>Örneğin:</p><ul><li>Database kısa süreliğine erişilemez durumda olabilir.</li><li>Harici bir API timeout verebilir.</li><li>Network problemi yaşanabilir.</li><li>Consumer içerisinde beklenmeyen bir exception oluşabilir.</li></ul><p>Bu gibi durumlarda mesajın kendisinde herhangi bir problem olmayabilir. Problem yalnızca consumer’ın mesajı <strong>o anda</strong> sağlıklı şekilde işleyememesidir. Dolayısıyla böyle bir mesajı doğrudan kaybetmek veya tamamen başarısız kabul etmek yerine, bir süre sonra tekrar işlenmesini isteyebiliriz.</p><p>Örneğin database’e erişim 10 saniyeliğine kesilmişse, aynı mesaj birkaç saniye sonra tekrar işlendiğinde işlem başarılı olabilir. Benzer şekilde geçici olarak cevap vermeyen bir harici servis kısa bir süre sonra tekrar kullanılabilir hale gelebilir.</p><p>Bu nedenle mesajlaşma sistemlerinde başarısız olan bir işlemi belirli kurallar çerçevesinde yeniden denemek isteyebiliriz.</p><p>İşte bu yeniden deneme yaklaşımına <strong>Retry</strong> diyoruz. Fakat burada yeni bir soru ortaya çıkıyor:</p><blockquote>Mesaj başarısız olduğu <strong>anda</strong> tekrar denenmeli mi?</blockquote><p>İlk bakışta mantıklı görünen bu yaklaşım, bazı durumlarda sistemi daha da probleme sokabilir.</p><h3>2. Retry Nedir ve Neden Immediate Retry Her Zaman Doğru Değildir?</h3><p>Retry, başarısız olan bir işlemin belirli koşullar altında tekrardan denenmesidir. RabbitMQ tarafında da bir mesaj sağlıklı şekilde işlenemediğinde bu mesajın consumer’a tekrardan ulaştırılmasını sağlamak için ilgili mesajı queue’ya göndeririz. Queue’ya geri gönderilen mesajlar tekrardan consumer’lara iletilir.</p><p>Bunu en basit şekilde alttaki kod örneği ile gerçekleştirebiliriz:</p><pre>await channel.BasicNackAsync( <br>  deliveryTag: args.DeliveryTag, <br>  multiple: false, <br>  requeue: true);</pre><p>Burada ‘<strong>requeue: true</strong>’ dediğimizde, consumer’a iletilen mesajın tekrardan queue’ya yazılmasını istediğimizi belirtiyoruz. Böylece bu mesaj, o an consumer tarafından işlenemese dahi tekrardan queue’ya yazılarak bir süre sonra consumer tarafından işlenebilir hale gelecektir. Bu akışı daha anlaşılabilir hale getirmek adına alttaki görseli inceleyebiliriz.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/680/1*4LP24lVQeMg1-owUEYcVTg.png" /></figure><p>Bu görselde anlayacağımız üzere queue’daki mesaj consumer’a iletiliyor fakat bir exception yaşandığından dolayı ilgili mesaj NACK ile birlikte requeue ediliyor. İlgili mesajın NACK edilmesi ‘Requeue : true’ parametresi ile yapıldığı için mesajı tekrardan queue’ya koyuyoruz.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/899/1*vfFgZzZfG3HMr2q7SiVlUQ.png" /><figcaption>Consumer sağlıklı şekilde çalışır hale geldikten sonra gelen mesajı başarıyla alıyor.</figcaption></figure><p>Requeue edilen mesaj queue’dan consumer’a tekrardan iletiliyor. Bu sırada consumer’ın kullandığı dış servis sağlıklı şekilde çalışmaya başladığı için ilgili mesaj başarıyla işleniyor.</p><p>Peki, ya consumer uzun bir süre sağlıklı şekilde çalışamazsa ne olacak? Bu süreç içerisinde broker ve consumer arasındaki trafik nasıl olacak?</p><p>İsterseniz şöyle bir kurgumuz olsun:</p><p>Queue’da hazır olarak bekleyen 10.000 mesajımız olsun ve bu mesajlar consumer tarafından tüketiliyor olsun. İlgili consumer’ın bir dış servise bağımlılığı olduğunu ve <strong>bu dış servisin</strong> <strong>5 saniyeliğine erişilemez durumda olduğunu farzedelim.</strong></p><p>Bu 5 saniyelik süreçte broker ve queue arasında nasıl bir etkileşim olacak?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y0uLTtwufPo89gmYv4wGcg.png" /><figcaption>5 saniyelik süreçte broker ile consumer arasındaki etkileşim</figcaption></figure><p>Üstteki görselde neler olacağını temsil etmeye çalıştım. Sizin de göreceğiniz üzere bu <strong>5 saniyelik süreçte</strong> broker tarafından queue’ya toplamda <strong>60.000 mesaj gönderilmiş</strong>. Aynı şekilde consumer tarafından broker’a da <strong>60.000 mesajın requeue edilmesi bildirilmiş.</strong></p><p>Bu <strong>5 saniyelik süreçte</strong>, consumer’ın dış servis bağımlılığında bir problem olmasına rağmen sistemdeki component’lar gereksiz yere işlemler yapıyor, CPU tüketiyor, hatalar fırlatıyor ve loglar üretiyor. Kısacası <strong>kaynaklarımız</strong> bu durumdan <strong>ciddi şekilde etkileniyor</strong>.</p><p>Bu örnekte, consumer’ın mesajı işleyemediği durumlarda mesajı direkt requeue etme işleminin verimli olamayabileceğini gözlemliyoruz.</p><p>Peki burada ne yapabiliriz diye düşünecek olursak aklımıza ilk gelen fikir muhtemelen şu olacaktır:</p><blockquote>“Bu kadar sık denemek yerine daha geniş aralıklar ile deneyelim.”</blockquote><p>İsterseniz bu fikri, yani ‘<strong>Delayed Retry</strong>’ı inceleyerek devam edelim.</p><h3>3. Delayed Retry, TTL ve Dead Letter Exchange</h3><p>Bir önceki bölümde, consumer’ın işleyemediği mesajların doğrudan requeue edilmesinin her zaman sağlıklı bir yaklaşım olmadığını gördük. Özellikle problemin birkaç saniye boyunca devam ettiği durumlarda mesajların sürekli olarak consumer’a iletilmesi, işlenememesi ve tekrar queue’ya alınmasının sistem üzerinde gereksiz kaynak tüketimine sebep olabileceğinden de bahsetmiştik.</p><p><strong>Delayed Retry</strong>’da yapmak istediğimiz şey oldukça basit:</p><blockquote>Başarısız olan mesajı hemen tekrar denemek yerine, belirli bir süre beklettikten sonra tekrar deneyelim.</blockquote><p>İsterseniz test sonucunda iki farklı retry yöntemini karşılaştırabilmek adına yine aynı örnek ile devam edelim. Requeue durumunda mesajı tekrar consumer’a göndermek yerine <strong>5 saniye bekletip</strong> tekrar deneyelim.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/929/1*gXdv6waiWZLrl3XmpTHBrQ.png" /><figcaption>Mesajları 5&#39;er saniye ara ile requeue etme senaryosu</figcaption></figure><p>Üstteki görselden de akışımızı anlayabiliriz.</p><p>Burada dikkat ederseniz immediate retry yaklaşımından farklı olarak başarısız olan mesajı doğrudan ana queue’ya geri göndermiyor, ayrı bir retry queue’ya yönlendiriyoruz.</p><p>Bu noktada anlamlandıramadığımız 2 farklı soru olabilir.</p><blockquote>1. Neden ayrı bir queue kullandık?</blockquote><blockquote>2. Mesajın queue içerisinde kaç saniye duracağını nasıl tanımlıyoruz?</blockquote><p>Bu sorulara verilecek cevapların temelini oluşturacak olan<strong> TTL (Time To Live)</strong> kavramı ile devam etmek istiyorum. Bu kavramın ne olduğunu ve nasıl çalıştığını anladığımızda üstteki sorulara doğrudan kendimiz cevap verebilir hale geleceğiz.</p><h4>TTL Nedir?</h4><p>TTL, bir mesajın queue içerisinde ne kadar süre tutulabileceğini belirlememizi sağlar. TTL dolduğunda ilgili mesaj queue’dan silinebilir hale gelir.</p><p>TTL değerini iki farklı kapsam da tanımlayabiliriz.</p><p>Bunlardan ilki, mesaj başına TTL tanımlaması yapmaktır. Bu yaklaşımda her bir mesaja ayrı bir TTL değeri atanır ve ilgili mesajların ömrü o TTL değeri kadardır.</p><p>Örneğin bir mesajın TTL değerini ‘5000 ms’ saniye olarak atadığımızda, 5 saniye sonra bu mesaj queue’dan silinebilir hale gelecektir.</p><blockquote><strong>Not:</strong> TTL süresi dolan mesajlar queue’dan direkt silinmez. Queue’nun başında yapılacak olan TTL kontrolü sonrasında silinirler. Dolayısıyla <strong>“TTL süresi biten bir mesajlar queue’dan direkt silinir”</strong> demek <strong>çok doğru değil</strong>.</blockquote><p>Bir diğer tanımlama şeklimiz de queue bazlı TTL tanımlaması yapmaktır. Bu yaklaşımda ise her bir queue’ya, içereceği mesajların ne kadar ömrü olacağı en baştan tanımlanır. Dolayısıyla bu tür queue’lara mesajlar gönderildiğinde, o mesajların TTL değerleri aslında queue’nun TTL değeri olacaktır.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*gXdv6waiWZLrl3XmpTHBrQ.png" /></figure><p>Bu görselden de hatırlayacağımız üzere “5s-retry-queue” sunda ilgili queue nun TTL değerini 5 saniye olarak ayarlamıştık. Böylece mesajların 5 saniye sonra retry queue’dan silinmesini sağlamıştık.</p><blockquote>Tamam, TTL’in iki şekilde tanımlandığını gördük. TTL süresi dolan mesajlar queue’dan silinecekti. Peki TTL değeri dolduğundan dolayı queue’dan <strong>silinen mesajlar nereye gidecek?</strong></blockquote><p>Bizim istediğimiz şey mesajın silinmesi değil.</p><p>Amacımız mesajı bir süre beklettikten sonra tekrar ana queue’ya göndererek consumer tarafından yeniden işlenmesini sağlamak.</p><p>Tam olarak bu noktada mesajların ana queue’ya yönlendirilmesinde önemli rol alacak <strong>Dead Letter Exchange</strong> devreye giriyor.</p><h4>Dead Letter Exchange</h4><p>RabbitMQ’da bir mesaj belirli nedenlerden dolayı bulunduğu queue’dan çıkarıldığında, mesaj başka bir exchange’e yönlendirilebilir.</p><p>Bu exchange’e <strong>Dead Letter Exchange</strong>, yani kısaca <strong>DLX</strong> denir. Bu exchange’e yönlendirilen mesajlar da ilgili routing key değeri ile birlikte ilgili queue’ya yönlendirilir.</p><p>Böylece akışımız şu hale gelir:</p><pre>order-created-queue<br>    ↓<br>consumer<br>    ↓<br>  fail<br>    ↓<br>5s-retry-queue<br>    ↓<br> TTL = 5s<br>    ↓<br>   DLX<br>    ↓<br>order-created-queue<br>    ↓<br>consumer</pre><p>İsterseniz burada biraz duralım ve ilk baştaki örneğimizi düşünelim. Bizim istediğimiz şey tam olarak şuydu :</p><p><strong>“ Mesajlar direkt requeue olmasın, 5 saniye bekledikten sonra requeue olsun”.</strong></p><p>O zaman kendi kendimize şu çıkarımı yapabiliriz: “Biz de consumer olarak bir mesajı işleyemediğimizde direkt requeue etmek yerine, o mesajı bir retry-queue’ya publish edelim. Retry-queue’ya gönderilen mesaj TTL’i dolana kadar beklesin. TTL dolduğunda mesajı, o queue’da DLX olarak tanımlanmış olan order-created-exchange’e yönlendirelim. Böylece işleyemediğim her bir mesaj 5 saniye bekledikten sonra tekrardan queue’ya yönlendirilsin.”</p><p>Farkettiyseniz bu noktaya kadar<strong> TTL</strong> ve <strong>DLX</strong>’in ne olduğunu öğrendik. Sonrasında ise öğrendiklerimizle üstteki çıkarımı yapabildik.</p><p>Dolayısıyla <strong>TTL</strong>’i anlatırken kendi kendimize sormuş olduğumuz şu iki soruyu kendimiz cevaplamış olduk :</p><blockquote>1. Neden ayrı bir queue kullandık?</blockquote><blockquote>2. Mesajın queue içerisinde kaç saniye duracağını nasıl tanımlıyoruz?</blockquote><p>Yazımızın bu noktasına kadar <strong>Immediate Retry</strong>’ın ne olduğunu ve ne gibi etkiler yaratabileceğini gördük. Yarattığı etkileri azaltmak için de <strong>Delayed Retry</strong> yaklaşımını kullandık.</p><p>Her iki yaklaşımın da temel amacı, o anda sağlıklı şekilde işlenemeyen bir mesajın daha sonra tekrar işlenebilmesini sağlamaktır.</p><p>Buradaki varsayımımız oldukça basit:</p><blockquote>Consumer’ın veya bağımlı olduğu servisin yaşadığı problem geçici olabilir.</blockquote><p>Peki problem geçici değilse?</p><p>Consumer 5 saniye sonra da, 10 saniye sonra da, 1 dakika sonra da aynı mesajı işleyemiyorsa ne olacak?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SZK9tttm9jaGiLkve39RaQ.png" /><figcaption>Queue çok fazla mesaj alıyor fakat bu mesajlar işlenemiyor.</figcaption></figure><p>Queue çok fazla mesaj alıyor fakat bu mesajlar işlenemiyor.</p><p>Üstteki senaryoda queue’ya yoğun şekilde mesaj gelmeye devam ederken consumer bu mesajları başarılı şekilde işleyemiyor. Başarısız olan mesajlar retry mekanizması üzerinden tekrar dolaşıma giriyor.</p><p>Consumer’ın sağlıklı şekilde hizmet veremediği süre uzadıkça işlenmeyi bekleyen ve tekrar denenen mesajların sayısı da artmaya devam ediyor.</p><p>Dolayısıyla yalnızca retry mekanizması oluşturmak yeterli değildir.</p><p>Bir noktada şu soruya cevap vermemiz gerekir:</p><blockquote>Bir mesajı kaç defa retry edeceğiz?</blockquote><p>Çünkü başarısız olan bir mesajı sonsuza kadar tekrar denemek yerine, belirli bir deneme sayısından sonra normal akıştan çıkarmamız gerekir.</p><p>Bu noktada karşımıza <strong>Retry Count</strong>, <strong>maksimum deneme sayısı</strong> ve başarısız mesajları ayrı bir yerde tutmamızı sağlayacak <strong>Dead Letter Queue</strong> kavramları çıkıyor.</p><h3>4. Retry Count, Maksimum Deneme ve Dead Letter Queue</h3><p>Bir önceki bölümde kendi kendimize şu soruyu sormuştuk:</p><blockquote>Bir mesajı kaç defa retry edeceğiz?</blockquote><p>Aslında bu sorunun cevabı oldukça önemli. Çünkü mesajın her başarısız olduğunda tekrar tekrar dolaşıma sokulması, bir noktadan sonra retry mekanizmasının kendisinin problem haline gelmesine sebep olabilir.</p><p>Örneğin bir consumer’ın işleyemediği bir mesajın döngüsü alttaki gibi olacaktır.</p><pre>Message<br>   ↓<br>Consumer<br>   ↓<br>Fail<br>   ↓<br>Retry #1<br>   ↓<br>Fail<br>   ↓<br>Retry #2<br>   ↓<br>Fail<br>   ↓<br>Retry #3<br>   ↓<br>Fail<br>   ↓<br>...</pre><p>Burada fark edeceğiniz üzere retry mekanizmasının ne zaman sonlanacağına dair herhangi bir kuralımız yok. Bu yüzden bu döngü sonsuza kadar devam edecektir. Bu döngünün belirli bir süre sonra sonlanması için ilk yapmamız gereken şeylerden biri, ilgili mesajın daha önce kaç defa retry edildiğini bilebilmek.</p><h4>Retry Count</h4><p>Retry Count, bir mesajın kaç defa yeniden işlenmeye çalışıldığını takip etmemizi sağlar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/557/0*ZWbLldU4kJ8zl4sd" /><figcaption>‘x-death’ örneği</figcaption></figure><p>RabbitMQ, mesajın header’ına “<strong>x-death</strong>” bilgisini ekler. Bu bilgi içerisinde ilgili mesajın <strong>hangi queue’dan, hangi sebeple</strong> ve <strong>kaç defa dead-letter edildiği gibi bilgileri içerir.</strong></p><p>Bu bilgi ile birlikte consumer mesajı her aldığında, ilgili mesajın daha önce kaç defa retry edildiğini bilebilir hale gelir.</p><h4>Maksimum Retry Sayısı</h4><p>Bazı durumlarda mesajların sağlıklı şekilde işlenememesi geçici bir durumdur. Şuanda işlenemeyen mesaj 1 saniye sonra sağlıklı şekilde işlenebilir. Bazı durumlarda ise ilgili mesaj uzun sürelerce işlenemeyebilir.</p><p>Mesajın uzun süre boyunca işlenememesinin component’larımız üzerinde oluşturacağı etkiyi engellemek için retry mekanizmalarında bir <strong>maksimum retry sayısı</strong> belirleyebiliriz.</p><p>Örneğin uygulamamızda şu şekilde bir kural tanımladığımızı düşünelim:</p><pre>Max Retry Count = 3</pre><p>Bu durumda sağlıklı bir şekilde işlenemeyecek olan mesajın akışı şu şekilde olacaktır:</p><pre>İlk İşleme<br>   ↓<br>Fail<br>   ↓<br>Retry #1<br>   ↓<br>Fail<br>   ↓<br>Retry #2<br>   ↓<br>Fail<br>   ↓<br>Retry #3<br>   ↓<br>Fail</pre><p>Mesaj üçüncü retry sonrasında hâlâ başarılı şekilde işlenemiyorsa artık şu soruyu sormamız gerekir:</p><blockquote>Bu mesajı bir kez daha retry etmenin bize gerçekten bir faydası var mı?</blockquote><p>Bazı durumlarda cevap evet olabilir; fakat belirlediğimiz retry politikasına göre bu noktada artık mesajın normal retry döngüsünden çıkarılması gerekir.</p><p>Çünkü aksi durumda başarısız olan mesaj sürekli olarak:</p><pre>Queue → Consumer → Fail → Retry Queue → Queue</pre><p>döngüsünde dolaşmaya devam eder.</p><p>Bu durum yalnızca kaynak tüketimini artırmakla kalmaz, aynı zamanda gerçekten işlenebilir mesajların da aynı altyapıyı paylaşmasına sebep olur. Aynı altyapıyı paylaşan bir yapıda belki hiç işlenmeyecek mesajlar, işlenebilecek mesajların önüne geçip ilgili mesajların daha geç işlenmesine de sebebiyet verebilir. Dolayısıyla bir mesajın kaç defa retry edilebilir olduğunun sınırlandırılmasının bize birden fazla yararının olacağını görebiliyoruz.</p><p>Bu zamana kadar consumer tarafında yaşanan bir problemden dolayı işlenemeyen mesajları retry mekanizmamız ile tekrardan döngüye sokarak işlenebilmesini sağladık.</p><p>Peki mesajlarımızın işlenememesini sebebi consumer değil de <strong>mesajın bizzat kendisiyse?</strong></p><p>Örneğin mesajın payload’ında bir problem varsa, zorunlu bir alan eksikse veya ilgili business rule mesajın işlenmesine izin vermiyorsa aynı mesajı 10 defa da, 100 defa da retry etsek sonuç büyük ihtimalle değişmeyecektir.</p><p>Dolayısıyla problemin ana nedeni mesajın kendisi olduğu durumlarda, ilgili mesajın retry edilmesinin bir mantığı olmayacaktır.</p><p>O zaman bu mesajı ne yapacağız, mesajın kendisinde hata varsa bu mesaj direkt yok mu sayılmalıdır? <strong>Tabii ki hayır.</strong></p><p>Bu mesaj bizim için hala önemli olabilir. Mesajın neden işlenemediğini incelemek, loglarla karşılaştırmak veya problem giderildikten sonra tekrar işlemek isteyebiliriz. Bu sebeple bu gibi problem yaşayan mesajları <strong>Dead Letter Queue</strong> adını verdiğimiz queue’larda tutabiliriz.</p><h4>Dead Letter Queue Nedir?</h4><p>Dead Letter Queue, normal mesaj akışı içerisinde başarılı şekilde işlenemeyen mesajları ayrı bir yerde tutmak için kullandığımız queue’dur.</p><p>Örneğin akışımızı şöyle kurgulayabiliriz:</p><pre>order-created-queue<br>        ↓<br>     consumer<br>        ↓<br>      fail<br>        ↓<br>   retry-queue<br>        ↓<br>     retry #1<br>        ↓<br>      fail<br>        ↓<br>     retry #2<br>        ↓<br>      fail<br>        ↓<br>     retry #3<br>        ↓<br>      fail<br>        ↓<br>       DLQ</pre><p>Böylece belirlediğimiz maksimum retry sayısından sonra mesaj artık tekrar tekrar normal akışa dahil edilmez.</p><p>Bunun yerine ayrı bir queue içerisinde tutulur.</p><p>Örneğin:</p><pre>order-created-dead-letter-queue</pre><p>şeklinde bir queue tanımlayabiliriz.</p><p>Bu queue içerisinde bulunan mesajlar artık bizim için şu anlama gelir:</p><blockquote>“Bu mesaj normal retry mekanizması ile başarılı şekilde işlenemedi. İncelenmesi gerekiyor.”</blockquote><p>İncelenmesi gerektiğini düşündüğümüz mesajlar dead-letter-queue vesilesiyle daha sonra inceleyebilir, hataları düzeltip tekrardan publish edebiliriz.</p><p>Yazımınızın bu noktasına kadar öğrendiklerimizle artık:</p><ul><li>Mesaj başarısız olduğunda retry edebiliyoruz.</li><li>Retry işlemini belirli aralıklarla gerçekleştirebiliyoruz.</li><li>Kaç defa retry edildiğini takip edebiliyoruz.</li><li>Belirli bir limitten sonra mesajı DLQ’ya gönderebiliyoruz.</li></ul><p>Şuanda geldiğimiz noktanın gayet iyi olduğunu düşünüyorum. İlk versiyonumuzda immediate retry ile birlikte çokça retry yapıyorduk. Sonrasında delayed retry ile birlikte bu retry etme sıklığını genişleterek kaynak tüketimini azalttık. Bununla birlikte mesajların retry edilmesini sınırlandırarak işleyenemeyecek olan mesajları main queue’dan çıkartıp ayrı bir queue’da muhafaza ettik.</p><p>İsterseniz şuandaki yapımızı biraz daha inceleyelim.</p><p>Maksimum retry sayısının 3 olduğu ve Delayed Retry kullandığımız bir senaryoda yaşanan olası problem 15 saniye içerisinde çözüldüğü takdirde ilgili mesaj başarıyla işlenebiliyor.</p><pre>5 saniye<br>5 saniye<br>5 saniye</pre><p>Problem 30 saniye içerisinde çözülecekse ne yapacağız?</p><p>Şuandaki çözüm yöntemimize göre mesajımız dead-letter-queue’ya gidecek ve bizim manuel olarak ilgili mesajı tekrardan publish etmemiz gerekecek değil mi?</p><p>Burada nasıl bir çözüm yöntemi uygulayabiliriz diye düşünelim ve aklımıza gerçek hayattan şu senaryoyu getirelim:</p><blockquote>“Birisini telefonla arayıp ulaşamadığımızda tekrardan ararız, yine ulaşamazsak biraz bekler sonrasında ararız. Eğer tekrardan ulaşamazsak şansımızı saatler sonrasında deneriz.”</blockquote><p>Gerçek hayatta uyguladığımız bu çözüm yöntemini burada da uygulabilir miyiz ?</p><p>İşte bu düşünce bizi <strong>Exponential Backoff</strong> yaklaşımına götürüyor.</p><h3>5. Exponential Backoff ve Jitter</h3><p>Exponential Backoff, her başarısız retry denemesinden sonra bir sonraki deneme için beklenecek sürenin kademeli olarak arttırılması yaklaşımıdır.</p><p>Örneğin retry sürelerimizi şu şekilde belirlediğimizi düşünelim:</p><pre>Retry #1 → 5 saniye<br>Retry #2 → 10 saniye<br>Retry #3 → 20 saniye</pre><p>Burada mesaj her başarısız olduğunda aynı süre boyunca beklemek yerine, bir sonraki retry için daha uzun süre bekliyor.</p><p>Akışımız kabaca şu şekilde olur:</p><pre>Message<br>   ↓<br>Consumer<br>   ↓<br>Fail<br>   ↓<br>5 saniye bekle<br>   ↓<br>Retry #1<br>   ↓<br>Fail<br>   ↓<br>10 saniye bekle<br>   ↓<br>Retry #2<br>   ↓<br>Fail<br>   ↓<br>20 saniye bekle<br>   ↓<br>Retry #3</pre><p>Aslında burada yaptığımız şey, bir önceki bölümde verdiğimiz telefon örneğinin karşılığıdır.</p><p>Örneğimiz şuydu:</p><blockquote>İlk denemede ulaşamadığımızda kısa bir süre bekleriz. Tekrar ulaşamazsak bu sefer biraz daha uzun bekleriz. Başarısızlık devam ettikçe denemelerimizin arasındaki süreyi arttırırız.</blockquote><p>Exponential Backoff yaklaşımında da temel düşünce aynı.</p><p>Bu yaklaşım ile hem consumer’ın bağımlı olduğu servise toparlanması için daha fazla zaman tanıyoruz hem de sürekli aynı sıklıkta retry yaparak sistemi gereksiz şekilde yormamış oluruz.</p><p>Bekleme süresini alttaki mantığa göre hesaplayalım</p><pre>delay = baseDelay × 2^retryCount</pre><p>Örneklerimizde baseDelay’i 5 saniye olarak kullanmıştık, burada da aynı değer ile devam edelim:</p><pre>Retry #0 → 5 × 2⁰ = 5 saniye<br>Retry #1 → 5 × 2¹ = 10 saniye<br>Retry #2 → 5 × 2² = 20 saniye<br>Retry #3 → 5 × 2³ = 40 saniye</pre><p>Tabii ki bu sürenin sonsuza kadar büyümesini de istemeyiz. Retry sayımız düşük oluğunda retry süresi ile ilgili bir problem olmayabilir. Retry sayısı eğer artarsa bu süre için üst sınır tanımlamak isteyebiliriz.</p><p>Örneğin:</p><pre>5s → 10s → 20s → 40s → 60s → 60s</pre><p>şeklinde bir üst sınır koyabiliriz.</p><p>Burada retry sürelerini nasıl belirlediğimizi gördük. Peki bunu nasıl uygulayabiliriz biraz buna bakalım.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*gXdv6waiWZLrl3XmpTHBrQ.png" /><figcaption>Delayed Retry case</figcaption></figure><p>Hatırlayacağınız üzere <strong>Delayed Retry</strong> yaklaşımında 1 adet retry-queue’muz vardı ve bu queue ile 5 saniyelik gecikme sağlıyorduk. <strong>Exponential Backoff</strong> yaklaşımında ise sahip olmamız gereken queue sayısı, maksimum retry sayısı ile doğrudan ilişkilidir. Maksimum retry sayımızı 3 olarak belirlersek 3 farklı retry süremiz olmalı. Bu süreler de 5, 10 ve 20 olacaktır. <strong>Bu sebeple 3 farklı retry süresi için 3 adet retry queue’su oluşturacağız.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/826/1*_kZ095XiRZBbQEYCDLx5bQ.png" /><figcaption>Exponential Backoff’ta oluşturduğumuz queue’lar</figcaption></figure><p>Bu senaryoda Consumer kendisine iletilen mesajın kaç defa retry edildiğine göre o mesajı ilgili retry-queue’lara publish edecek. Retry-queue’ların da dead-letter-exchange’leri order-created-exchange olduğu için mesajlar TTL sonunda ana queue’mıza yönlendirilecektir.</p><p><strong>Exponential Backoff</strong> yöntemimizde temel olarak bu şekildeydi. İsterseniz bu 3 farklı yöntemin broker-consumer arasındaki trafiğe nasıl etki ettiğini gözlemlerek devam edelim.</p><p><strong>Immediate Retry</strong>’da bekleme sürecinde broker-consumer arasındaki iletişim şu şekildeydi:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/989/1*TnfPbOvlg9xR64FY4DdWfQ.png" /><figcaption>Immediate Retry</figcaption></figure><p>Görüldüğü üzere 30 saniyelik bir kesintide onlarca mesaj döngüsü oluşuyor.</p><p><strong>Delayed Retry</strong>’a baktığımızda ise:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/992/1*xfjFyc5ISVjUhNjp4Z5STA.png" /><figcaption>Delayed Retry</figcaption></figure><p>Burada ise Immediate Retry’daki sıkı retry işlemlerinin biraz daha azaltılmış halini görüyoruz. Immediate retry’a göre kaynakların daha iyi kullanıldığı bir yöntem olduğunu düşünüyorum.</p><p>Son olarak da <strong>Exponential Backoff</strong> kullandığımız retry’a bakalım:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/984/1*eFK09bjcZ9S5vqNJ2VyKwA.png" /><figcaption>Exponential Backoff</figcaption></figure><p>Exponential Backoff ta ise broker ile consumer arasında sadece 4 adet cycle oluşmuş. Kaynakların en verimli şekilde kullanıldığı yöntemin bu olduğunu görebiliriz.</p><p>Üç yaklaşımı da alttaki görselden çok daha iyi inceleyebiliriz.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*iwEfGEqI-S9pHHAjIG08zw.png" /><figcaption>3 yöntemin tek görseli</figcaption></figure><p>Bu üç yöntemde de broker-consumer arasındaki döngü sayılarının nasıl değiştiğini ve bu değişimin kaynaklarımızı nasıl etkileyebileceğini gördük.</p><p>Bu noktada dikkat çekmek istediğim bir konu var.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*n7i5DeyTkutVKJjQ_0syJA.png" /></figure><p>Dikkat edersek şimdiye kadar kullandığımız retry yaklaşımlarında mesajlarımızın büyük bir kısmı belirli zamanlarda toplu şekilde tekrar publish ediliyor.</p><p>Örneğin elimizde 10.000 mesaj olduğunu düşünelim. Bu mesajların tamamı aynı anda başarısız olduysa ve aynı retry süresine sahipse, 10 saniye sonra bu 10.000 mesajın tamamı tekrar aynı anda dolaşıma girecektir.</p><p>Peki böylesine büyük bir yükün tek bir anda sisteme geri dönmesi sizce bizi nasıl etkiler?</p><h4>Thundering Herd Problem</h4><p>Bir retry zamanı geldiğinde binlerce mesajın aynı anda tekrar publish edildiğini düşünelim. Bu mesajların tamamı kısa bir zaman aralığında consumer’lara ulaşmaya başlayacaktır.</p><p>İlk bakışta bunda bir problem yokmuş gibi görünebilir. Sonuçta amacımız zaten mesajları tekrar işlemek.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*1isLJUfZvZivemM8.gif" /></figure><p>Fakat binlerce mesajın aynı anda consumer’lara ulaşması, sistem üzerinde ani bir yük oluşturabilir. Consumer tarafında CPU kullanımı artabilir, database’e veya harici servislere aynı anda çok sayıda istek gönderilebilir. Eğer problemimizin sebebi zaten kaynakların yoğun şekilde kullanılmasıysa binlerce mesajı retry etmemiz problemli olan yapıya bir darbe daha vurabilir.</p><p>Bahsettiğimiz problemli döngünün akışı şu şekilde:</p><pre>Servis yoğunlaştı<br>        ↓<br>Mesajlar başarısız oldu<br>        ↓<br>Belirli bir süre beklendi<br>        ↓<br>Binlerce mesaj aynı anda retry edildi<br>        ↓<br>Servis tekrar yoğunlaştı<br>        ↓<br>Mesajlar tekrar başarısız oldu</pre><p>Burada kaynaklarımızı sıkıntıya sokan problem <strong>Thundering Herd Problemidir.</strong></p><p>İsterseniz bir diğer olan Jitter bölümünde bu problemi çözmeye çalışalım.</p><h4>Jitter</h4><p>Bir önceki bölümde gördüğümüz üzere problem, yalnızca retry işlemi yapmak değil; çok sayıda mesajın <strong>aynı anda</strong> retry edilmesiydi.</p><p>Örneğin 10.000 mesajın tamamının 5saniye sonra tekrar işlenmek üzere beklediğini düşünelim.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/817/1*pXWJRJXKTtywhnYkkf4k1A.png" /></figure><p>Bu durumda Exponential Backoff kullanıyor olsak bile bütün mesajlar aynı bekleme süresine sahip olduğu için sistem üzerinde yine<strong> ani bir yük oluşabilir.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/934/1*HTXiPQqrFT6SVx_2cQK06w.png" /><figcaption>Jitter kullanımı</figcaption></figure><p>İşte <strong>Jitter</strong> yaklaşımında yapmak istediğimiz şey, hesaplanan retry süresine küçük miktarda rastgelelik eklemektir.</p><p>Örneğin normalde retry süremiz 5 saniye olsun. Jitter uyguladığımızda bu süre şu aralıkta olur:</p><pre>4.5 - 5.5 saniye</pre><p>Böylece aynı anda başarısız olan mesajların tamamı tam olarak 5. saniyede tekrar işlenmek yerine farklı zamanlarda retry edilir.</p><p>Örneğin:</p><pre>Message #1 → 4.6 saniye<br>Message #2 → 4.9 saniye<br>Message #3 → 5.2 saniye<br>Message #4 → 5.4 saniye</pre><p>şeklinde farklı bekleme süreleri oluşabilir.</p><p>Bu sayede bütün retry işlemlerini tek bir ana yığmak yerine zamana yaymış olur ve kaynaklarımızı daha iyi bir şekilde tüketmiş oluruz.</p><p>Jitter’ı Exponential Backoff ile birlikte düşündüğümüzde ise şöyle bir yapı elde edebiliriz:</p><pre>Retry #1 → 4.5 - 5.5 saniye<br>Retry #2 → 9.5 - 10.5 saniye<br>Retry #3 → 19.5 - 20.5 saniye</pre><p>Burada <strong>Exponential Backoff </strong>ile birlikte her başarısız denemeden sonra bekleme süresini artırırken; <strong>Jitter</strong> ile de bu süreye küçük bir rastgelelik ekleyerek mesajların aynı anda retry edilmesini azaltmaya çalıştık. Gün sonunda sistem üzerindeki ani yükü azalttık, consumer’ların ve daha kontrollü şekilde trafik almasını sağladık diyebiliriz.</p><p>Bu yazı serisinde RabbitMQ öğrenme sürecimde merak ettiğim ve öğrenmekten keyif aldığım başlıkları yazıya dökmek istedim. Umarım bilgilendirici bir yazı serisi olmuştur.</p><p>İyi okumalar dilerim.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cad524a36c84" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/rabbitmq-reliability-part-3-retry-dlq-exponential-backoff-jitter-cad524a36c84">RabbitMQ Reliability — Part 3: Retry, DLQ, Exponential Backoff &amp; Jitter</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RabbitMQ Reliability — Part 2: Duplicate Messages, Idempotency, Inbox & Outbox]]></title>
            <link>https://medium.com/kodcular/rabbitmq-reliability-part-2-duplicate-messages-idempotency-inbox-outbox-aa40836b7547?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/aa40836b7547</guid>
            <category><![CDATA[messaging]]></category>
            <category><![CDATA[message-broker]]></category>
            <category><![CDATA[broker]]></category>
            <category><![CDATA[reliability]]></category>
            <category><![CDATA[rabbitmq]]></category>
            <dc:creator><![CDATA[Görkem Kaya]]></dc:creator>
            <pubDate>Tue, 08 Sep 2026 20:45:54 GMT</pubDate>
            <atom:updated>2026-09-08T20:45:54.318Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WjgH-qSuKR22Ha3D.jpg" /></figure><h4>Publisher Confirm Sonrasında Güvenilirliği Sürdürmek</h4><h4>Güvenilir mesajlaşmada duplicate mesajların neden kaçınılmaz olabileceğini, consumer tarafında nasıl güvenli işlendiğini ve Outbox ile veri tutarlılığının nasıl korunduğunu ele alıyoruz.</h4><p>Bu serinin ilk bölümünde, temel bir mesajlaşma altyapısı kurup publisher ile broker arasında mesaj gönderiminin nasıl güvenli şekilde yapılabileceği ile ilgili konuşmuş, çeşitli örnekler ile de konuştuklarımızı deneyimlemiştik. Bahsettiğimiz güvenli iletişimi sağlamak için çeşitli yaklaşımlar kullanıp güvenli iletimi sağlamıştık. Yazımızın ikinci bölümünde ise, benimsediğimiz yaklaşımların nelere sebep olabileceği ile ilgili konuşacağız. Devamında da bu etkileri nasıl ortadan kaldırabileceğimiz hakkında çeşitli yaklaşımlar öğreneceğiz.</p><p>İsterseniz vakit kaybetmeden devam edelim.</p><h3>1. Part 1’den Part 2’ye: Reliable Publish Neden Duplicate Mesaj Üretebilir?</h3><p>Hatırlayacağınız üzere, güvenli iletişimi sağlayabilmek adına mesajlarımızı takip ediyor, broker’dan gönderilen ACK/NACK bildirilerine göre mesajların iletim durumları hakkında bilgi sahibi oluyorduk. Buna ek olarak herhangi bir connection fail durumunda mesajlarımızın iletim durumlarının başarılı yada başarısız olup olmadığı ile ilgili kesin bir kanıya varamayacağımız için bu mesajları tekrardan broker’a publish ediyorduk. Böylece her bir mesajımızın broker’a en az bir kere iletildiğinden emin olduğumuz bir yapıyı kurgulamıştık.</p><p>Mesajların en az bir kez iletilmesini bizim başarı kıstasımızdı. Peki ya birden fazla kez iletilirse?</p><p>İsterseniz alttaki akış ile bunu canlandıralım:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vYEsb_B-pLfxUD6LfULn6w.png" /><figcaption>mesajın broker tarafından alınıp ACK bildirisi yapamaması</figcaption></figure><p>Gördüğümüz üzere broker’a bir mesaj iletildi, broker da bu mesajı başarılı şekilde alıp ACK bildirisini publisher’ın dinlemesi için yayınladı. Bu sırada bağlantı koptuğu için ACK bildirisi publisher’a iletilemediğinden dolayı publisher tarafı <strong>#1 mesajının durumu hakkında bilgiye sahip olamadı.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JNf6F9TawxaMAvu9rYZ-mQ.png" /><figcaption>mesajın republish edilmesi ile birlikte duplicate message’ın oluşabilmesini temsil eden görsel</figcaption></figure><p>Her mesajın en az bir kez broker’a iletilmesi gerektiğinden dolayı aynı mesaj tekrardan broker’a üstte görüldüğü üzere iletildi. Mesajın republish edilmesi ile birlikte, publisher kendi sorumluluğı olan <strong>‘at least one delivery ’</strong>i tamamlamış oldu. Peki bunu yaparken nelere sebebiyet verdi?</p><p><strong>Duplicate Delivery.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sZ3KfmH2TMNzhpUjV-dC7A.png" /><figcaption>broker’da aynı mesajın iki kopya şeklinde yer alması</figcaption></figure><p>Gördüğümüz üzere artık queue’da aynı mesajdan 2 adet var. Peki aynı mesajdan iki adet olması bizim istediğimiz bir şey mi?</p><p>Tabii ki de değil.</p><p>Bazen bir yeri düzeltmek isterken başka yerleri kaçınılmaz şekilde etkileyebiliyoruz. Mesajlarımızın güvenilir şekilde iletilmesi için bir yaklaşım ortaya koyduk, fakat bu bizi bambaşka bir şekilde etkiledi.</p><p>Bize olan etkisini görmek istersek şu örneğe bakabiliriz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*77tq42kEhmo4rSdVdAhkzQ.png" /><figcaption>Duplicate message’ın etkisini ifade eden görsel</figcaption></figure><p>Az önce queue’ya order-created mesajını istemeyerek de olsa iki kopya şeklinde yazmıştık. Bu mesajlar consumer tarafından alındı ve kullanıcının uygulamasına <strong>‘siparişiniz oluşturuldu’</strong> bildiriminin iki defa gönderilmesine sebebiyet verdi. Siz müşteri olsaydınız ne düşünürdünüz?</p><p>Ben sipariş geçmişime bakıp <strong>‘acaba yanlışlıkla iki defa sipariş mi verdim?’</strong> diye bakardım.</p><p>Bu verdiğim örnek sadece customer’ı endişelendiren, belki de çok önemli olmayan bir örnek. Peki ya bu duplicate message bizim tutarlılığımızı etkileseydi ne olurdu? Belki de geri döndüremeyeceğimiz şeylere yol açacaktı.</p><p>Peki burada biz nasıl bir şey olsun isterdik bunu düşünelim.</p><p>Aynı mesajdan iki tane var ise, ben birini işlerim, diğerini de pas geçerim. Böylece mesajların duplicate olması beni etkilemez.</p><p><strong>Böyle bir çözüm yolu aslında idempotent olmanın ta kendisidir.</strong></p><blockquote>Idempotency kelimesinin kökeni aslında matematikten geliyor. Latince “<strong>idem</strong>” kelimesi “<strong>aynı</strong>” anlamına gelirken, “<strong>potent</strong>” ise “<strong>etkili</strong>” anlamına geliyor, ikisini birleştirince de “<strong>aynı etkiye sahip</strong>” anlamı çıkıyor.</blockquote><p>Idempotency’in ne olduğu ve nasıl kullanıldığı ile ilgili daha geniş bir anlatım isterseniz<a href="https://medium.com/kodcular/idempotencyyi-anlamak-yaz%C4%B1l%C4%B1mda-tekrarlanan-taleplerin-y%C3%B6netimi-21fabb94ad97"> <strong>ilgili yazıma</strong></a> göz atabilirsiniz.</p><p>Lafın kısası, duplicate message oluşmasını her zaman engelleyemeyebiliriz; fakat consumer’larımızı idempotent tasarlayarak aynı mesajın birden fazla kez işlenmesinin sistemimiz üzerindeki etkisini ortadan kaldırabiliriz.</p><h3>2. Idempotent Consumer Nedir ve Nasıl Tasarlanmalıdır?</h3><p>Idemponent consumer, aynı mesaj birden fazla kez kendisine ulaştığında bu mesajın oluşturacağı iş etkisini yalnızca bir kez gerçekleştiren consumer’dır.</p><p>Aslında buradaki temel fikir az önce bahsettiğimiz gibidir:</p><blockquote>“Bu mesajı daha önce işledim mi?”</blockquote><p>Consumer bir mesajı aldığında, ilk olarak o mesajı benzersiz olarak tanımlayan kimlik bilgisi üzerinden bir kontrol sağlar. Bu kontrol, ilgili mesajın daha önce işlenip işlenmediğinin sorgulanmasıyla ilgilidir. İlgili mesaj daha önceden işlenmediyse işlenir ve o benzersiz kimliğe ait mesajın işlendiğine dair bir bilgi oluşturur. Bundan sonraki süreçte eğer aynı kimliğe sahip bir mesaj gelirse, o kimliğe sahip bir mesajın daha önce işlendiğini kayıtlardan anlayacak ve mesajı işlemeyecektir.</p><p>Burada mesaja benzersiz kimlik değerini de <strong>MessageId</strong> ile sağlıyoruz.</p><p>MessageId değeri, ilgili mesaja publish esnasında atadığımız değerdir. Bu değer benzersiz olacak şekilde belirlenir, dolayısıyla her bir mesaj benzersiz bir kimliğe sahiptir.</p><p>MessageId değerini şu şekilde oluşturup mesaja atayabiliriz:</p><pre>...<br>var messageId = Guid.NewGuid().ToString();<br><br>var properties = new BasicProperties<br>{<br>    MessageId = messageId,<br>    ContentType = &quot;application/json&quot;,<br>    Persistent = true<br>};<br><br>await channel.BasicPublishAsync(<br>    exchange: &quot;&quot;,<br>    routingKey: &quot;order-created&quot;,<br>    mandatory: true,<br>    basicProperties: properties,<br>    body: body<br>);<br></pre><p>Bu şekilde artık publish ettiğimiz her bir mesaj bir kimliğe sahip oluyor.</p><h3>3. İlk Idempotency Deneyi: ConcurrentDictionary ile Duplicate Kontrolü</h3><p>Idempotency mantığını ilk olarak en basit haliyle görmek için, consumer tarafında işlenen <strong>MessageId</strong> değerlerini bir <strong>ConcurrentDictionary</strong> içerisinde tutabiliriz.</p><pre>var processedMessages = new ConcurrentDictionary&lt;string, byte&gt;();</pre><p>Artık işlenen mesajlarımızı burada tutacak, kontrollerimizi de buradan yapacağız. İsterseniz mesaj geldiğinde nasıl davranmamız gerektiğini de tanımlayalım:</p><pre>consumer.ReceivedAsync += async (sender, args) =&gt; <br>{ <br>    var messageId = @event.BasicProperties?.MessageId;<br>    if (messageId == null)<br>    {<br>        Console.WriteLine(&quot;MessageId is null. Rejecting message.&quot;);<br>        await channel.BasicRejectAsync(@event.DeliveryTag, requeue: false);<br>        return;<br>    }<br>    <br>    if (!processedMessages.TryAdd(messageId, 0))<br>    {<br>        Console.WriteLine($&quot;Message {messageId} has already been processed. Acknowledging message.&quot;);<br>        await channel.BasicAckAsync(@event.DeliveryTag, multiple: false);<br>        return;<br>    }<br>    <br>    Console.WriteLine($&quot;Processing message {messageId}.&quot;);<br>    // ... process the message here ...<br><br>    await channel.BasicAckAsync(@event.DeliveryTag, multiple: false);<br>};</pre><p>Bu tanımlama ile birlikte artık aynı <strong>MessageId </strong>değerine sahip mesajı iki kez publish ettiğimizde, ilk mesaj başarıyla işlenirken ikinci mesaj ignore’lanacaktır.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w8b58JzlUXynOVwDnT2Vxg.gif" /><figcaption>Consumer’ın idempotent davranışı</figcaption></figure><p>Bu ilk örneğimizde consumer’ımıza temel bir idempotentlik kattık ve genel mantığın nasıl işlediğini gördük. Uygulama içerisinde bir <strong>ConcurrentDictionary </strong>tutarak mesaj takibini sağladık.</p><p>Bu örnekte işlediğimiz mesajları memory’de sakladık. Dolayısıyla mesajlarımızın ömrü, uygulamanın ömrüyle eşdeğer oldu.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/724/1*l8QeyMe6EBl-1cvVcJDqBg.gif" /></figure><p>Dolayısıyla üstteki GIF’te olduğu gibi, uygulama her çöktüğünde processedMessages kayıtları siliniyor, uygulama her ayağa kalktığında da idempotentlik o anki mesajlar için sağlanmamış oluyor.</p><p>Buradan anlıyoruz ki mesajların geçici bir yerde saklanması, consumer’ların her zaman idempotent davranış göstermesi için ideal değil.</p><p>Bu sebeple bir diğer başlıkta, işlediğimiz mesajları Inbox Pattern ile birlikte kalıcı bir yerde tutacağız ve consumer’ların idempotent davranışını daha kalıcı hale getireceğiz.</p><h3>4. Production’da Kalıcı Idempotency: ProcessedMessages ve Inbox Pattern</h3><p>Consumer tarafında gelen ve işlenen mesajların kalıcı şekilde takip edilmesi için Inbox Pattern yaklaşımını kullanacağız.</p><p>Inbox Pattern’i kısaca şu şekilde açıklayabiliriz:</p><blockquote>Consumer, işlediği mesajları kalıcı şekilde kaydeder ve aynı mesaj tekrar geldiğinde, bu kayıtları kullanarak ilgili kaydın daha önce işlenip işlenmediğini kontrol eder.</blockquote><p>Biz de bu bölümde, Inbox Pattern’i oldukça sade bir şekilde kullanacak, Idempotent şekilde çalışmanın temeli olacak olan ProcessedMessages tablosunu oluşturacağız.</p><p>İşlediğimiz mesajları kalıcı olarak tutacağımızı söylemiştik. İsterseniz mesajlarımızı nasıl bir yapıda tutacağımızı ProccessedMessages tablosunu oluşturarak başlayalım.</p><pre>public class ProcessedMessage<br>{  <br>   public string MessageId { get; set; } = null!; <br>   public DateTime ProcessedAtUtc { get; set; }<br>}</pre><p>Bu sınıfımız, veritabanında şöyle bir tabloya tekabül edecektir:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*1RGpsUUntXJUhU4Wpgg-NA.png" /><figcaption>ProcessedMessages sınıfının neyi ifade ettiğini gösteren görsel</figcaption></figure><p>Burada <strong>MessageId </strong>alanının <strong>unique</strong> olarak tanımlanması oldukça önemlidir. Çünkü aynı mesaj farklı consumer instance’ları tarafından aynı anda işlenmeye çalışıldığında uygulama seviyesindeki kontrol yeterli olmayabilir. Bu sebeple hem uygulama tarafında, hem de veritabanı tarafında benzersizliği garanti ederek olası duplicate işlemi engellemeyi amaçlıyoruz.</p><blockquote><strong>Dipnot:</strong> Bu yazıda tek bir consumerdan söz ediyoruz. Eğer birbirinden farklı consumerlarımız varsa ve fanout exchange type’ını kullanıyorsak, ortak tablo kullanımında unique constraint tanımlamasını (MessageId,Consumer) olarak tanımlayabiliriz.</blockquote><p>ProcessedMessages tablosunu işlediğimiz mesajların takibini yapmak ve duplicate mesajların işlenmesini engellemek adına oluşturduk.</p><p>İsterseniz şimdi de consumer’ın processedMessages tablo ile olan ilişkisinin nasıl olacağını, ne zaman tablo kontrolünün yapılması gerektiğini ve hangi anlarda bu tabloya kayıt eklenmesi gerektiğini kod seviyesinde tanımlayalım.</p><pre>var messageId = @event.BasicProperties.MessageId;<br> <br>var alreadyProcessed = await dbContext.ProcessedMessages<br>                       .AnyAsync(x =&gt; x.MessageId == messageId); <br>if (alreadyProcessed) <br>{ <br>   Console.WriteLine($&quot;Message {messageId} has already been processed.&quot;); <br>   await channel.BasicAckAsync( @event.DeliveryTag, multiple: false); <br>   return;<br>}</pre><p>Consumer, mesajı ilk aldığında ilgili mesajı daha öncesinde işleyip işlemediğini kontrol etmesi gerekir. Bu kontrolün sonraki satırlara sarkması, gereksiz yere işlemlerin yapılmasına sebebiyet verebilir ve response’un daha geç oluşmasına sebep olabilir.</p><p>Eğer ilgili mesaj daha önce işlenmediyse, mesajı işlemeye devam edebiliriz.</p><pre>Console.WriteLine($&quot;Processing message {messageId}.&quot;); <br><br>// Business işlemlerinin burada tamamlandığını farzedelim.<br>// Örneğin sipariş bildirimi oluşturma, stok güncelleme vb.<br> <br>dbContext.ProcessedMessages.Add(<br>  new ProcessedMessage { <br>    MessageId = messageId, <br>    ProcessedAtUtc = DateTime.UtcNow }<br>  );<br>await dbContext.SaveChangesAsync(); <br>await channel.BasicAckAsync( @event.DeliveryTag, multiple: false);</pre><p>Mesajı başarıyla işlediğimiz senaryoda <strong>ProcessedMessages</strong> tablosuna <strong>mesajı işlediğimize dair kaydımızı yapıyoruz.</strong> Dolayısıyla aynı MessageId’ye sahip mesaj tekrardan consumer’a iletilirse ilgili kaydın varlığı sebebiyle <strong>mesaj işlenmeyecek ve idempotent davranışı sağlamış olacağız.</strong></p><p>Bu noktada akışımızı şu şekilde görselleştirebiliriz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/377/1*RK0IdLqRWmX-vGRplUk1bg.png" /><figcaption>Inbox pattern’in uygulama akışı</figcaption></figure><p>Bu yaklaşımımız ile birlikte mesajlarımızın durumunu geçici tutmak yerine kalıcı hale getirdik ve idempotency’in kalıcı hale getirmiş olduk.</p><p>Tabii ki burada dikkat etmemiz gereken bir nokta var. Buradaki business process ile ProcessedMessages tablosuna kayıt ekleme işimiz birbiriyle doğrudan ilişkili olduğu aşikar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/530/1*dYL39VXZKRVLaZtLWTdaAg.png" /></figure><p>İşlem başarılıysa kayıt eklenmeli, değilse eklenmemeli. Peki bu ilişki kod seviyesinde de garanti edildi mi?</p><h3>5. Business Operation ile Inbox Kaydı Neden Aynı Transaction’da Olmalı?</h3><p>Bir önceki bölümde consumer’ın işlediği mesajları <strong>ProcessedMessages</strong> tablosunda saklayarak kalıcı bir idempotency mekanizması oluşturduk.</p><p>Fakat burada dikkat etmemiz gereken önemli bir nokta var:</p><blockquote>Business operation başarılı olurken <strong>ProcessedMessages </strong>kaydı oluşturulamazsa ne olur?</blockquote><p>İsterseniz bunu basit bir senaryo üzerinden düşünelim.</p><p>Consumer bir <strong>OrderCreated</strong> mesajı aldı ve siparişe ait ürünün stok miktarını 1 azalttı. Yani business operation başarıyla tamamlandı. Fakat hemen sonrasında uygulamanın çöktüğünü ve mesajın işlendiğine dair ProcessedMessages kaydının veritabanına yazılamadığını düşünelim.</p><p>Aynı zamanda consumer RabbitMQ’ya ACK gönderemediği için broker, mesajın başarıyla işlendiğinden emin olamaz ve aynı mesajı tekrar consumer’a iletebilir.</p><p>Consumer mesajı ikinci kez aldığında <strong>ProcessedMessages</strong> tablosunu kontrol eder. Ancak önceki işlemde herhangi bir kayıt oluşturulamadığı için ilgili <strong>MessageId</strong> değerini bulamaz ve mesajı daha önce hiç işlememiş gibi tekrar işler.</p><p>Sonuç olarak aynı <strong>OrderCreated</strong> mesajı iki kez business etkisi oluşturmuş olur:</p><pre>İlk teslimat  → Stok -1<br>İkinci teslimat → Stok -1</pre><p>Günün sonunda tek bir sipariş için stok miktarı <strong>iki kez azaltılmış olur.</strong></p><p>İşte tam olarak bu yüzden, <strong>business operation ile mesajın işlendiğine dair Inbox kaydının birbirinden bağımsız işlemler olarak ele alınmaması gerekir.</strong></p><pre>await using var transaction =<br>    await dbContext.Database.BeginTransactionAsync();<br><br>try<br>{<br>    var alreadyProcessed = await dbContext.ProcessedMessages<br>        .AnyAsync(x =&gt; x.MessageId == messageId);<br><br>    if (alreadyProcessed)<br>    {<br>        await transaction.RollbackAsync();<br>        await channel.BasicAckAsync(args.DeliveryTag, false);<br>        return;<br>    }<br><br>    // Business operation<br>    product.Stock--;<br><br>    // Inbox kaydı<br>    dbContext.ProcessedMessages.Add(new ProcessedMessage<br>    {<br>        MessageId = messageId,<br>        ProcessedAtUtc = DateTime.UtcNow<br>    });<br><br>    await dbContext.SaveChangesAsync();<br>    await transaction.CommitAsync();<br><br>    // Commit&#39;ten sonra ACK<br>    await channel.BasicAckAsync(args.DeliveryTag, false);<br>}<br>catch<br>{<br>    await transaction.RollbackAsync();<br><br>    await channel.BasicNackAsync(<br>        args.DeliveryTag,<br>        multiple: false,<br>        requeue: true);<br>}</pre><p>Bu sebeple birbiriyle doğrudan ilişkili olan bu iki işlemi ortak transaction ile atomik hale getirebiliriz.</p><h3>6. ACK Neden Database Commit’ten Sonra Gönderilmeli?</h3><pre>await dbContext.SaveChangesAsync();<br>await transaction.CommitAsync();<br><br>// Commit&#39;ten sonra ACK<br>await channel.BasicAckAsync(args.DeliveryTag, false);</pre><p>Burada ACK bildirisinin commit’ten sonra olması oldukça önemlidir. Bunun sebebi ACK bildirisinin yapılması ile veritabanı değişikliklerinin uygulanmasının birbirinden farklı şeyler olmasıdır. Eğer biz commit öncesinde ACK bildirisi yaparsak ve herhangi bir commit hatası alırsak, yaptığımız business işlemler geçersiz olacak ve ACK göndereceğimiz için mesaj queue’dan silinecektir.</p><p>Bu kısımda da tek bir satır konumunun, tutarlılığı nasıl etkileyebileceğini görmüş olduk.</p><h3>7. Producer Tarafındaki Problem: Dual Write</h3><p>Yazımızın bu noktasına kadar güvenilir mesajlaşmayı sağlamak adına odağımız consumer tarafındaydı. Fakat güvenilir mesajlaşmada producer tarafında da önemli bir problemimiz var.</p><p>Bu problem, <strong>Dual Write</strong>.</p><p>Bir sipariş oluşturduğumuzu düşünelim. Uygulamamız ilk önce sipariş bilgisini veritabanına kaydediyor, ardından da <strong>OrderCreated</strong> mesajını RabbitMQ’ya publish ediyor. Akışımız şu şekilde olsun:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*d_uvCYM8J5jwF63qTntWAw.png" /></figure><p>İlk bakışta bir problem yokmuş gibi gözüküyor. Kod satırlarının ne ile ilgili olduğuna baktığımızda şunu çıkarıyoruz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/792/1*Mwm0WdkZMjrMbxF9K9KSPA.png" /><figcaption>Kod satırlarının farklı amaçlara hizmet ettiğinin görseli</figcaption></figure><p>Göreceğimiz üzere kod parçası hem database, hem de RabbitMQ ile ilgili işlemler yapıyor ve <strong>bu işlemler sıralı şekilde tanımlanmış.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/834/1*Ye1qBM7-ITOMXpDoXw7IKQ.png" /></figure><p>Peki bu sıralı işlemler ya yarıda kalırsa ne olacak? Yine bir tutarsızlık söz konusu.</p><p>Hatırlarsanız şuanda yaşadığımız problemin bir benzerini consumer tarafında yaşamış ve bunu <strong>inbox pattern</strong> ile çözmüştük. Gerçekleştirdiğimiz business işlem ile processedMessages tablosuna yapacağımız işlemleri tek bir transaction içerisine alıp reliability’i sağlamıştık.</p><p>Çözümümüz şu şekildeydi:<strong> İki işlem, tek transaction.</strong></p><p>Peki consumer tarafında ne yapabiliriz? Inbox Pattern’indeki çözüm şekli, bize yardımcı olabilir mi?</p><h3>8. Outbox Pattern Nedir ve Dual Write Problemini Nasıl Çözer?</h3><p>Consumer tarafında uyguladığımız Inbox Pattern’de iki farklı işlemi aynı transaction içerisine alarak tutarlılığı sağlamıştık. Producer tarafında da benzer bir yaklaşım uygulayabiliriz.</p><p>Buradaki temel fikir şu:</p><blockquote>Business verisini ve publish edilmesi gereken mesajı aynı database transaction içerisinde kaydetmek.</blockquote><p>Yani siparişi oluşturduğumuz anda mesajı doğrudan RabbitMQ’ya publish etmek yerine, önce bu mesajı veritabanındaki bir <strong>OutboxMessages</strong> tablosuna kaydederiz.</p><pre>await using var transaction = await dbContext.Database.BeginTransactionAsync();<br><br>dbContext.Orders.Add(order);<br>dbContext.OutboxMessages.Add(new OutboxMessage<br>{<br>    Id = Guid.NewGuid(),<br>    Type = &quot;OrderCreated&quot;,<br>    Payload = JsonSerializer.Serialize(orderCreatedEvent),<br>    CreatedAtUtc = DateTime.UtcNow<br>});<br><br>await dbContext.SaveChangesAsync();<br>await transaction.CommitAsync();</pre><p>Bu durumda iki işlem de aynı transaction içerisinde gerçekleşir:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ca5p8Y2F3ONEdni08Tt7yg.png" /></figure><p>Transaction başarılı olursa hem sipariş hem de ona ait mesaj kaydedilmiş olur. Herhangi bir hata yaşanırsa ise ikisi de rollback edilir.</p><p>Böylece artık <strong><em>“Order kaydedildi ama OrderCreated mesajı kayboldu.”</em></strong> gibi bir durumun yaşanması önlenmiş olur.</p><p>Peki mesaj RabbitMQ’ya nasıl ulaşacak?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XIBYdnoE8uxRQANwM2vcJw.png" /><figcaption>Outbox Publisher’ın nasıl çalıştığını temsil eden görsel</figcaption></figure><p>Burada devreye ayrı bir <strong>Outbox Publisher</strong> girer. Bu yapı belirli aralıklarla henüz publish edilmemiş <strong>OutboxMessages</strong> kayıtlarını okur ve RabbitMQ’ya gönderir.</p><p>Mesaj broker tarafından güvenli şekilde kabul edildiğinde ise ilgili Outbox kaydı publish edilmiş olarak işaretlenir.</p><p>Bu yaklaşım sayesinde database ile RabbitMQ arasında doğrudan tek bir transaction kurmaya çalışmak yerine, önce güvenilir olan database transaction’ından faydalanmış oluruz.</p><p>İşte bu yaklaşım <strong>Outbox Pattern</strong> olarak adlandırılır.</p><p>Kısacası:</p><blockquote>Inbox, consumer tarafında mesajın birden fazla kez işlenmesini engellerken; Outbox, producer tarafında business işlem ile mesaj üretimi arasındaki tutarlılığı korur.</blockquote><h3>9. Outbox Publisher, MessageId ve PublishedAtUtc</h3><p>Outbox tablosuna kaydettiğimiz mesajların RabbitMQ’ya iletilmesi için bir <strong>Outbox Publisher</strong> kullanırız. Bu yapı belirli aralıklarla henüz publish edilmemiş kayıtları bulur ve broker’a gönderir.</p><pre>var messages = await dbContext.OutboxMessages<br>    .Where(x =&gt; x.PublishedAtUtc == null)<br>    .ToListAsync();</pre><p>Her Outbox kaydının benzersiz <strong>Id</strong> değerini, RabbitMQ mesajının <strong>MessageId</strong> değeri olarak kullanabiliriz:</p><pre>properties.MessageId = outboxMessage.Id.ToString();</pre><p>Böylece aynı Outbox mesajı tekrar publish edilmek zorunda kalırsa yeni bir <strong>MessageId</strong> üretmek yerine aynı kimliği koruruz. Böylece olası duplicate message senaryosunda messageId aynı kalacağı için consumer’ın idempotent şekilde davranmasını sağlayabiliriz.</p><p>Gönderdiğimiz mesaj broker tarafından başarıyla kabul edildiğinde ilgili kayıt işaretlenir:</p><pre>outboxMessage.PublishedAtUtc = DateTime.UtcNow;<br>await dbContext.SaveChangesAsync();</pre><p><strong>PublishedAtUtc</strong>, bize bu Outbox kaydının artık publish sürecinden geçtiğini gösterir. Böylece publisher sonraki taramalarda yalnızca henüz publish edilmemiş kayıtlarla ilgilenir.</p><h3>10. Outbox Duplicate Publish’i Tamamen Engeller mi?</h3><p>Outbox Pattern’in ana amacı duplicate publish’i engellemek değil, olası mesaj kayıplarını engellemektir. Dolayısıyla duplicate publish’in engellenmesi hususunda rol oynamaz.</p><p>Şu senaryoyu düşünelim:</p><pre>Mesaj RabbitMQ&#39;ya ulaştı<br>        ↓<br>Broker ACK gönderdi<br>        ↓<br>Publisher ACK&#39;i alamadan bağlantı koptu<br>        ↓<br>PublishedAtUtc güncellenemedi</pre><p>Outbox kaydı hala publish edilmemiş gibi göründüğü için Outbox Publisher daha sonra aynı mesajı tekrar gönderebilir.</p><p>Sonuç olarak aynı <strong>MessageId</strong> değerine sahip mesaj broker’a birden fazla kez ulaşabilir.</p><p>Bu aslında daha önce de bahsettiğimiz durumun aynısıdır: <strong>at-least-once delivery duplicate mesaj üretebilir.</strong></p><p>Dolayısıyla Outbox Pattern ile Inbox Pattern birbirinin tamamlayıcısıdır:</p><blockquote><strong><em>Outbox mesajın kaybolmamasını sağlar, Inbox ise aynı mesajın birden fazla kez işlenmesinin etkisini engeller.</em></strong></blockquote><p>Böylece producer ve consumer tarafını birlikte ele aldığımızda daha güvenilir bir mesajlaşma yapısı elde etmiş oluruz.</p><h3>11. Outbox ve Inbox Birlikte Nasıl Çalışır?</h3><p>Outbox ve Inbox Pattern aslında birbirini tamamlayan iki yapıdır.</p><p>Outbox Pattern, producer tarafında mesajların kaybolmasını engeller. Business işlem ile publish edilmesi gereken mesaj aynı transaction içerisinde saklanır ve mesaj, broker’a <strong>başarıyla iletilene kadar tekrar gönderilebilir.</strong></p><p>Bu yaklaşım bize <strong>at-least-once delivery</strong> garantisi sağlar. Ancak bu garanti beraberinde <strong>duplicate message</strong> riskini de getirir.</p><p>İşte bu noktada Inbox Pattern devreye girer. Consumer, daha önce işlediği mesajları <strong>MessageId</strong> üzerinden takip ederek aynı mesaj tekrar ulaştığında business işlemi yeniden gerçekleştirmez.</p><p>Bu iki pattern birlikte kullanıldığında, producer ve consumer tarafında daha güvenilir ve tutarlı bir mesajlaşma akışı elde edilir.</p><h3>12. Part 3’e Geçiş: Consumer Mesajı İşleyemezse Ne Olacak?</h3><p><strong>RabbitMQ Reliability serisinin ilk bölümünde</strong>, publisher tarafından iletilen mesajların broker’a sağlıklı şekilde iletilmesini hakkında konuştuk.</p><p><strong>Bu bölümde ise</strong> broker’a güvenli şekilde iletilen mesajların sahip olduğu olası duplicate durumlarını consumer tarafında nasıl yönetebileceğimizi imceledik. İşlenen her bir mesajın durumunu kayıt altına alarak consumer’larımızın idempotent şekilde çalışmasını sağladık. Publisher tarafında da oluşturulan event’ların kaybolmamasını Outbox Pattern ile sağlamış olduk.</p><p><strong>Bu serinin son bölümü</strong> olacak<strong> Part-3</strong>&#39;te ise , consumer’larımızın mesajları işleyemedikleri durumlarda nasıl davranması gerektiği ile ilgili olan <strong>Retry,DLQ, Backoff ve Jitter </strong>kavramlarından bahsederek bu seriyi sonlandıracağım.</p><p>Yazılarımda henüz yeni öğrendiğim şeylerden bahsediyorum. Dolayısıyla yanlış öğrendiğim/bahsettiğim yerler olabilir, mazur görünüz.</p><p>İyi okumalar dilerim.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aa40836b7547" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/rabbitmq-reliability-part-2-duplicate-messages-idempotency-inbox-outbox-aa40836b7547">RabbitMQ Reliability — Part 2: Duplicate Messages, Idempotency, Inbox &amp; Outbox</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RabbitMQ Reliability — Part 1: Publisher Confirms, Connection Failure & Recovery]]></title>
            <link>https://medium.com/kodcular/rabbitmq-reliability-part-1-publisher-confirms-connection-failure-recovery-bf3a658fb809?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/bf3a658fb809</guid>
            <category><![CDATA[message-broker]]></category>
            <category><![CDATA[reliability]]></category>
            <category><![CDATA[rabbitmq]]></category>
            <category><![CDATA[broker]]></category>
            <dc:creator><![CDATA[Görkem Kaya]]></dc:creator>
            <pubDate>Tue, 01 Sep 2026 19:14:15 GMT</pubDate>
            <atom:updated>2026-09-01T19:14:14.432Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Um-j7Bhwr0pKjtURTm7ADw.jpeg" /></figure><h4>RabbitMQ Güvenilirliği — Bölüm 1</h4><h4>Bir mesajın broker’a gerçekten ulaşıp ulaşmadığını nasıl anlayabilir, bağlantı kopmalarında mesaj kaybını nasıl azaltabiliriz?</h4><p>Merhaba, bu yazıda message broker kullandığımız sistemlerde asenkron iletişimi nasıl daha güvenilir hale getirebileceğimizden bahsedeceğim.</p><p>Özellikle bir mesajı publish ettiğimizde, bu mesajın gerçekten broker tarafından alınıp alınmadığını nasıl anlayabileceğimizi ve olası bağlantı problemlerinde mesaj kaybını nasıl önleyebileceğimizi inceleyeceğiz.</p><h4>1. Bir Mesajı Publish Etmek Gerçekten Yeterli mi?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/809/1*yOHtOzF-0oXfd867aPOudQ.png" /><figcaption>Basit bir kuyruk yapısı</figcaption></figure><p>RabbitMQ ile çalışmaya ilk başladığımızda akış oldukça basit görünür. Publisher kendi içerisinde yapması gereken işlemi yapar, ardından yaptığı işlem ile ilgili bir mesaj publish eder. Publish edilen mesaj broker’a ulaşır ve ilgili consumerlar bu mesajları tüketir.</p><blockquote>Peki ilk başladığımızda bu kadar basit görünen şey, neden karmaşıklaşır?</blockquote><p>Bu sorunun cevabını doğrudan vermek yerine, bir sistem kurgulayıp o kurgu üzerine düşünürken bulalım istiyorum. Bu sebeple şu örnek ile devam edelim.</p><p>Bir e-ticaret platformunda sipariş oluşturuldukça, ilgili siparişleri oluşturan müşterilere siparişlerinin başarıyla oluşturulduğuna dair bildirim gönderilir. Biz de bu yapıyı basitçe tasarlayalım, sonrasında başımıza gelebilecek problemleri deneyimleyip bu problemleri çözmek adına neler yapabileceğimize bakalım.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/802/1*Mn7PZIiLpdIzcurdg-jOKg.png" /><figcaption>Kurgumuzun şeması</figcaption></figure><p>Kurguladığımız yapı şuanda bu şekilde. İsterseniz direkt bir mesaj publish ederek akışın nasıl olduğuna bakalım.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/621/1*f3dCEotn9_qji1Fu-tCXow.png" /><figcaption>Mesajın publish edilmesi</figcaption></figure><p>İlgili kod ile event’ımızı broker’a publish ettik. Alttaki görselde de mesajın başarıyla broker’a iletildiğini görebiliriz.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*_8YObrRWBazGBQ6PRjM54A.png" /><figcaption>İlgili mesaj ‘order-created.queue’ya iletildiğini temsil eden görsel.</figcaption></figure><p>Şuanda istediğimiz şeyi kolaylıkla yapabildik çünkü her şey istediğimiz gibiydi. Peki her şey her zaman istediğimiz gibi olabilir mi?</p><p>Message broker’lar çoğu zaman bir sunucuda çalışır ve ağ üzerinden iletişime geçilebilir haldedirler. Dolayısıyla hem producer, hem de consumer’lar ağ üzerinden broker’a yazabilir ve tüketebilir. Az önceki senaryomuzda ağ gayet sağlıklıydı ve biz istediğimizi yapabildik. İsterseniz şimdi de broker’ın çalışmayı durdurduğu senaryoda neler olacağını gözlemleyelim. Çünkü gerçek hayatta her şey istediğimiz gibi olmayabilir.</p><p>Kodumuzu şu şekilde güncelledim:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/618/1*dRJrYMmz0cJrY35DjZ8jyQ.png" /></figure><p>Uygulama mesajı publish etmeden önce broker process’ini durduracağım ve mesajı publish edeceğim. Mesaj publish ettikten sonra da broker’ı tamamen kapatacağım. Bakalım mesajımız gerçekten broker tarafından alınacak mı?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/926/1*zZokLjcdVmlgMAWxFOTHYw.gif" /></figure><p>Eklediğim GIF’te gördüğünüz üzere producer tarafı mesajı başarıyla publish ettiğini söylerken broker tarafında bu mesaj yok.</p><p>Tabiri caizse publisher ile broker arasında şöyle bir muhabbet var:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/245/1*CyhmCv-zI-SG3XJWTCeF_g.gif" /></figure><p>Biz producer olarak mesajı publish ettik ama broker mesajımızı alamadı. Dolayısıyla mesaj da kayboldu.</p><p>Peki biz burada nasıl bir yapı olmasını isterdik?</p><blockquote>Broker keşke mesajı başarıyla alıp almadığının bilgisini bize verseydi, biz de ona göre mesajımızı tekrardan publish eder yada silerdik.</blockquote><h4>2. Publisher Confirms</h4><p>Publisher confirms, publisher tarafından broker’a iletilen mesajların broker tarafından başarılı şekilde alınıp alınmadığı bilgisinin producer’a iletilmesini sağlar. Broker, publisher confirm özelinde ACK ve NACK olmak üzere 2 tür bilgilendirmeyi publisher’a iletir.</p><p>ACK, mesajın broker tarafından başarıyla kabul edildiğini doğrularken; NACK, broker’ın mesaj için olumlu bir publisher confirm veremediğini ifade eder.</p><p>Böylece publisher ilettiği mesajların durumunu takip edebilir ve gerekli aksiyonları alabilir.</p><p>Bu yapı, default olarak kapalı geliyor. Dolayısıyla publisher-broker arasındaki etkileşim varsayılan olarak şu şekilde:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R-XLT89mOztRiFGnAMqhoQ.png" /><figcaption>Publisher confirm kapalı olduğundaki akış.</figcaption></figure><p>Publisher confirm kapalı olduğunda broker tarafından mesajın sağlıklı şekilde alınıp alınmadığı ile ilgili bilgi gönderilmiyor. Dolayısıyla publisher tarafı ilettiği mesajın sağlıklı şekilde alınıp alınmadığı ile ilgili bilgiye sahip değil.</p><p>Bu yapıyı açmak için channel oluşturma sırasında ilgili konfigürasyonu channel’a tanımlamak gerekiyor.</p><p>Bu tanımlamayı şu şekilde yapabiliriz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/545/1*d9noJONSOTj3b7s7vrZjyw.png" /><figcaption>Publisher Confirmation’un tanımlanmasının kod görseli.</figcaption></figure><p>Görseldeki kod ile birlikte gerekli tanımlamayı yaptığımızda ise publisher-broker arasındaki etkileşim şu hale geliyor:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-rzJ3g8x6nD9Ewhchh8vsw.png" /><figcaption>Publisher confirm aktif edildiğinde publisher mesajın durumu ile bilgilendirildiğinin temsili</figcaption></figure><p>Görüldüğü üzere publisher confirm edilmesiyle birlikte publisher gönderdiği mesajın durumu ile ilgili bilgiye sahip oluyor. Herhangi bir mesaj o anda nack alırsa o mesajı tekrardan publish edebilmesi için ilgili bilgilendirmeyi alabiliyoruz artık.</p><p>Dikkat ederseniz ACK durumunda gönderdiğimiz ‘order-created’ mesajını queue içerisine yazmadım. Bu şekilde yapmamın özel bir sebebi var. ACK, ilgili mesaj broker tarafından başarılı şekilde alındığında yayınlanır. Mesajın herhangi bir queue’ya yönlendirilip yönderilmemesiyle ilgili değildir. Dolayısıyla herhangi bir mesaj broker’a sağlıklı şekilde iletilse bile bir queue’ya yönlendirilemeyebilir. Bizler tabii ki burada yönlendirilemeyen mesajların kaybolmasını istemeyiz. Buna yazının devamında değineceğim.</p><p>Bu noktaya kadar gönderdiğimiz mesajların durumuyla akalı bilgi alabilir hale geldik. Peki biz bu bilgileri nasıl alıyoruz ve bu bilgiler ışığında nasıl aksiyon alabiliriz?</p><p>İlk olarak ACK ve NACK bildirimlerinin publisher tarafında nasıl değerlendirileceğinden bahsedeceğim.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OsvYQ1XJNuGw8pQirriyJQ.png" /><figcaption>ACK durumunda akışın nasıl olacağının temsili</figcaption></figure><p>Broker tarafı, kendisine gönderilen mesajı başarılı şekilde işlediğinde ACK gönderdiğinden bahsetmiştik. Bu ACK bildirisi, publisher tarafında BasicAcksAsync() metodunu tetikliyor. Biz de bu sayede publisher tarafında ilgili mesajın onaylanması halinde yapacağımız işlemleri yapabiliyoruz.</p><p>Alttaki GIF ile akışı göstermeye çalıştım.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/646/1*o7GcRcYymRnacdhQN_4N3w.gif" /></figure><p>Aynı şey NACK için de geçerli. İlgili mesaj broker tarafından başarıyla işlenemediği durumda NACK bildirisi publisher tarafında BasicNacksAsync() metodunu tetikliyor. Aynı şeyler bu metod için de geçerli.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gH717vTQyHkVlmGNvfflPg.png" /><figcaption>NACK durumunda akışın nasıl olacağının temsili</figcaption></figure><p>Bu kısma kadar ACK ve NACK bildirilerinin nasıl ele alınacağından bahsetmiş oldum. Şimdi bahsetmek istediğim konu ise biraz önce değindiğim ‘broker tarafından başarıyla alınan fakat herhangi bir queue’ya route edilemeyen’ mesajlar ile ilgili.</p><p>Mesajları publisherdan farklı şekillerde gönderebiliyoruz. Bu şekiller amacımıza göre değişeceğinden mesajlar kimi zaman kesinlikle bir queue’ya route edilmesi gerekirken kimi zamanda route edilmesi şart değildir. Publisher tarafından gönderilen mesajın kesinlikle bir queue’ya route edilmesini istiyorsak mesajı gönderirken “mandatory:true” tanımlamasını yapmamız gerekir.</p><pre>await channel.BasicPublishAsync(<br>    exchange: string.Empty, <br>    routingKey: &quot;order-created.queue&quot;, <br>    mandatory: true, <br>    body: eventBody);</pre><p>Bu tanımlama ile birlikte ilgili mesaj herhangi bir queue’ya yönlendirilemediğinde publisher tarafında BasicReturnAsync() metodunu tetikleyecektir.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*4Xv6pn5DYdQNsYp7eYSd5g.gif" /><figcaption>Route edilemeyen mesajların BasicReturnAsync metodunu tetiklemesi</figcaption></figure><p>Unroutable mesajları publisher confirm’den ayrı şekilde değerlendirmemiz gerekir. Çünkü unroutable mesajlar broker tarafından başarıyla işlenmiş mesajlardır. Dolayısıyla “mesajım broker tarafından ACK aldı, bu yüzden brokerda illaki bir queue’ya yönlendirilmiştir” gibi bir yanılgıya düşmemeliyiz.</p><h4>3. Publisher Confirmation Tracking</h4><p>Buraya kadar publisher confirm mekanizmasının ACK ve NACK bildirimleri üzerinden nasıl çalıştığını gördük. Bu noktada kendimize şu soruyu sorabiliriz:</p><blockquote>Broker’dan gelen ACK veya NACK bilgisinin hangi publish işlemimize ait olduğunu nasıl takip edeceğiz?</blockquote><p>Bu gayet mantıklı bir soru. İncelediğimiz örneklerde birer mesaj üzerinden gösterim yaptık. Ya binlerce mesaj publish edeceksek bunların takibini nasıl yapacağız?</p><p>RabbitMQ .NET Client bu noktada bize<strong> publisherConfirmationTrackingEnabled</strong> adında bir özellik sunuyor.</p><p>Channel oluştururken bu özelliği şu şekilde aktif edebiliriz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/848/1*tQ6gUilOHBPSxbIsovNZnA.png" /></figure><p>Burada <strong>publisherConfirmationsEnabled</strong> ile Publisher Confirms mekanizmasını aktif ederken, <strong>publisherConfirmationTrackingEnabled </strong>ile RabbitMQ Client’ın confirmation işlemlerini bizim yerimize takip etmesini sağlıyoruz.</p><blockquote>Peki tracking özelliğini <strong>true</strong> yaptığımızda tam olarak ne oluyor?</blockquote><p>Tracking aktif olduğunda RabbitMQ .NET Client, publish ettiğimiz mesaj ile broker’dan gelen confirmation bilgisini kendi içerisinde eşleştiriyor. Böylece <strong>BasicPublishAsync()</strong> metodunu <strong>await</strong> ettiğimizde publish işleminin sonucunu doğrudan ilgili task üzerinden takip edebiliyoruz.</p><p>Örneğin:</p><pre>try<br>{<br>    await channel.BasicPublishAsync(<br>        exchange: string.Empty,<br>        routingKey: &quot;order-created.queue&quot;,<br>        mandatory: true,<br>        body: eventBody);<br><br>        Console.WriteLine(&quot;Message successfully confirmed.&quot;);<br>}<br>catch (Exception ex)<br>{<br>    Console.WriteLine($&quot;Publish failed: {ex.Message}&quot;);<br>}</pre><p>Bu yapıda mesaj ACK aldığında <strong>BasicPublishAsync()</strong> başarılı şekilde tamamlanır.</p><p>Mesaj NACK aldığında veya <strong>mandatory : true</strong> olduğu halde herhangi bir queue’ya route edilemediğinde ise publish işlemi exception ile sonuçlanır. Dolayısıyla tracking açıkken confirmation sürecinin önemli bir kısmını RabbitMQ Client bizim yerimize yönetiyor.</p><p>Client bunu nasıl yönetiyor diye düşünüyor olabiliriz. Tam da şu şekilde yönetiyor :</p><p>RabbitMQ.Client gönderdiği her bir mesaj için ilgili channel içerisinde lineer artan bir değişken tutar. Bu değişken değeri her iletilen mesaj ile birlikte birer birer artar. Publisher tarafında ilettiği her bir mesajın sequental number değerini kendi içerisinde saklar ve mesajı publish eder. Brokerdan dönen ACK/NACK bildirisinin argümanlarında ‘DeliveryTag’ değeri bulunur. Bu deliveryTag değeri de aslında mesaja atadığı sequental number’ın kendisidir. Bu değere göre gönderdiği mesajlardan hangisinin başarılı yada başarısız şekilde iletildiğini takip edebilir.</p><p>Bu oldukça kullanışlıdır. Çünkü gönderdiğimiz her mesaj için sequence number üretmek, pending mesajları saklamak ve ACK/NACK geldiğinde ilgili mesajı manuel olarak bulmak zorunda kalmayız.</p><p>Ancak bu kolaylığın karşılığında bazı kontrolleri de client’a bırakmış oluruz.</p><p>Örneğin tracking açıkken confirmation mekanizmasının iç detaylarına uygulama seviyesinde erişimimiz oldukça kısıtlanıyor.</p><p>Örneğin:</p><ul><li>Hangi mesajlar ACK aldı?</li><li>Hangi mesajlar hala pending durumda?</li><li>Connection koptuğu anda hangi mesajların durumu artık kesin olarak bilinemeyecek?</li><li>Recovery sonrasında hangi mesajları tekrardan publish etmeliyiz?</li></ul><p>İşte bu gibi sorulara cevap bulabilmek için publisher confirm’u kendimiz yönetmeliyiz. Kontrol elimizde olduğunda bu soruların cevaplarını kolaylıkla verebiliyor olacağız.</p><p>İsterseniz kontrolü elimize alarak devam edelim.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/768/1*0SFW9XEZr-PdB0Or94lWvA.png" /><figcaption>tracking’i manuel yöneteceğimizi belirtiyoruz.</figcaption></figure><p><strong>“publisherConfirmationTrackingEnabled : false” </strong>dediğimizde tracking’i rabbitMQ.Client üzerinden alıyor ve kendimizin yöneteceğini dikte ediyoruz.</p><p>Biraz önce RabbitMQ.Client’ın sequenceNumber’lar vesilesiyle ACK/NACK yönetimini nasıl yaptığından bahsetmiştim. Aynı mantığı kendimiz uygulayacağız. Gönderdiğimiz mesajların takibini yapabilmek için ConcurrentDictionary oluşturacağız. Mesajlarımızı publish etmeden önce bu dictionary’e atacağız ve brokerdan gelen ACK/NACK bilgisine göre dictionary’i güncelleyeceğiz. Böylece herhangi bir connection failure durumunda mesajların state’leri hakkında bilgiye sahip olacağız.</p><pre>var pendingMessages = new ConcurrentDictionary&lt;ulong, string&gt;();</pre><p>Göndereceğimiz mesajların takibini yapabilmek için bir dictionary oluşturduk.</p><p>Göndermek istediğimiz mesaj şu şekilde olsun.</p><pre>var message = new OrderCreatedEvent<br>{<br>    Id = Guid.NewGuid(),<br>    OrderId = Guid.NewGuid(),<br>    CreatedTime = DateTime.UtcNow<br>};</pre><p>Şimdi ise, göndereceğimiz mesajın takibini yapabilmek için ilgili mesaja atanacak olan sequenceNumber bilgisini alalım.</p><pre>var sequenceNumber = await channel.GetNextPublishSequenceNumberAsync();</pre><p>SequenceNumber bilgisini de aldık. Artık broker’a göndereceğimiz mesajın takibini yapabilmek için herşeye sahibiz. O zaman göndereceğimiz mesajın kaydını dictionary’e yapalım ve mesajımızı publish edelim.</p><pre>pendingMessages.TryAdd(<br>    sequenceNumber,<br>    JsonSerializer.Serialize(message));<br><br>await channel.BasicPublishAsync(<br>    exchange: &quot;&quot;,<br>    routingKey: &quot;order-created&quot;,<br>    body: Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message)));</pre><p>Mesajlarımızı yayınladığımızda pendingMessages şu şekilde olacak:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kJ5oMEw4HEWMUhanhFTUQw.png" /><figcaption>Oluşturduğumuz 10 adet mesajın pendingMessages içerisinde tutulmasının temsili</figcaption></figure><p>Bu noktada broker’a göndereceğimiz mesajları bir dictionary aracılığı ile takip ediyoruz. Peki ACK/NACK olduğunda pendingMessages nasıl güncellenmeli?</p><p>Sizin de tahmin edeceğiniz üzere daha önce bahsettiğimiz BasicAcksAsync/BasicNacksAsync metodları ile yapacağız:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/1*Xpq-NQLV9kBv4T2JGxLh4A.png" /></figure><p>Bu metodumuz ile birlikte broker mesajı sağlıklı şekilde aldığında ilgili mesajı pendingMessages’tan çıkarıyoruz. Dolayısıyla broker tarafında oluşabilecek herhangi bir fail anında broker’a tekrardan publish edilmesi gereken mesajlar pendingMessages içerisinde bekliyor olacak. Başarıyla iletilen mesajlar pendingMessages içerisinde bulunmayacağı için aynı mesajı tekrar tekrar göndermemiş olacağız. Burada kısmi idempotentlik sağlıyoruz diyebiliriz. Çünkü ilettiğimiz mesajı tekrardan göndermiyoruz. Tabii ki asıl idempotentlik davranışını consumer tarafında kazandıracağız. Buna da bu yazının diğer partlarında değineceğim.</p><h4>4. Connection Failure ve Unknown Mesajlar</h4><p>Yazımızın bu noktasına kadar broker’a gönderilen mesajların broker tarafından sağlıklı şekilde alınıp alınmamasıyla ilgili konuştuk.</p><p>Peki şunu düşündük mü :</p><blockquote>Publish ettiğim mesaj ya broker’a dahi ulaşamadıysa ne olacak? Yada broker’a ulaşan bir mesajdan ACK dönerken connection düşerse ne yapacağız?</blockquote><p>Broker ile olan bağlantımız herhangi bir anda koparsa ve biz o anda mesajlarımızı publish ediyorsak, mesajlarımızın broker’a başarılı ulaşıp ulaşmadığı ile ilgili kesin bir bilgi sahibi olamayız. Çünkü mesajımız henüz broker’a ulaşmadan bağlantı kopmuş olabilir. Yada mesajımız broker’a başarılı şekilde ulaşmış ve publisher’a ACK bildirisi yayınlanmışken bağlantı kopmuş olabilir.</p><p>Aklımızda canlandırmak amacıyla alttaki görseli inceleyebiliriz:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2sfVsnv3ddY2CavHASZFFQ.png" /><figcaption>Broker tarafından alınan mesajın ACK bildirisinin publisher’a ulaşmadığı senaryo</figcaption></figure><p>Üstteki görselde , broker tarafından alınan mesajın connection failure olması sebebiyle ACK bildirisini iletememesini, dolayısıyla ilgili mesajın hala pending durumunda olmasını ifade etmeye çalıştım.</p><h4>Peki biz ne yapacağız?</h4><p>Connection failure durumlarında <strong>‘mesajımı gönderdim ve broker’dan confirm bekliyorum’</strong> gibi senaryolarda pendingMessages’ta bulunan mesajları <strong>unknown</strong> olarak işaretlemeliyiz. Çünkü bu mesajların en az 1 kere iletilip iletilmediği garanti edilmediği için tekrardan publish edilmesi gerekir.</p><p>Unknown olarak işaretlediğimiz mesajları, connection recovery olduğunda tekrardan publish ederek mutlaka 1 kez broker’a pushlanmasını garanti etmeye çalışacağız ( at least one delivery ).</p><p>İsterseniz bu yapıyı da koda dökelim.</p><pre>public enum PublishState<br>{<br>    Pending,<br>    Unknown<br>}</pre><p>Artık pendingMessages’ta sadece ‘Pending’ durumundaki mesajları değil, aynı zamanda ‘Unknown’ olan mesajları tutacağız. bu sebeple bir enum ile bunu ifade edelim.</p><pre>class PendingPublish { <br>  public required string Body { get; init; } <br>  public PublishState State { get; set; } <br>}</pre><p>PendingMessages’ta artık sadece mesajın body’sini değil, aynı zamanda state’ini de tutacağız.</p><pre>var pendingMessages = new ConcurrentDictionary&lt;ulong, PendingPublish&gt;();</pre><p>Dictionary’in value değerini artık PendingPublish olarak tutacağız. Mesajların republish edilme işlemini de bu State değerine göre yapacağız.</p><pre>pendingMessages.TryAdd( sequenceNumber, new PendingPublish { <br>  Body = JsonSerializer.Serialize(message), <br>  State = PublishState.Pending });</pre><p>Mesajları publish etmeden önce dictionaryde tutuyorduk. Artık PendingPublish tutacağımız için pendingMessage’a kayıt ekleme işlemini de bu şekilde güncelleyelim.</p><p>Publisher olarak mesajlarımızı artık state’leri ile birlikte tutuyoruz. Bağlantı koptuğunda mesajlarımızın state’ini ‘unknown’ olarak işaretleyeceğimizden bahsetmiştik.</p><p><strong>Peki bunu nasıl yapacağız?</strong></p><blockquote>connection.ConnectionShutdownAsync() metoduyla.</blockquote><pre>connection.ConnectionShutdownAsync += async (_, args) =&gt;<br>{<br>    Console.WriteLine();<br>    Console.WriteLine(&quot;========== CONNECTION SHUTDOWN ==========&quot;);<br>    Console.WriteLine($&quot;ReplyCode : {args.ReplyCode}&quot;);<br>    Console.WriteLine($&quot;ReplyText : {args.ReplyText}&quot;);<br>    Console.WriteLine($&quot;Initiator : {args.Initiator}&quot;);<br>    Console.WriteLine($&quot;Cause     : {args.Cause?.Message}&quot;);<br><br>    var unknownCount = 0;<br><br>    foreach (var item in pendingMessages)<br>    {<br>        if (item.Value.State != PublishState.Pending)<br>            continue;<br><br>        item.Value.State = PublishState.Unknown;<br>        unknownCount++;<br><br>        Console.WriteLine(<br>            $&quot;[UNKNOWN] Seq={item.Key} | MessageId={item.Value.MessageId}&quot;);<br>    }<br><br>    Console.WriteLine(<br>        $&quot;[CONNECTION LOST] {unknownCount} pending message marked as UNKNOWN.&quot;);<br><br>    Console.WriteLine(&quot;=========================================&quot;);<br>    Console.WriteLine();<br><br>    await Task.CompletedTask;<br>};</pre><p>.ConnectionShutdownAsync() metodu da , çeşitli davranışlar ile tetikleniyor. Tıpkı ACK-NACK bildirimi gibi. Broker ile olan bağlantımız koptuğunda tetiklenen bu metod ile birlikte mesajlarımızın state’ini unknown olarak işaretleyeceğiz. Böylece recovery durumunda mesajlarımızı tekrardan gönderebilelim.</p><p>Buraya kadar çoğu adımımızı tanımladık. Mesajlarımızı gönderdik, confirm bekledik, herhangi bir connection fail durumunda da mesajlarımızın durumunu güncelleyerek tekrardan iletebilir hale geldik.</p><p>Peki bu mesajları ne zaman tekrardan iletebileceğiz diye sorarsak ta o anda bize ‘connection.RecoverySucceededAsync() yardımcı olacak.</p><blockquote>Not: ConnectionFactory oluşturulurken “AutomaticRecoveryEnabled = true” değeri default olarak true’dur. Bu sebeple recovery olduğunda ilk olarak channel’lar tekrardan oluşturulur. İlgili RecoverySucceededAsync() metodu bu aşamadan sonra çalışır durumda olacaktır.</blockquote><pre>connection.RecoverySucceededAsync += async (sender, args) =&gt;<br>{<br>    var unknownMessages = pendingMessages<br>        .Where(x =&gt; x.Value.State == PublishState.Unknown)<br>        .ToList();<br><br>    foreach (var item in unknownMessages)<br>    {<br>        var oldSequenceNumber = item.Key;<br>        var message = item.Value;<br><br>        pendingMessages.TryRemove(<br>            oldSequenceNumber,<br>            out _);<br><br>        var newSequenceNumber =<br>            await channel.GetNextPublishSequenceNumberAsync();<br><br>        message.State = PublishState.Pending;<br><br>        pendingMessages.TryAdd(<br>            newSequenceNumber,<br>            message);<br><br>        try<br>        {<br>            await channel.BasicPublishAsync(<br>                exchange: string.Empty,<br>                routingKey: &quot;order-created&quot;,<br>                mandatory: true,<br>                body: Encoding.UTF8.GetBytes(message.Body));<br><br>            Console.WriteLine(<br>                $&quot;[REPUBLISH] OldSeq={oldSequenceNumber} | &quot; +<br>                $&quot;NewSeq={newSequenceNumber}&quot;);<br>        }<br>        catch (Exception ex)<br>        {<br>            message.State = PublishState.Unknown;<br><br>            Console.WriteLine(<br>                $&quot;[REPUBLISH FAILED] Seq={newSequenceNumber} | &quot; +<br>                $&quot;Error={ex.Message}&quot;);<br>        }<br>    }<br>};</pre><p>Metodu bu şekilde tanımlayabiliriz. Dikkat ederseniz <strong>unknown</strong> durumundaki mesajların state’i pending olarak değiştirilip publish edilmiyor. Bunun sebebi sequence number’ların channel bazlı tanımlanmasıdır. Örneğin bir channel açıldı ve sequence numaralar 1 den başlayarak dağıtılmaya başlandı. Eğer connection koparsa ilgili channel de kapanır. Dolayısıyla ilgili sequence numberlar da eski bir channeldaki tanımlanan referansı gösterir. Buradaki sequence number’ı güncellemek oldukça önemlidir. Buradaki sequence number’ı güncellemezsek 2 farklı mesaj birbirlerini istemeden ACK edilmesine sebebiyet verebilir.</p><p>İsterseniz connection fail durumunda mesajlarımızın unkown olarak işaretlenmesini, ardından yaşanacak recovery ile birlikte de unknown mesajların republish edilmesini simüle edelim.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/930/1*25-k7KCI3njdY4dkDsnDeg.gif" /><figcaption>Broker ile olan bağlantı koptuğunda mesajların ‘Unknown’ olarak işaretleniyor.</figcaption></figure><p>Broker’ı tekrardan ayağa kaldırdığımızda ne olacak? Hadi bakalım.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/930/1*KzBGN0QP6TqozVwuFOW_CQ.gif" /></figure><p>Broker tekrardan aktif olduğunda mesajlarımız tekrardan publish edilmiş oldu.</p><p>Bu yazımda <strong>Publisher Confirms</strong>, <strong>ACK/NACK</strong>, <strong>mandatory:true</strong>, <strong>confirmation tracking</strong> ve <strong>connection failure</strong> durumlarında mesajların nasıl takip edilebileceği ve recovery sürecinde neler yapabileceğimiz ile ilgili öğrendiklerimden bahsetmek istedim.</p><p>Bu yazının devamı niteliğinde olacak <strong>Part 2</strong>’de ise, recovery sürecinin beraberinde getirebileceği <strong>Duplicate Message</strong> problemine odaklanacağız. Aynı mesajın consumer tarafında birden fazla kez işlenmesinin ne gibi sorunlara yol açabileceğini ve bu problemleri nasıl önleyebileceğimizi inceleyeceğiz.</p><p>Yazılarımda henüz yeni öğrendiğim şeylerden bahsediyorum. Dolayısıyla yanlış öğrendiğim/bahsettiğim yerler olabilir, mazur görünüz.</p><p>İyi okumalar dilerim.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bf3a658fb809" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/rabbitmq-reliability-part-1-publisher-confirms-connection-failure-recovery-bf3a658fb809">RabbitMQ Reliability — Part 1: Publisher Confirms, Connection Failure &amp; Recovery</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[D365 F&O: SysOperation Framework kullanımı ve RunBaseBatch ile farkları.]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/kodcular/d365-f-o-sysoperation-framework-kullan%C4%B1m%C4%B1-ve-runbasebatch-ile-farklar%C4%B1-bf9090d7a0c2?source=rss----ff2de8699413---4"><img src="https://cdn-images-1.medium.com/max/2600/1*AlRqcsQEJzVr0hn6z5JWow.jpeg" width="2752"></a></p><p class="medium-feed-snippet">Merhaba arkada&#x15F;lar,
Dynamics AX 2012 g&#xFC;nlerinden beri hayat&#x131;m&#x131;zda olan RunBaseBatch s&#x131;n&#x131;f&#x131;, y&#x131;llarca arka plan i&#x15F;lemlerimizin (batch jobs)&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/kodcular/d365-f-o-sysoperation-framework-kullan%C4%B1m%C4%B1-ve-runbasebatch-ile-farklar%C4%B1-bf9090d7a0c2?source=rss----ff2de8699413---4">Continue reading on Kodcular »</a></p></div>]]></description>
            <link>https://medium.com/kodcular/d365-f-o-sysoperation-framework-kullan%C4%B1m%C4%B1-ve-runbasebatch-ile-farklar%C4%B1-bf9090d7a0c2?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/bf9090d7a0c2</guid>
            <category><![CDATA[dynamics-365-for-finance]]></category>
            <category><![CDATA[dynamics-ax]]></category>
            <category><![CDATA[dynamics-365-finance]]></category>
            <category><![CDATA[d365-fo]]></category>
            <category><![CDATA[microsoft-dynamics-365]]></category>
            <dc:creator><![CDATA[Semih Çelikol]]></dc:creator>
            <pubDate>Fri, 21 Aug 2026 13:31:11 GMT</pubDate>
            <atom:updated>2026-10-14T20:50:36.290Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Feature-Driven Proje Yapısı]]></title>
            <link>https://medium.com/kodcular/feature-driven-proje-yap%C4%B1s%C4%B1-e25f6b6b78d6?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/e25f6b6b78d6</guid>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[feature-driven]]></category>
            <category><![CDATA[virastack]]></category>
            <dc:creator><![CDATA[Ömer Gülçiçek]]></dc:creator>
            <pubDate>Mon, 03 Aug 2026 11:27:40 GMT</pubDate>
            <atom:updated>2026-08-03T11:27:39.255Z</atom:updated>
            <content:encoded><![CDATA[<p>React projelerine başlarken genellikle , ve gibi teknik klasörler oluştururuz. Ancak proje büyüdükçe, hangi bileşenin hangi özellikle ilgili olduğunu bulmak zorlaşır ve bu klasörler birer çöplüğe dönüşür. Bu yazıda, kodu teknik türüne göre değil, işlevine göre gruplayan <strong>feature-driven</strong> ( <em>özellik odaklı</em>) mimariyi ele alacağız.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VFQecY5ikvQpaMxP.png" /></figure><h3>Birlikte Değişen Kodlar, Birlikte Yaşamalı</h3><p><a href="https://tkdodo.eu/blog/">TkDodo</a> ve <a href="https://overreacted.io/">Dan Abramov</a> gibi isimlerin sıkça vurguladığı temel bir kural vardır: Birlikte değişen kodlar, birlikte yaşamalıdır. Birçok projede ile hook’ları aynı klasörde dururken, ile bileşeni ayrı yerlerde bulunur. Bu durum küçük projelerde sorun yaratmasa da, proje büyüdükçe kodun takibini zorlaştırır ve ölçeklenebilirliği düşürür.</p><p>Feature-driven yaklaşım, uygulamanın her bir özelliğini kendi içinde bağımsız birer mini-uygulama gibi tasarlar.</p><pre>src/<br>├── features/<br>│   ├── auth/<br>│   │   ├── api/<br>│   │   ├── components/<br>│   │   ├── hooks/<br>│   │   ├── schemas/<br>│   │   └── index.ts<br>│   └── dashboard/<br>├── components/<br>│   └── shared/<br>└── lib/</pre><p>Bu yapı sayesinde, projenin klasörlerine baktığınızda uygulamanın sadece React ile yazıldığını değil, <strong>ne işe yaradığını</strong> doğrudan anlarsınız.</p><h3>Sınırlar ve Public API</h3><p>Bu mimarinin en güçlü yanı sınırları net çizmesidir. Her feature klasöründeki dosyası, o özelliğin dışarıya açılan kapısıdır (Public API).</p><pre>// src/features/auth/index.ts<br>// Sadece dışarıdan erişilmesi gerekenleri dışa aktarıyoruz<br>export { LoginForm } from &quot;./components/login-form&quot;<br>export { useAuth } from &quot;./hooks/use-auth&quot;<br>export { loginSchema } from &quot;./schemas/auth.schema&quot;<br> <br>// utils/ veya private bileşenler dışa aktarılmaz!</pre><p>Bir feature, başka bir feature’ın içindeki private bir bileşeni doğrudan import edemez. Sadece üzerinden izin verilen bileşenlere erişilebilir. Bu kural ESLint ile otomatik denetlenerek spagetti kod oluşumu engellenir.</p><p>Eğer bir bileşen, hook veya şema birden fazla feature tarafından kullanılmaya başlanırsa, o kod klasöründen çıkarılıp veya gibi global katmanlara taşınır. Böylece hem kod tekrarı önlenir hem de sınırlar korunur.</p><h3>Küçük Projeler İçin Uygun Mu?</h3><p>Her mimari kararın bir bedeli vardır. 3–5 sayfalık basit bir portfolyo veya blog sitesi için her özelliği alt klasörlere bölmek ve üzerinden dışa aktarmak gereksiz bir iş yükü (boilerplate) yaratır.</p><p>Küçük projelerde tip tabanlı basit bir yapı dosyaları bulmak için yeterlidir. Feature-driven yapı, küçük bir projede geliştirme hızını artırmak yerine yavaşlatır. Ölçeklenebilirlik ihtiyacı doğduğunda bu geçişi yapmak daha mantıklıdır.</p><h3>Kuralları Otomatize Etmek</h3><p>Feature-driven mimariyi manuel olarak korumak, özellikle kalabalık ekiplerde zordur. Sürekli “bu dosya nereye gidecek” veya “ortak klasöre ne zaman taşınacak” tartışması yaşanır.</p><p>Bu noktada, mimari kuralları yapay zekâ asistanlarına (Cursor vb.) otomatik uygulatan <a href="https://github.com/virastack/ai">ViraStack AI</a> paketini kullanabilirsiniz. Projenizin mimari standartlarını belirler, klasör yapınızı korur ve AI’ın bu kuralların dışına çıkarak projeyi çöplüğe çevirmesini engeller.</p><p><em>Originally published at </em><a href="https://www.omergulcicek.com/blog/feature-driven-proje-yapisi"><em>https://www.omergulcicek.com</em></a><em> on July 9, 2026.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e25f6b6b78d6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/feature-driven-proje-yap%C4%B1s%C4%B1-e25f6b6b78d6">Feature-Driven Proje Yapısı</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Yapay Zeka Çağında Körelmemek: Bir Yazılımcı Olarak Kendimi Nasıl Güncel Tutuyorum?]]></title>
            <link>https://medium.com/kodcular/yapay-zeka-%C3%A7a%C4%9F%C4%B1nda-k%C3%B6relmemek-bir-yaz%C4%B1l%C4%B1mc%C4%B1-olarak-kendimi-nas%C4%B1l-g%C3%BCncel-tutuyorum-e7fea2388482?source=rss----ff2de8699413---4</link>
            <guid isPermaLink="false">https://medium.com/p/e7fea2388482</guid>
            <category><![CDATA[leetcode]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Metin Ağaoğlu]]></dc:creator>
            <pubDate>Wed, 08 Jul 2026 23:46:25 GMT</pubDate>
            <atom:updated>2026-07-08T23:46:23.798Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YxfNH4xKhtkPeKVB.png" /></figure><p>Yapay zeka çağının hayatımıza girmesiyle birlikte kod yazma alışkanlıklarımız ciddi derecede değişti. Aslına bakarsanız, bazen elimizden kod yazmanın o saf zevkini aldıklarını hissediyorum. Eskiden bir web sayfasını, mobil uygulamayı ya da bir API endpoint’ini satır satır, handler’larıyla, şemalarıyla kendimiz yazardık. Şimdi ise daha ilk satırı yazarken Claude veya OpenAI fark etmeksizin, iyi bir context verdiğimizde yapay zekanın bizden çok daha iyi dökümante edilmiş bir kod yazacağını biliyoruz. Bu durum, daha ilk satırdan itibaren insanın kod yazma hevesini kırabiliyor, devam edesi gelmiyor.</p><p>Bu durum çok sinsi bir soruna yol açıyor: Uzun zaman boyunca sadece agent’ları orkestre ederek, yani onları yönetip yönlendirerek projelerde ilerleyince, en basit bir for döngüsünde veya dilin kendi syntax detaylarında bile el antrenmanımız gidiyor. Çünkü artık kod yazmak eski zevkini vermiyor ve tembelliğe alışıyoruz.</p><p>Peki, yapay zeka sonrası dönemde bir yazılım geliştirici olarak kendimi geriye gitmekten nasıl koruyorum ve geliştirmeye devam ediyorum? Kendi hayatımda uyguladığım yöntemi 4 basit adımda anlatmak istedim.</p><h3>1. El Antrenmanını Kaybetmemek (LeetCode &amp; HackerRank Deneyimi)</h3><p>Artık yapay zekayı tamamen hayatımızdan çıkarmak ve her şeyi sıfırdan yazmaya çalışmak gerçekçi bir seçenek değil. Ancak o mesleki reflekslerimizin ve pratikliğimizin de yok olmasını istemiyoruz. Bu sorun için kendi geliştirdiğim bir rutini paylaşayım.</p><p>LeetCode ve HackerRank gibi platformlardaki algoritma sorularına eskisinden çok daha fazla önem vermeye başladım. Yapay zeka öncesinde de bu soruları çözer, bazen takılır, araştırarak bir sonuca ulaşırdım. Şimdi bir soruyu AI’a verdiğinizde saniyeler içinde çözümü önünüze koyuyor. Ama bunu yaptığımızda ne beynimiz çalışıyor ne de elimiz klavyeye alışıyor.</p><p>Yapay zeka döneminde hep söylenen bir şey var: Önemli olan sistem kurmak, orkestre etmek, yapay zeka yapsa bile arka planda ne yaptığını bilmek… Yani computer science ve algoritmalar hâlâ çok önemli.</p><p>Ben de bu yüzden haftada birkaç kez, yapay zekadan <em>çözüm aşamasında</em> hiçbir yardım almadan bu platformlardan soru çözüyorum. Bunu tek başıma yaparken hem işin algoritma mantığına odaklanıyorum hem de ana dilim olarak kullandığım Golang ve JavaScript gibi dillerin syntax kısımlarında el antrenmanımı güncel tutuyorum. Çözümü yaparken de düşünce şeklimi sanki teknik bir mülakattaymışım gibi açıklama satırlarına (comment) tek tek yazıyorum.</p><p><strong>Altın kuralım şu: Soruyu çözerken kesinlikle yardım almıyorum.</strong></p><p>Soruyu kendi başıma çözüp bitirdikten sonra yapay zeka tekrar devreye giriyor. Yazdığım kodu ve açıklama satırlarındaki düşünce yapımı alıp farklı LLM modellerine besliyorum. “Mülakatta olsam bunları söyleyecektim, şimdi beni değerlendir” diyorum. AI beni algoritma mantığı, programlama diline yatkınlık ve Big(O) karmaşıklığı gibi konularda analiz ediyor.</p><p>Özellikle Medium ve Hard sorularda mutlaka bir eksiğim çıkıyor. Soruyu çözmüş olsam bile AI ile en optimal yolu tekrar öğrenip çözümümü yukarı çekiyorum. Varsa LLM modeliyle birlikte follow-up sorularını veya “Soruda verilen parametre limiti daha fazla olsaydı ne yapardık?” kısmını tartışıyoruz. Verdiğim prompt gereği bana tartışma sırasında asla doğrudan kod vermiyor, sadece ipuçları vererek beni optimal çözüme ulaştırmaya çalışıyor.</p><p>Ek bir avantajı da şu: Büyük bir uygulama veya network servisi yaparken LLM’in daha iyisini yapacağını bildiğiniz için hevesiniz kaçabiliyor demiştim ya, bu algoritma sorularında hedef tek bir odak noktası olduğu ve bir metodun core (çekirdek) kısmını yazdığınız için canınız hiç sıkılmıyor. Hem zihnim çalışıyor hem de el antrenmanım güncel kalıyor. Hatta bazen kendimi zorlamak için tamamen farklı konular ve farklı programlama dilleriyle de denemeler yapıyorum.</p><h3>2. Sektörün Kült Kitaplarını Derinlemesine Okumak</h3><p>Daha önce okumak isteyip de bir türlü fırsat ya da zaman bulamadığım o kalın, sektörel kitapları artık yapay zeka ile birlikte çok daha verimli okuma fırsatı yakalıyorum.</p><p>NotebookLM gibi sistemler sayesinde bu kitapları part part, bölüm bölüm içeri alıyorum. Okurken merak ettiğim ya da havada kalan konularda hem yapay zekanın gücünü hem de internetteki kaynakları kullanarak derinleşiyorum. Bunu yapay zeka öncesinde yapmak, saatlerce süren kaynak taramaları anlamına geliyordu ve çok zaman alıyordu.</p><p>Şu anda ise süreç inanılmaz hızlı işliyor. Bu sayede beynimi tekrar çalıştırmış oluyorum. Daha önce araştırması gözümde büyüyen konulara artık çok daha hızlı giriş yapıp, proof of concept (PoC) çalışmalarını çok hızlı hazırlayabiliyorum. Özetle, daha hızlı ve daha derin öğreniyorum.</p><h3>3. Low-Level Konular ve Network Programlama</h3><p>Daha önce denemek istediğim ancak zaman azlığından, fırsat bulamamaktan ya da daha doğrusu internette yeterince temiz kaynak olmamasından dolayı giremediğim alanlar vardı: Binary inceleme, low-level programlama dilleri, network programlama gibi konular… Artık bunlara çok daha hızlı dalış yapabiliyorum.</p><p>Örneğin önceki şirketimde ihtiyaç duyduğumuz spesifik bir protokolü araştırmıştım. Genellikle bu tarz konularda internette hazır döküman veya kaynak bulmak çok zor oluyor. O dönem bir proof of concept hazırlamam günlerce sürmüştü.</p><p>Ancak artık LLM’ler sayesinde bu protokolün tam olarak ne olduğunu, içinde ne taşıdığını, RFC standartlarını ve OSI modelini hızlıca özetleterek bulabiliyorum. Çok hızlı bir demo hazırlayıp protokolün mantığını kavrayabiliyorum. Aslında bu durum, yapay zekanın biz geliştiricilere verdiği muazzam bir süper güç.</p><h3>4. Büyük Open Source Projeleri Analiz Etmek</h3><p>İncelemek istediğim ama kod tabanının büyüklüğü ve vakit darlığı yüzünden gözümün korktuğu açık kaynaklı (open source) projeleri artık yapay zeka ile birlikte analiz ediyorum.</p><p>Özellikle GitHub’da bulunan, uzun zamandır bakmak istediğim ancak çok zamanımı alacağını düşündüğüm için ertelediğim büyük projelere yapay zeka modelini de ortak ediyorum. Kod tabanını açıp projenin tam olarak ne iş yaptığını kendime özetletiyorum. Projenin core metotlarını ve modüllerini bulduruyorum. İçeride hangi algoritmaların, nasıl bir mimariyle kullanıldığını çözümlüyorum.</p><p>Yapay zeka öncesinde bu kadar büyük codebase’lerin içinde kaybolup gitmek çok kolayken ve inanılmaz zaman alıyorken, artık süreci çok daha hızlı ve efektif bir şekilde yönetebiliyorum. Bu da doğrudan mimari vizyonumu geliştiriyor ve kendime yeni şeyler katmamı sağlıyor.</p><p><em>Özetlemek gerekirse; yapay zeka elimizden amelelik diye tabir edebileceğimiz uzun kod bloklarını yazma işini aldı evet, ama bizi daha stratejik ve derin düşünmeye zorluyor. Önemli olan bu araçları bizi tembelleştiren birer unsur olarak değil, bizi yukarı taşıyan birer kaldıraç olarak kullanabilmek.</em></p><p><em>Originally published at </em><a href="https://metinagaoglu.com/yapay-zeka-caginda-korelmemek-bir-yazilimci-olarak-kendimi-nasil-guncel-tutuyorum/"><em>https://metinagaoglu.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e7fea2388482" width="1" height="1" alt=""><hr><p><a href="https://medium.com/kodcular/yapay-zeka-%C3%A7a%C4%9F%C4%B1nda-k%C3%B6relmemek-bir-yaz%C4%B1l%C4%B1mc%C4%B1-olarak-kendimi-nas%C4%B1l-g%C3%BCncel-tutuyorum-e7fea2388482">Yapay Zeka Çağında Körelmemek: Bir Yazılımcı Olarak Kendimi Nasıl Güncel Tutuyorum?</a> was originally published in <a href="https://medium.com/kodcular">Kodcular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>