İçeriğe Atla
Mustafa Erbay
Teknoloji · 12 dk okuma · görüntülenme Read in English

Dependency Yönetimi: Monorepo mu, Polyrepo mu? Benim Seçimlerim

Yazılım projelerinde dependency yönetimi için monorepo ve polyrepo yaklaşımlarını kendi tecrübelerimle karşılaştırıyorum. Avantajlar, dezavantajlar ve pratik…

100%

Yazılım geliştirme yaparken, projenin büyüklüğü ve ekibin dinamikleri ne olursa olsun, bir noktada bağımlılık yönetimiyle ilgili temel bir karar vermek gerekiyor: Kod tabanını tek bir büyük depoda (monorepo) mı yoksa her bir bileşeni ayrı bir depoda (polyrepo) mı tutacağız? Bu, basit bir klasör yapısı seçimi gibi görünse de, aslında geliştirme hızından deploy güvenilirliğine, hatta ekip içi iletişime kadar birçok alanı etkileyen stratejik bir karar.

Yirmi yıldır sistemler ve yazılımlarla uğraşırken, hem çok parçalı polyrepo yapılarında kaybolduğum, hem de monoreponun getirdiği karmaşıklıklarla boğuştuğum zamanlar oldu. Her iki yaklaşımın da kendine göre avantajları ve dezavantajları var. Bu yazıda, kendi deneyimlerimden yola çıkarak bu iki yaklaşımı karşılaştıracak ve hangi durumlarda neyi tercih ettiğimi, nedenleriyle birlikte anlatacağım.

Polyrepo Yaklaşımı: Parçalı Yönetimin Zorlukları

Polyrepo yaklaşımı, adından da anlaşılacağı gibi, her bir servisin, uygulamanın veya kütüphanenin kendine ait ayrı bir kod deposunda tutulması anlamına geliyor. Bu, özellikle mikroservis mimarileriyle birlikte popülerleşen, “her şey kendi başına yaşasın” felsefesini destekleyen bir yapı.

Bu yaklaşımın ilk bakışta getirdiği bazı bariz avantajlar var. Her bir depo, kendi başına bağımsız bir yaşam döngüsüne sahip olabiliyor. Kendi CI/CD pipeline’ı, kendi versiyonlama stratejisi ve kendine özgü bağımlılıkları olabilir. Bu durum, özellikle farklı ekiplerin farklı ürünler üzerinde çalıştığı büyük organizasyonlarda, ekiplere daha fazla özerklik sağlıyor. Her ekip kendi temposunda ilerleyebilir, kendi teknoloji stack’ini seçebilir ve diğer projelerden bağımsız olarak deploy yapabilir. Ancak bu özerkliğin bir bedeli oluyor.

Ortak bir kütüphaneyi (örneğin bir entegrasyon modülünü) güncellemek gerektiğinde, bu modülü kullanan çok sayıda mikroservisi tek tek güncellemek ve deploy etmek zorunda kalırsınız. Her bir servis için ayrı bir pull request oluşturmak, ayrı testleri çalıştırmak ve ayrı deploy süreçlerini yönetmek, testlerle birlikte ciddi zaman alıyor. Bu durum, küçük bir değişiklik için bile gereksiz bir operasyonel yük oluşturuyor. Ayrıca, farklı depolarda çalışan CI/CD pipeline’larını senkronize etmek ve yönetmek, zamanla karmaşık bir yapıya dönüşebiliyor. Her depo için ayrı bir Jenkinsfile veya GitHub Actions konfigürasyonu tutmak, zamanla tutarsızlıkları da beraberinde getirebiliyor.

# Örnek bir polyrepo'da bağımlılık güncellemeleri
# Microservice A
cd microservice-a
npm update [email protected]
npm test
git commit -m "Update common-library to 1.2.0"
git push

# Microservice B (aynı kütüphaneyi kullanıyor ama farklı bir ekip yönetiyor)
cd microservice-b
# Burada 1.2.0 yerine 1.1.0'da kalmış olabilirler, veya güncellediklerinde farklı bir hata çıkabilir.
npm update [email protected]
npm test
# Testler başarısız olursa, debug süreci başlıyor...

