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 →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 make your database query disappear. Learn how RequestAborted flows through ASP.NET Core, EF Core, and HttpClient, where cancellation should stop, and how to test it.
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 so crashes do not lose business events.
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, inspect the generated UPDATE, handle DbUpdateConcurrencyException, and test the race with two DbContexts.
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 can't tell your arguments apart. Value objects in modern C#, the bugs they delete, and when they're overkill.
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. The SQL, the EF Core, the trade-offs.
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, nearly a minute of CPU. Why nested quantifiers explode and how .NET lets you defuse them.
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. The case for practicing what predicts a good engineer: 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. Fitness functions with ArchUnitNET assert layering and dependency rules in CI like any test.
Read article →Hexagonal architecture in .NET without the dogma
Ports and adapters explained in plain C#: domain in the center, ports as interfaces, adapters as infrastructure. Why it beats layered spaghetti, and when it is overkill for a CRUD app.
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 depending on abstractions you own and pointing dependencies inward, not registering services.
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 (extract method, introduce a seam, replace conditional with polymorphism).
Read article →Green checkmarks are incomplete: grading C# beyond correctness
A green check can prove output without proving the solution scales. Learn how performance, allocation, query, and structure gates turn a result into actionable feedback.
Read article →Clean code is a vibe until you measure it
Four code quality metrics you can actually automate in C#: cyclomatic complexity, nesting depth, method length, and duplication, with reasonable thresholds and a refactor that passes them.
Read article →EF Core habits that quietly wreck performance
Nine EF Core habits that pass every test and quietly tank performance in production: missing AsNoTracking, fetching whole entities, N+1, cartesian explosion, counting in memory, and more.
Read article →Your LINQ is fast; your query plan isn't
A perfectly 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 C# memory. How 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, and 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, and most coding practice fails that test badly.
Read article →How senior .NET engineers actually keep their edge
It isn't grinding more LeetCode for correctness. Seniors practice the things production grades: performance, data-access shape, design, and security. Here's the difference.
Read article →The premature optimization myth: what Knuth actually said
The most-abused quote in software lets developers dodge thinking about performance entirely. Here's Knuth's real argument, and why finding the 3% requires an instinct you have to train.
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. Here's 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
In .NET, correct and algorithmically optimal code can still drag, because every needless allocation feeds the GC. Here's 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. Here's where it bites and how to make complexity an instinct.
Read article →