Skip to content
Katabench
Try free

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 →
System Design 9 min read

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 →
.NET Performance 8 min read

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 →
System Design 9 min read

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 →
System Design 9 min read

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 →
System Design 9 min read

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 →
System Design 9 min read

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 →
Databases & EF Core 9 min read

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 →
System Design 8 min read

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 →
System Design 8 min read

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 →
Databases & EF Core 8 min read

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 →
System Design 8 min read

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 →
Engineering Practice 8 min read

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 →
Security & Reliability 8 min read

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 →
Testing & Refactoring 8 min read

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 →
System Design 9 min read

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 →
System Design 9 min read

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 →
Security & Reliability 8 min read

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 9 min read

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 →
Architecture & Design 7 min read

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 →
Architecture & Design 9 min read

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 →
Architecture & Design 8 min read

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 →
Testing & Refactoring 8 min read

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 →
Security & Reliability 8 min read

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 →
.NET Performance 8 min read

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 →
Architecture & Design 7 min read

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 →
.NET Performance 7 min read

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 →
Architecture & Design 7 min read

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 →
Databases & EF Core 7 min read

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 →
Architecture & Design 8 min read

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 →
.NET Performance 8 min read

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 →
Architecture & Design 8 min read

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 →
Testing & Refactoring 8 min read

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 →
Databases & EF Core 7 min read

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 →
Testing & Refactoring 9 min read

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 Performance 8 min read

.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 →
Databases & EF Core 8 min read

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 →
Security & Reliability 8 min read

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 →
Security & Reliability 8 min read

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 →
Testing & Refactoring 8 min read

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 →
Testing & Refactoring 7 min read

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 →
.NET Performance 9 min read

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 →
Databases & EF Core 7 min read

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 →
.NET Performance 7 min read

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 →
Security & Reliability 7 min read

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 →
Engineering Practice 7 min read

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 →
Engineering Practice 6 min read

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 →
Security & Reliability 8 min read

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 →
Security & Reliability 6 min read

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 & Design 6 min read

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 →
Architecture & Design 6 min read

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 →
Architecture & Design 6 min read

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 →
Testing & Refactoring 5 min read

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 →
Engineering Practice 7 min read

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 →
Testing & Refactoring 6 min read

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 →
Databases & EF Core 7 min read

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 →
Databases & EF Core 6 min read

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 →
Databases & EF Core 6 min read

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 →
Databases & EF Core 6 min read

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 →
Engineering Practice 5 min read

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 →
Engineering Practice 6 min read

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 →
.NET Performance 6 min read

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 →
.NET Performance 5 min read

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 →
.NET Performance 6 min read

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 →
.NET Performance 6 min read

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 →

Get new puzzles and .NET tips in your inbox

A short note when fresh kata land, plus the C# and performance tricks behind the grading. No spam, unsubscribe anytime.