Bu tarz senaryolarda, özellikle shared kütüphanelerin sık güncellendiği veya tüm uygulamaların aynı anda uyumlu olması gereken durumlarda polyrepo yaklaşımı beni çok zorladı. Bir değişiklik birden fazla depoyu etkilediğinde, bu değişikliklerin koordinasyonu ve atomik olarak deploy edilmesi neredeyse imkansız hale geliyor.

Monorepo Yaklaşımı: Bütüncül Bir Bakış

Monorepo, tüm projelerin, kütüphanelerin ve servislerin tek bir versiyon kontrol deposunda tutulduğu yaklaşımdır. Bu, ilk duyulduğunda “tüm yumurtaları tek sepete koymak” gibi gelse de, doğru araçlar ve stratejilerle yönetildiğinde ciddi avantajlar sağlayabiliyor.

Benim için monorepo’nun en büyük artısı, bağımlılık yönetiminin basitleşmesi. Tüm kod tek bir yerde olduğu için, shared kütüphanelerin veya iç modüllerin tek bir versiyonunu kullanmak doğal hale geliyor. Bu, “dependency hell” senaryolarının önüne geçiyor. Bir üretim ERP’sinde, üretim planlama, operatör ekranları ve raporlama modülleri gibi kritik birimlerin tümü aynı kod tabanında olduğunda, ortak bir güvenlik modülünü güncellediğimde tüm bağımlı uygulamaların aynı anda tek bir PR ile güncellenmesi, denetim ve dağıtım süreçlerini inanılmaz kolaylaştırdı.

Kendi yan ürünümün backend’inde, tüm servislerimi tek bir monorepo altında topladığımda, bağımlılık çakışmaları belirgin şekilde azaldı diyebilirim. Ayrıca, farklı projeler arasında kod paylaşımı ve yeniden kullanım çok daha kolay hale geliyor. Bir projeden diğerine kod kopyalamak yerine, doğrudan import edebilir veya ortak bir kütüphane oluşturup bunu tüm projelerde kullanabilirim. Bu, kod kalitesini artırır ve tekrar eden işleri azaltır.

Ancak monorepo’nun da kendi zorlukları var. Repo boyutu zamanla çok büyüyebilir. Bir ERP projesinde monorepo boyutu epeyce şiştiğinde, git clone süreleri uzayabiliyordu. Bu da yeni ekip üyelerinin onboarding süresini uzatıyor ve hatta günlük geliştirme akışını yavaşlatabiliyor. Ayrıca, doğru tooling olmadan monorepo yönetimi bir kabusa dönüşebilir. Her commit’te tüm monoreponun testlerini çalıştırmak, CI pipeline sürelerini ciddi şekilde uzatabiliyordu. Bu durum, hızlı geri bildirim almayı imkansız hale getiriyordu.

# Örnek pnpm-workspace.yaml dosyası (monorepo tooling örneği)
packages:
  - 'apps/*' # Uygulamalarım: web, admin, api
  - 'packages/*' # Ortak kütüphanelerim: ui-kit, common-utils, types

Bu yapı sayesinde pnpm install komutu, tüm projelerimin bağımlılıklarını tek seferde ve oldukça hızlı bir şekilde yönetebiliyor. İlk başlarda CI sürecinde yaşadığım yavaşlıkları, Nx veya Turborepo gibi araçların affected komutları sayesinde aşmayı başardım. Sadece değişen projelere ait testleri ve build’leri çalıştırdığımda, CI döngüleri belirgin şekilde hızlandı. Bu, monorepo’nun potansiyel dezavantajlarını doğru araçlarla nasıl avantaja çevirebileceğimi gösterdi.

Bağımlılık Yönetiminde Pratik Çözümler ve Araçlar

Her iki yaklaşımın da kendine özgü zorlukları olduğunu gördüğüm için, bağımlılık yönetiminde kullandığım pratik çözümler ve araçlar büyük önem taşıyor. Amacım, karmaşıklığı azaltırken güvenilirliği artırmak.

