# sade.dev > A notebook on backend and systems engineering. Production lessons, architecture decisions and practical developer tools. Notes and developer tools on backend, system design, PostgreSQL, Redis, Laravel and AI-assisted engineering. I write about backend and systems engineering. Writing code since 2008, in production for 15+ years. Muhammet Şafak — https://www.muhammetsafak.com.tr. Content language is English. When quoting, please cite with a link to the page. Citation format: Muhammet Şafak, "
", sade.dev,
. Every article listed below has a plain-markdown twin at its own URL with `.md` appended — e.g. .md. Read that instead of fetching the HTML. ## Notes - [Indexes: Too Few Slow Reads, Too Many Kill Writes](https://sade.dev/en/notes/index-management/): A missing index slows the query; too many indexes make every write expensive. Striking the right balance by measurement, not by guesswork (2026-09-06) - [Changing Schema in Production With Zero Downtime](https://sade.dev/en/notes/zero-downtime-database-migrations/): Running migrations without causing downtime in production: backward-compatible steps, multi-phase column changes, and avoiding table locks (2026-09-06) - [Read/Write Splitting: Separating Read and Write Load](https://sade.dev/en/notes/read-write-splitting/): Before scaling up the database: moving read traffic to replicas, the traps replication lag creates, and when you actually need it (2026-09-06) - [Before You Reach for Redis: The Right Cache Strategy](https://sade.dev/en/notes/the-right-cache-strategy/): The questions to ask before adding a cache, why cache invalidation is hard, and the choice between TTL and consistency (2026-09-06) - [Circuit Breakers for External Service Integrations](https://sade.dev/en/notes/circuit-breaker-external-service-integration/): Connecting a critical system to external APIs: using timeouts, retries, and circuit breakers to stop one service's collapse from taking the whole system down. (2026-09-06) - [The Serverless Decision: Cold-Start and Vendor Lock-in](https://sade.dev/en/notes/serverless-migration-decision/): Before you move to FaaS: the real cost of cold-start latency, vendor lock-in risk, and the workloads where serverless actually pays off (2026-09-06) - [BFF Pattern: A Separate API Layer for Mobile and Web](https://sade.dev/en/notes/bff-backend-for-frontend/): Separate API layers for mobile and web: the hidden cost of bending one API to fit every client, and when a BFF is actually warranted (2026-09-06) - [Synchronous or Asynchronous? HTTP or the Queue](https://sade.dev/en/notes/sync-vs-async/): Should a job run inside the HTTP request or go to a queue? The decision line, drawn through response time and fault tolerance (2026-09-06) - [Event-Driven Architecture: What It Solves and Hides](https://sade.dev/en/notes/when-event-driven-architecture/): The price of loose coupling through events: balancing the flexibility it buys against the risk of making your system's flow impossible to trace (2026-09-06) - [Replaying a Dead-Letter Queue Without Making It Worse](https://sade.dev/en/notes/replaying-a-dead-letter-queue/): Naive replay re-poisons the queue or double-fires side effects. The safe shape: reset, dry-run, sandbox-first, select. (2026-09-06) - [Validating the Schema at the Edge of the Queue](https://sade.dev/en/notes/validate-schema-at-the-edge/): A message's data is an untyped contract the queue won't check. Validate it at the edge: producer before publish, consumer as a safety net. (2026-09-06) - [Idempotency: When the Same Message Arrives Twice](https://sade.dev/en/notes/idempotency-duplicate-delivery/): At-least-once delivery means a handler will see the same message twice. The idempotency key, where it comes from, and the dedupe that survives a crash (2026-09-06) - [Namespace Isolation for a Shared Redis](https://sade.dev/en/notes/shared-redis-namespace-isolation/): The pattern I use to avoid key collisions and accidental deletes when a single Redis instance is shared across several projects (2026-09-06) - [A PostgreSQL Backup Strategy with pgBackRest](https://sade.dev/en/notes/pgbackrest-postgresql-backup-strategy/): pg_dump alone is not a backup. A PITR-capable, compressed, verifiable backup architecture with pgBackRest. (2026-09-06) - [Why Do Laravel Queues Slow Down in Production?](https://sade.dev/en/notes/laravel-queue-production-slowdown/): Laravel queue workers are flawless locally and slow in production. The five most common reasons developers overlook. (2026-09-06) - [Nginx + PHP-FPM Pool Separation](https://sade.dev/en/notes/nginx-php-fpm-pool-separation/): The concrete benefits of defining a separate pool per application instead of a single PHP-FPM pool when running multiple PHP apps on one VPS (2026-09-06) - [Multiple DB Users with pgBouncer auth_query](https://sade.dev/en/notes/pgbouncer-auth-query/): A practical setup for dynamic user authentication via `auth_query` instead of `userlist.txt` when running pgBouncer (2026-09-06) ## Systems - [Transactional Outbox: Dual-Write, Idempotent Consumers](https://sade.dev/en/systems/transactional-outbox-dual-write-and-idempotent-consumption/): Closing dual-write with an outbox, suppressing the at-least-once repeats it creates, and encrypting the personal data inside the event. (2026-09-06) - [GPU FinOps with eBPF](https://sade.dev/en/systems/gpu-finops-with-ebpf/): Why GPU spend is invisible to cgroup and cAdvisor metrics, how eBPF attributes it to teams — and the CUDA boundary only DCGM can cover. (2026-09-06) - [Zero Static Authority in Multi-Cluster GitOps](https://sade.dev/en/systems/zero-static-authority-multi-cluster-gitops/): Multi-cluster GitOps with no long-lived kubeconfig: SPIFFE IDs, SPIRE-issued short-lived SVIDs — the order to build it in, and what each step costs. (2026-09-06) - [Distributed Tracing Across a Polyglot Queue](https://sade.dev/en/systems/polyglot-queue-distributed-tracing/): Turning a message that crosses PHP, Go and Python over a frozen envelope into one OpenTelemetry trace — no new field, no new dependency. (2026-09-06) - [Race Conditions and Gaps in Sequential Numbering](https://sade.dev/en/systems/race-conditions-and-gaps-in-sequential-numbering/): Generating legally sequential, gap-free numbers with parallel workers: the JIT reservation pattern that solves race conditions and gaps together (2026-09-06) - [Scale Breaking Points in Data-Intensive Systems](https://sade.dev/en/systems/data-intensive-systems-breaking-points/): As data load grows, where does boring architecture break? Working set, read replicas, partitioning, write load, a separate data layer — in order. (2026-09-06) - [Multiple Projects on a Single VPS](https://sade.dev/en/systems/single-vps-multi-project-architecture/): One VPS, several independent apps. Deliberate minimalism over Kubernetes: user isolation, separate PHP-FPM pools, shared PostgreSQL/Redis, a plain deploy. (2026-09-06) - [Laravel Production Stack: Why Each Piece Is There](https://sade.dev/en/systems/laravel-production-stack/): A production-grade Laravel setup with Nginx + PHP-FPM + Redis + PostgreSQL + Supervisor + Horizon. Each piece's job and what you lose if you drop it (2026-09-06) ## Journal - [The NoSQL Trap: Starting Because "It Has No Schema"](https://sade.dev/en/journal/the-nosql-trap/): The consistency and data-integrity crises that projects which moved to NoSQL for the comfort of going schemaless eventually hit (2026-09-06) - [Is PostgreSQL Enough for Everything?](https://sade.dev/en/journal/is-postgresql-enough-for-everything/): Before adding a separate search engine, queue, or document DB: how far PostgreSQL gets you on its own, and where its limit begins. (2026-09-06) - [Signals That a System Has Grown Too Complex](https://sade.dev/en/journal/signals-of-an-over-complex-system/): The concrete red flags that reveal code or architecture has turned needlessly complex — and where to start simplifying (2026-09-06) - [Architecture Decisions Rot Because Nothing Runs Them](https://sade.dev/en/journal/architecture-decisions-rot/): An architecture decision written in a wiki is a wish. The boundaries you can't enforce quietly erode until the diagram stops matching the code. (2026-09-06) - [AI Didn't Remove the Bottleneck — It Moved It](https://sade.dev/en/journal/ai-didnt-remove-the-bottleneck/): Cheap code production doesn't raise throughput if review and integration can't keep up. The bottleneck moves downstream — that's where the work is (2026-09-06) - [Flaky Tests: Retrying Isn't a Fix](https://sade.dev/en/journal/flaky-tests-and-determinism/): AI made writing tests nearly free, and flaky suites exploded. Why retry and quarantine are a treadmill, and what catches flakiness deterministically (2026-09-06) - [Schema Evolution Without Breaking the Contract](https://sade.dev/en/journal/schema-evolution-without-breaking/): You can't change a cross-service contract atomically. The rule: additive is safe, a breaking change needs a new identity — consumers upgrade first (2026-09-06) - [Can You Ship Vibe-Coded Software to Production?](https://sade.dev/en/journal/can-you-ship-vibe-coded-to-production/): Vibe coding lowers the cost of a first version, not the cost of being wrong. That bill arrives in production, not at build time. (2026-09-06) - [Layered Architecture or Clean Architecture?](https://sade.dev/en/journal/layered-vs-clean-architecture/): Why the classic layered structure is enough for most projects while we drown in abstraction layers, and when Clean Architecture actually pays off. (2026-09-06) - [The Production Cost of "Just-in-Case" Code](https://sade.dev/en/journal/the-cost-of-just-in-case-code/): Applying YAGNI in practice: the price of adding flexibility you don't need today, repaid daily in maintenance, readability, and bug surface (2026-09-06) - [When I Move to Microservices](https://sade.dev/en/journal/when-i-move-to-microservices/): The measured signals that justify moving from a monolith to a distributed architecture — and the common false reasons that say it's still too early (2026-09-06) - [Gitflow or GitHub Flow? How I Choose](https://sade.dev/en/journal/gitflow-vs-github-flow/): Practical criteria for picking a branching strategy by product model, version count, and deploy rhythm — and how each model breaks in the wrong place. (2026-09-06) - [When I Switch to Trunk-Based Development](https://sade.dev/en/journal/when-i-switch-to-trunk-based/): Why release branches break for high-frequency deploy teams and how much of trunk-based is really discipline — the thresholds I cross and the ones I don't (2026-09-06) - [Why I Start Projects With a Modular Monolith](https://sade.dev/en/journal/why-i-start-with-a-modular-monolith/): The case for drawing clean module boundaries inside a single deployable app before jumping to microservices — and when I stop doing it. (2026-09-06) - [Why I Prefer Boring Architecture](https://sade.dev/en/journal/why-boring-architecture/): The pragmatic case for choosing proven tools over chasing new technology. This is not cowardice, it is a budget decision. (2026-09-06) - [My AI-Assisted Engineering Workflow](https://sade.dev/en/journal/ai-assisted-engineering-workflow/): The seven rules I follow to use Claude Code and similar agents on production code with confidence — not a skeptical take, a discipline manifesto (2026-09-06) ## Optional - [.env Parser](https://sade.dev/en/tools/env-parser/): Splits dotenv-format content into a key/value table. Masks sensitive fields. - [Backoff & Jitter Calculator](https://sade.dev/en/tools/backoff-calculator/): Compute an exponential backoff schedule and see how full, equal, and decorrelated jitter spread the retries. Client-side, nothing is retried. - [Base64 Encoder / Decoder](https://sade.dev/en/tools/base64/): Encode and decode text with Base64. Supports the URL-safe variant. Unicode-aware. - [HTTP Status Codes Reference](https://sade.dev/en/tools/http-status/): The most commonly used HTTP status codes and what they mean. Searchable. - [JSON Formatter](https://sade.dev/en/tools/json-formatter/): Format and minify JSON. Runs in your browser; your data never leaves it. - [JSON Schema Backward-Compatibility Diff](https://sade.dev/en/tools/schema-compat/): Diff two JSON Schemas: is the change backward-compatible, or a breaking one that needs a new message version? Runs in your browser. - [JWT Decoder](https://sade.dev/en/tools/jwt-decoder/): Decodes the header and payload of a JSON Web Token. Does not verify the signature. - [Password Generator](https://sade.dev/en/tools/password-generator/): Cryptographically secure password generator with adjustable length and character classes. Runs in your browser. - [Unix Timestamp Converter](https://sade.dev/en/tools/timestamp/): Convert between Unix timestamp and ISO 8601. Seconds and milliseconds are detected automatically. - [URL Parser](https://sade.dev/en/tools/url-parser/): Breaks a URL into its components and query parameters - [UUID Generator (v4 & v7)](https://sade.dev/en/tools/uuid/): UUID v4 (random) and v7 (time-ordered) generator. One at a time or in bulk. - [About](https://sade.dev/en/about/): About Muhammet Şafak and sade.dev — why it exists, where it is going, what gets written here and what does not. - [Cookie Policy](https://sade.dev/en/cookies/): sade.dev sets no cookies of its own. The theme preference lives in localStorage; cookies can only originate from Cloudflare or the comment embed. - [Privacy Policy](https://sade.dev/en/privacy/): What sade.dev processes and what it does not: no analytics, no accounts, no server-side application. The exceptions that apply to hosting and comments. - [Tools I use](https://sade.dev/en/stack/): The editors, languages, libraries, server software, and VPS setup I use in daily production. - [Terms of Use](https://sade.dev/en/terms/): Use of sade.dev content, the license covering code samples, the warranty scope of the tools and the production advice, and the comment rules. - [Glossary](https://sade.dev/en/glossary/): Definitions of the backend and systems engineering terms used across the site; each term has its own anchor (#term). - [llms-full.txt](https://sade.dev/en/llms-full.txt): Full text of every English article, in one file. - [RSS](https://sade.dev/en/rss.xml): Machine-readable feed of all posts.