Katabench blog
Engineering field notes
Practical, production-minded guides for .NET engineers. Learn the mechanism, see the failure mode, and leave with an instinct you can use in real code.
Latest articles
RSS feed →Back-of-the-envelope capacity estimation for system design
Back of the envelope estimation for system design: turn daily active users into QPS, bandwidth, storage, and pool sizes, then find what saturates first.
Read article →BenchmarkDotNet: measuring C# performance without fooling yourself
BenchmarkDotNet measures C# performance without the lies a Stopwatch tells: tiered JIT, dead-code elimination, and GC noise, plus how to read the results table.
Read article →CDN and edge caching: what belongs at the edge
CDN edge caching is HTTP caching at scale. Which Cache-Control directives matter, what never belongs at the edge, and how a 95% hit rate changes the origin.
Read article →Change data capture: keep a search index in sync without dual writes
Updating the database and the search index in one request is a dual write that drifts. Use change data capture or an outbox to derive the index instead.
Read article →Database replication and failover: what a standby promises
A replica is not a backup and a failover is not free. Database replication and failover explained: sync vs async commit, replication lag, safe promotion.
Read article →Database sharding vs partitioning: when one database is not enough
Database sharding vs partitioning: one splits a table inside a database, the other splits data across them. What each gives up and how to pick a shard key.
Read article →Transaction isolation levels: the anomalies each level allows
Database transaction isolation levels are a menu of anomalies you tolerate, not a safety dial. Watch write skew commit, then add the retry Serializable needs.
Read article →Design a notification system: fan-out, retries, and consent
Design a notification system that fans one event out to email, push, and SMS without double-sending, blocking on a dead provider, or messaging opted-out users.
Read article →Design a URL shortener: from one API to a billion redirects
Design a URL shortener with real numbers: 40 writes and 4,000 redirects a second, base62 keys, a cache-aside redirect path, and the 301 vs 302 analytics trade.
Read article →EF Core bulk updates with ExecuteUpdate and ExecuteDelete
An EF Core bulk update that loads 50,000 entities to change one column pays per byte and per statement. ExecuteUpdate sends one statement and skips the tracker.
Read article →Fan-out on write vs fan-out on read: designing a social feed
Fan-out on write vs fan-out on read, with the arithmetic: why push wins for ordinary accounts, where a 10M-follower post explodes, and how a hybrid merges both.
Read article →How to practice system design without waiting for an interview
How to practice system design deliberately: a weekly protocol that picks one pressure, draws the smallest design, breaks it, and compares against a reference.
Read article →Insecure deserialization in .NET: why BinaryFormatter died
Insecure deserialization in .NET turns untrusted input into code execution. Why BinaryFormatter was removed, and the Newtonsoft.Json and XmlSerializer traps.
Read article →Integration testing in .NET with Testcontainers and WebApplicationFactory
Integration testing in .NET with Testcontainers and WebApplicationFactory runs your API against real PostgreSQL, so constraints and query counts get tested.
Read article →Load balancing algorithms: round robin to consistent hashing
Load balancing algorithms are bets about your fleet. Round robin assumes equal servers, least connections dodges slow ones, consistent hashing keeps keys put.
Read article →Queue-based load leveling: absorb write spikes without losing work
Queue-based load leveling puts a durable queue between a write spike and the workers that drain it. Do the backlog arithmetic and watch the metric that matters.
Read article →SSRF in ASP.NET Core: your server as the attacker's browser
Server-side request forgery (SSRF) in ASP.NET Core makes your server the attacker's browser. Why naive URL checks fail and how to pin the connected address.
Read article →System design fundamentals: nine building blocks
System design fundamentals as nine building blocks: name the pressure, apply the pattern, count what it costs, and learn the failure mode each block introduces.
Read article →Aspire Service Discovery: Names, Ports, Replicas
Learn how Aspire service discovery turns logical names into endpoints with WithReference, HttpClient resolution, health checks, and replica load balancing.
Read article →Circuit Breaker Pattern in .NET with Polly
Build a circuit breaker in modern .NET with Polly and HttpClient resilience. Tune the failure window, compose retries safely, and prove recovery.
Read article →Message Queue vs Event Bus: Choose the Topology
Compare message queues, event buses, and pub/sub by delivery topology. Choose competing workers or fan-out, then design retries and ordering honestly.
Read article →Mutation Testing in C# with Stryker.NET
Mutation testing in C# plants controlled bugs and asks whether your tests notice. Learn Stryker.NET, triage survivors, and set useful CI gates.
Read article →Path Traversal Vulnerability in ASP.NET Core
A path traversal vulnerability lets user input escape a trusted directory. See the unsafe C# patterns, the containment check, and tests that prove it.
Read article →C# String Concatenation Performance, Measured
Choose between +, interpolation, StringBuilder, and direct writes in modern C#. Fix repeated copying, then verify the result with allocation benchmarks.
Read article →Structured Logging in .NET: Write Logs You Can Query
Learn structured logging in .NET with ILogger message templates, named fields, scopes, correlation IDs, safe data handling, and query-first design.
Read article →CancellationToken in ASP.NET Core: stop doing work nobody wants
A disconnected client does not cancel your database query by itself. How RequestAborted flows through ASP.NET Core, EF Core, and HttpClient, and where it stops.
Read article →Transactional outbox pattern in .NET: publish without losing messages
Saving data and publishing an event are two writes that can disagree. Build a transactional outbox with EF Core, a retrying publisher, and idempotent consumers.
Read article →Optimistic concurrency in EF Core: stop silent overwrites
Two users edit the same row and the last save silently wins. Use EF Core concurrency tokens, handle DbUpdateConcurrencyException, and test the race properly.
Read article →AddSingleton vs AddScoped vs AddTransient: what each promises
AddSingleton vs AddScoped vs AddTransient in plain terms: what each lifetime promises, sensible defaults, and the captive dependency bug that ships.
Read article →C# async await mistakes that pass code review
The C# async await mistakes that pass code review: async void, fire and forget tasks, missing CancellationToken plumbing, and blocking on .Result.
Read article →Caching strategies: cache-aside, invalidation, and stampedes
Caching strategies from the cache-aside flow to the hard parts: invalidation owned by a human and stampedes tamed by single-flight and jitter.
Read article →Code smells: a working catalog of six, with C# examples
Code smells are symptoms, not verdicts. Six worth knowing, with C# examples: the tell for each, why it hurts, and the refactoring move that clears it.
Read article →Connection pooling: why your app ran out of connections
Connection pooling explained from the failure end: what a connection costs, the three ways pools drain, and why the timeout blames the wrong request.
Read article →Cyclomatic complexity: what the number actually tells you
Cyclomatic complexity counts the independent paths through a method. How to compute it by eye, what a score of 10 predicts, and how to reduce it for real.
Read article →.NET garbage collection explained: what gen 0, 1, and 2 cost
.NET garbage collection explained: why most objects die young, what gen 0, 1, and 2 collections cost, and why the large object heap hurts latency.
Read article →How do database indexes work? B-trees, page by page
How do database indexes work? A B-tree is a small sorted structure with huge fanout, so a lookup reads a handful of pages instead of the whole table.
Read article →Idempotency in API design: surviving the retry you didn't send
Every serious client retries. How the idempotency key pattern and a database unique constraint keep a retried payment from becoming two charges.
Read article →Rate limiting algorithms: how token buckets forgive bursts
Rate limiting algorithms compared: why fixed windows leak double the limit, how token buckets forgive bursts, and what a good 429 response returns.
Read article →Unit testing best practices: test behavior, not implementation
Unit testing best practices that survive refactoring: AAA structure, behavior-first assertions, a healthy test pyramid, and determinism over flakiness.
Read article →Primitive obsession: the bug the compiler was begging to catch
When everything is a string, a decimal, or a Guid, the type system cannot tell arguments apart. Value objects in modern C#, the bugs they delete, their limits.
Read article →Sync over Async: Diagnose .NET Thread Pool Starvation
Learn how sync over async causes .NET Thread Pool starvation, spot it with dotnet-counters, capture blocking stacks, and fix ASP.NET Core latency.
Read article →Skip and Take will betray you: offset vs keyset pagination
OFFSET pagination reads every row it skips, so page 2,000 costs 2,000 pages of work. Keyset pagination seeks straight to the next page. SQL and EF Core shown.
Read article →The LINQ query that ran twice: multiple enumeration and other deferred-execution traps
An IEnumerable isn't a list; it's a promise to do work later. Enumerate it twice and the work happens twice, including, with EF Core, the database query.
Read article →ReDoS: the innocent regex that can take down your API
Catastrophic backtracking turns an email regex into a denial of service: 30 characters, almost a minute of CPU. Why nested quantifiers explode and the .NET fix.
Read article →Tutorial hell: why you can finish every course and still freeze at a blank file
Watching code get written trains recognition, not recall. Why tutorials feel like progress, why the skill doesn't transfer, and how to build a loop that does.
Read article →Coding interviews are broken, but the skills they fumble are real
Whiteboard interviews measure the wrong slice of engineering. Practice what predicts a good engineer instead: correct, fast, well-structured, secure code.
Read article →The OWASP Top 10 through a C# lens
A practical tour of the OWASP Top 10 for .NET developers: the ASP.NET Core and EF Core patterns behind broken access control, injection, SSRF, and overposting.
Read article →SQL injection is still alive, and it's hiding in your C#
SQL injection is supposedly solved, yet it's still in the OWASP Top 10. The C# patterns that keep it alive in EF Core and ADO.NET, and how to kill them.
Read article →Architecture fitness functions: tests that fail the build when your layers rot
Architecture decays because nothing fails the build when the domain references the DbContext. ArchUnitNET fitness functions assert layering rules in CI.
Read article →Hexagonal architecture in .NET without the dogma
Ports and adapters in plain C#: domain in the center, ports as interfaces, adapters as infrastructure. Why it beats layered spaghetti, and when it is overkill.
Read article →Dependency inversion in C# is not about your DI container
The D in SOLID is the most quoted and least applied principle. Dependency inversion means owning your abstractions and pointing dependencies inward.
Read article →A test-first playbook for refactoring legacy C# safely
How to refactor legacy C# without breaking it: write characterization tests first, then take small behavior-preserving steps like extract method and seams.
Read article →Green checkmarks are incomplete: grading C# beyond correctness
A green check proves output without proving the solution scales. How performance, allocation, query, and structure gates turn a pass into actionable feedback.
Read article →Clean code is a vibe until you measure it
Four code quality metrics you can automate in C#: cyclomatic complexity, nesting depth, method length, and duplication, with thresholds and a passing refactor.
Read article →EF Core habits that quietly wreck performance
Nine EF Core habits that pass every test and quietly tank production performance: missing AsNoTracking, whole-entity fetches, N+1, cartesian explosion, more.
Read article →Your LINQ is fast; your query plan isn't
A well-written EF Core query can still do a sequential scan: no index, or a function on a column makes it non-sargable. How to read a query plan and fix it.
Read article →IQueryable vs IEnumerable: the one return type that decides where your query runs
Returning IEnumerable, or calling ToList too early, moves filtering and paging into memory. One type change turns an indexed WHERE into loading a million rows.
Read article →The N+1 query problem in EF Core: the most expensive habit in .NET
An EF Core loop that looks innocent fires one query per row. It passes every unit test and dies in production. How N+1 happens, the SQL it generates, the fix.
Read article →Deliberate practice for programmers: why a green check isn't feedback
Repetition doesn't make you better; deliberate practice does. It needs immediate, specific feedback at the edge of your ability, which most practice lacks.
Read article →How senior .NET engineers actually keep their edge
It isn't grinding more LeetCode for correctness. Seniors practice what production grades: performance, data-access shape, design, and security.
Read article →The premature optimization myth: what Knuth actually said
The most-abused quote in software lets developers dodge performance entirely. Knuth's real argument, and why finding the critical 3% takes a trained instinct.
Read article →Span<T>, Memory<T>, and stackalloc: when zero-allocation C# is worth it
Span<T> and stackalloc let you parse and slice without allocating, but they have real limits. When zero-allocation C# earns its keep and when it's overkill.
Read article →The allocation tax: why correct, O(n) C# can still be slow
Correct, algorithmically optimal .NET code can still drag, because every needless allocation feeds the GC. How to spot and cut allocation pressure in C#.
Read article →Big-O isn't interview trivia: where complexity actually bites in production
Big-O gets crammed for interviews and forgotten, but quadratic C# passes your tests and melts in prod. Where complexity bites and how to make it an instinct.
Read article →