Polyrepo senaryolarında, versiyon çakışmalarını en aza indirmek için Semantic Versioning (SemVer) kurallarına sıkı sıkıya bağlı kalıyorum. Ayrıca, bağımlılıkları sabitlemek (pinning) için araçlar kullanıyorum. Örneğin, Python projelerinde pip-compile ile requirements.txt dosyalarını otomatik olarak oluşturup, tüm transitif bağımlılıkları da kesin versiyonlarıyla belirliyorum. Birden çok mikroservisin aynı bağımlılıkları paylaştığı durumlarda bu yöntem, production ortamındaki versiyon sapmalarından kaynaklanan sorunları büyük ölçüde engelliyor. JavaScript ekosisteminde ise package-lock.json veya yarn.lock dosyalarının commit edilmesi bu amaca hizmet ediyor.

# Python projesinde bağımlılıkları sabitleme örneği
# requirements.in dosyam
flask
requests==2.28.1

# pip-compile komutu ile requirements.txt oluşturma
pip-compile requirements.in -o requirements.txt

# Oluşan requirements.txt içeriği (örnek)
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    pip-compile requirements.in
#
flask==2.3.2
    itsdangerous<2.2,>=2.1.0
    Jinja2<3.2,>=3.0.3
    Werkzeug<2.4,>=2.3.3
requests==2.28.1
    charset-normalizer<3,>=2
    idna<4,>=2.5
    urllib3<1.27,>=1.21.1

Monorepo tarafında ise, modern JavaScript ekosistemindeki workspaces özellikleri hayatımı kolaylaştırıyor. npm workspaces, yarn workspaces veya benim favorim olan pnpm workspaces sayesinde, tek bir depo içinde birden fazla proje ve kütüphaneyi yönetebiliyorum. Kendi bir yan ürünümün React frontend ve FastAPI backend’ini içeren monorepoda, pnpm workspaces kullanarak bağımlılıkları tek bir yerden yönetiyorum. pnpm install komutu tüm bağımlılıkları kısa sürede yüklüyor ve disk üzerinde de yerden tasarruf sağlıyor.

Monorepoları daha verimli hale getirmek için Nx, Turborepo veya Bazel gibi özel build sistemleri kullanıyorum. Bu araçlar, sadece değişen kod parçalarını tespit ederek, CI/CD pipeline’larında sadece ilgili testleri ve build’leri çalıştırmamı sağlıyor. Nx’in affected komutlarıyla yalnızca değişen projelere ait testleri ve build’leri çalıştırmak, CI döngülerini belirgin şekilde hızlandırıp uzun pipeline sürelerini kayda değer ölçüde kısaltabiliyor. Bu optimizasyonlar olmadan monorepo’nun faydalarını tam olarak görmek mümkün değil.

Ayrıca, bağımlılıkların otomatik olarak güncellenmesini sağlayan Dependabot gibi servisleri de kullanıyorum. Bu, güvenlik açıklarını kapatmak ve kütüphaneleri güncel tutmak için kritik. Ancak, otomatik güncellemelerin test süreçlerinden geçmesi ve olası breaking change’lere karşı dikkatli olunması gerekiyor. Bu araçlar, manuel bağımlılık yönetimi yükünü önemli ölçüde azaltıyor.

Karar Verirken Göz Önünde Bulundurmam Gerekenler

Monorepo ve polyrepo arasında seçim yaparken, sadece teknik detaylara değil, aynı zamanda organizasyonel ve operasyonel faktörlere de bakmak gerekiyor. Kendi projelerimde ve çalıştığım şirketlerde bu kararları verirken şunları göz önünde bulunduruyorum:

  1. Ekip Büyüklüğü ve Yapısı: Küçük, tek bir ekiple çalışan projelerde monorepo’nun getirdiği basitlik ve hız genellikle daha avantajlı oluyor. Herkes aynı kod tabanında olduğu için koordinasyon daha kolay. Ancak, farklı ürünler veya iş alanları üzerinde çalışan büyük, bağımsız ekipler için polyrepo’lar daha fazla özerklik sağlayabilir. Yine de, büyük ekiplerde de monorepo’yu doğru araçlarla (Nx gibi) yönetmek, ortak kütüphanelerin tutarlılığını sağlamak açısından faydalı olabilir.

  2. Projenin Büyüklüğü ve Karmaşıklığı: Projenin ne kadar ortak bileşeni var? Bu bileşenler ne sıklıkla değişiyor? Eğer çok sayıda shared kütüphane ve modül varsa ve bunlar sık sık güncelleniyorsa, monorepo’nun atomic commit yeteneği büyük bir avantaj sağlar. Eğer projeler birbirinden tamamen bağımsız ve çok az ortak nokta içeriyorsa, polyrepo daha mantıklı olabilir. Kendi Android spam uygulamamın native kodu ile bir yan ürünümün web tabanlı finansal hesaplayıcısının kodunun aynı repoda olması pek mantıklı değil, çünkü teknoloji stack’leri ve bağımlılıkları tamamen farklı.

  3. CI/CD Süreçleri ve Gerekli Performans: Build ve deploy süreleri ne kadar kritik? Monorepo’da her ne kadar akıllı build sistemleri ile optimizasyon yapılabilse de, başlangıçta ve doğru araçlar olmadan CI/CD süreçleri yavaşlayabilir. Bir projemde, build sürelerinin uzaması kabul edilemezdi. Bu yüzden monoreponun getirdiği incremental builds gibi build optimizasyon yetenekleri kritik hale geldi. Polyrepo’da ise her proje için ayrı pipeline’lar kurmak ve bunları yönetmek operasyonel olarak daha fazla efor gerektirebilir.

  1. Güvenlik Endişeleri: Tek bir depoda tüm kodun tutulması, teorik olarak tek bir güvenlik ihlalinin tüm projelere erişim sağlaması riskini barındırır. Polyrepo’da ise risk daha dağıtılmıştır. Ancak, iyi bir güvenlik pratiği ve erişim kontrolü ile bu risk monorepo’da da yönetilebilir. Önemli olan, risk toleransını ve uygulanacak güvenlik katmanlarını doğru belirlemektir.

  2. Organizasyonel Kültür: Ekipler özerkliği mi, yoksa sıkı koordinasyonu mu tercih ediyor? Bu, genellikle göz ardı edilen ama çok önemli bir faktör. Eğer ekipler kendi bağımsızlıklarına çok düşkünse, monorepo’ya geçiş süreci kültürel olarak zorlayıcı olabilir. Ancak, ortak hedeflere ulaşmak için ekipler arası işbirliğinin teşvik edildiği bir ortamda monorepo daha kolay benimsenebilir.

Bu faktörleri bir araya getirerek, her projenin kendine özgü ihtiyaçlarına göre en uygun yaklaşımı belirlemeye çalışıyorum. Her zaman tek bir doğru cevap olmadığını kendi tecrübelerimle gördüm.

Benim Tercihlerim ve Nedenleri

Monorepo ve polyrepo yaklaşımlarını defalarca denedim ve sonunda kendi adıma bazı net tercihler geliştirdim. Artık çoğu senaryoda monorepo’ya daha yakın duruyorum, özellikle de birbiriyle ilişkili ve ortak kütüphaneler kullanan projeler için.

Neden mi? Çünkü monorepo’nun getirdiği atomik değişiklik yapabilme ve birleşik bağımlılık yönetimi faydaları, modern monorepo araçlarıyla (Nx, Turborepo) aşılabilecek tooling karmaşıklığının çok ötesine geçiyor. Özellikle bir üretim planlama gibi kritik birimlerin olduğu bir ERP projesinde, üretim hattını etkileyebilecek bir bağımlılık değişimini tek bir PR içinde tüm operatör ekranları ve raporlama modülleriyle birlikte test edip deploy edebilmek benim için çok büyük bir avantaj. Bu, olası hataların kapsamını daraltıyor ve rollback süreçlerini basitleştiriyor.

Kendi yan ürünlerimin çoğunda, web frontend, API backend ve ortak kütüphaneleri tek bir repoda tutuyorum. Bu, geliştirme hızımı ve deploy güvenilirliğimi artırdı. Örneğin, kendi siteme yaptığım finansal hesaplayıcılarda, hem frontend hem de backend’in aynı tip tanım dosyalarını (TypeScript types) kullanması, monorepo sayesinde çok daha kolay hale geliyor. Bir tip tanımını değiştirdiğimde, hem frontend hem de backend’in bu değişikliğe uyum sağladığından emin olmak tek bir PR ile mümkün oluyor.

Ancak, her zaman monorepo tercih etmiyorum. Çok farklı teknoloji stack’lerine sahip, birbiriyle neredeyse hiç etkileşmeyen ve farklı ekipler tarafından yönetilen bağımsız ürünler için polyrepo hala mantıklı olabilir. Örneğin, dediğim gibi, Android spam uygulamamın native kodu ile bir yan ürünümün web tabanlı finansal hesaplayıcısının kodu aynı repoda olmamalı. Bu iki proje arasındaki bağımlılık neredeyse sıfır ve farklı platformlarda, farklı dillerde yazılmışlar. Bu durumda monorepo’nun getireceği tek şey gereksiz karmaşıklık olurdu.

Bazen de “hybrid” yaklaşımlar benimsiyorum. Yani, mantıksal olarak bir monorepo gibi davranan ama fiziksel olarak ayrı repoları olan bir yapı. Bu, genellikle çok büyük ve eski sistemlerin modernizasyonu sırasında karşıma çıkıyor. Ortak kütüphaneleri ayrı bir depoda tutarken, uygulamaların kendi depoları olabiliyor. Bu durumda, sıkı bir versiyonlama ve sürüm yönetimi stratejisi uygulamak gerekiyor. Yazılımda Sürüm Yönetimi ve Otomasyon

Sonuç olarak, benim tercihim çoğunlukla monorepo yönünde. Ancak bu tercih, her zaman projenin ve ekibin özel ihtiyaçları, ölçeği ve beklentileri doğrultusunda şekilleniyor. Önemli olan, her iki yaklaşımın da güçlü ve zayıf yönlerini bilmek ve doğru zamanda doğru aracı seçebilmek.

Sonuç

Yazılım projelerinde dependency yönetimi için monorepo ve polyrepo yaklaşımları, her birinin kendine özgü avantaj ve dezavantajlarıyla birlikte karşımıza çıkan temel bir mimari karardır. Bu iki yaklaşımın da farklı senaryolarda nasıl çalıştığını, hangi zorlukları beraberinde getirdiğini ve hangi faydaları sağladığını bizzat deneyimledim.

Polyrepo, küçük, bağımsız ve farklı teknoloji stack’lerine sahip projeler için başlangıçta daha basit görünebilirken, özellikle ortak kütüphanelerin sık güncellendiği ve projeler arası koordinasyonun kritik olduğu durumlarda “dependency hell” ve operasyonel karmaşıklık gibi sorunlara yol açabiliyor. Monorepo ise, doğru araçlar (Nx, Turborepo) ve stratejilerle yönetildiğinde, atomik commit’ler, basitleştirilmiş bağımlılık yönetimi ve kolay refactoring gibi önemli avantajlar sunarak geliştirme hızını ve güvenilirliğini artırabiliyor. CI/CD Pipeline Güvenilirliği

Unutmamak gerekir ki, tek bir “en iyi” çözüm yoktur. Karar verirken ekip büyüklüğü, projenin karmaşıklığı, CI/CD beklentileri, güvenlik endişeleri ve hatta organizasyonel kültür gibi faktörleri dikkate almak gerekiyor. Benim kişisel tercihim genellikle monorepo’dan yana olsa da, her projenin kendi bağlamında değerlendirilmesi gerektiğine inanıyorum. Önemli olan, bu yaklaşımların tüm nüanslarını anlamak ve projenizin ihtiyaçlarına en uygun olanı bilinçli bir şekilde seçmektir.

Sonraki adım, kendi projenizin ihtiyaçlarını detaylı bir şekilde listelemek, yukarıda bahsettiğim faktörleri değerlendirmek ve bu yaklaşımlardan hangisinin size daha uygun olacağını belirlemektir. Seçtiğiniz yaklaşımın araçlarını (örneğin pnpm workspaces, Nx) küçük bir deneme projesinde test etmek, kararınızı pekiştirmenize yardımcı olacaktır. PostgreSQL Performans Optimizasyonu İpuçları

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

Monorepo'yu yeni bir projeye taşımak istiyorum, hangi adımları izlemeli ve hangi araçları tercih etmeliyim?
Ben ilk adım olarak mevcut polyrepo'yu tek bir GitHub organizasyonunda birleştiriyorum, ardından `nx` ya da `turborepo` gibi monorepo yöneticilerini kuruyorum. Depo kökünde bir `pnpm-workspace.yaml` dosyası oluşturarak paketler arası bağımlılıkları tanımlıyorum. CI/CD için GitHub Actions’da birden çok job tanımlayıp sadece değişen paketleri build edip test ediyorum. Ayrıca `eslint` ve `prettier` konfigürasyonlarını kök seviyesinde paylaşarak tutarlılığı sağlıyorum. Bu adımları izlerken, her paket için ayrı `package.json` tutmak ve versiyonlama stratejisini (ör. independent vs fixed) önceden belirlemek kritik.
Polyrepo kullanırken ortak bir kütüphane güncellenince tüm servislerde oluşan çakışmaların önüne nasıl geçebilirim?
Ben, ortak kütüphaneler için semver kurallarına sıkı sıkıya uyarak `^` yerine `~` kullanıyorum, böylece sadece patch seviyesindeki güncellemeler otomatik çekilir. Ayrıca her servisin `dependabot` ya da `renovate` gibi otomatik güncelleme botlarıyla PR açmasını sağlıyorum; PR'lar CI'da bütün entegrasyon testlerinden geçince merge ediyorum. Çakışma yaşandığında ise, ilgili servisin `yarn.lock` ya da `pnpm-lock.yaml` dosyasını güncelleyip testleri tekrar çalıştırıyorum. Bu süreçte, ortak kütüphaneyi ayrı bir repo olarak tutup sürüm notlarını detaylı belgelemek de çakışma riskini büyük ölçüde azaltıyor.
Monorepo'nun karmaşıklığı nedeniyle build süresi çok uzuyor, bu sorunu nasıl hafifletebilirim?
Ben, `nx`'in etkili cache ve incremental build özelliklerini kullanıyorum; değişmeyen paketler için önceki build çıktıları yeniden kullanılıyor. Ayrıca `turbo run` komutuyla paralel çalıştırma sayısını artırıyorum ve CI ortamında `--filter` flag'iyle sadece değişen paketleri derliyorum. Docker katmanlarını da optimize ederek her paket için ayrı image yerine ortak bir base image kullanıyorum. Son olarak, CI pipeline'ında `caching` adımları ekleyip `node_modules` ve `dist` klasörlerini saklayarak sonraki çalıştırmalarda tekrar indirme ve derleme süresini büyük ölçüde kısaltıyorum.
Polyrepo'nun her ekip için bağımsız repo yönetimi daha iyi olduğu yaygın bir kanı, gerçekten doğru mu?
Benim deneyimim, bu kanının sadece belirli durumlarda geçerli olduğunu gösteriyor. Küçük, birbirinden bağımsız servisler ve farklı teknoloji stack'leri kullanan ekipler için polyrepo gerçekten esneklik sağlar. Ancak, ortak kod, ortak CI kuralları ve tutarlı versiyonlama gerektiğinde monorepo daha az yönetim yükü ve daha hızlı entegrasyon sunar. Birkaç yıl polyrepo'da çalıştıktan sonra, kod tekrarı ve bağımlılık uyumsuzlukları yüzünden bakım maliyetinin yükseldiğini gördüm. Dolayısıyla, ekip büyüklüğü, servis bağımlılıkları ve organizasyon kültürü göz önüne alındığında karar vermek en sağlıklı yaklaşımdır.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar