<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[PrepStack]]></title><description><![CDATA[PrepStack]]></description><link>https://prepstack.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>PrepStack</title><link>https://prepstack.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 19 Jun 2026 04:58:43 GMT</lastBuildDate><atom:link href="https://prepstack.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[We Replaced REST with Kafka and Cut API Failures by 90% — A Production Cutover (.NET 9)]]></title><description><![CDATA[TL;DR — We swapped Mattrx's inter-service REST calls for Kafka topics (multi-tenant marketing analytics SaaS, .NET 9 / ASP.NET Core, Azure SQL, ~3,200 req/sec peak). Over 8 weeks: end-to-end ingestion]]></description><link>https://prepstack.hashnode.dev/we-replaced-rest-with-kafka-and-cut-api-failures-by-90-a-production-cutover-net-9</link><guid isPermaLink="true">https://prepstack.hashnode.dev/we-replaced-rest-with-kafka-and-cut-api-failures-by-90-a-production-cutover-net-9</guid><category><![CDATA[kafka]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Randhir Jassal]]></dc:creator><pubDate>Sun, 14 Jun 2026 14:06:56 GMT</pubDate><content:encoded><![CDATA[<p>TL;DR — We swapped Mattrx's inter-service REST calls for Kafka topics (multi-tenant marketing analytics SaaS, .NET 9 / ASP.NET Core, Azure SQL, ~3,200 req/sec peak). Over 8 weeks: end-to-end ingestion failures 1.9% → 0.18% (−90%), ingestion p95 180 ms → 8 ms (−96%), events lost on downstream outage tens of thousands → 0, cascading-failure incidents ~3/month → 0, time to add a new consumer ~1 sprint → ~1 day.</p>
<p>3-minute video walkthrough on YouTube: <a href="https://youtu.be/O21rbuQdM1Y">https://youtu.be/O21rbuQdM1Y</a></p>
<p>Full deep-dive (architecture, code, pre-adoption checklist, when NOT to reach for Kafka) on PrepStack: <a href="https://prepstack.co.in/blog/replaced-rest-with-kafka-cut-failures-90-percent">https://prepstack.co.in/blog/replaced-rest-with-kafka-cut-failures-90-percent</a></p>
<p>The one mental shift</p>
<p>Distinguish commands and queries (need an answer → REST) from events (fire-and-forget → log). A synchronous call makes the caller's uptime a function of the callee's uptime. An event published to a durable log decouples them in time. Most "we need a message bus" problems are really "we mislabeled an event as a function call."</p>
<p>The old pipeline — synchronous REST chain</p>
<p>Customer site → Collector → Enrichment → Analytics → Persister. Each service waits on the next. The Collector can't return 200 until all three downstream services succeed. Availability multiplies. Analytics slow at month-end → Collector times out → customer's event is gone → customer retries → MORE load on the struggling service → retry storm.</p>
<p>The new pipeline — Kafka log in the middle</p>
<p>The collector produces a single event to events.raw and returns 202 Accepted in ~8 ms. Three independent consumer groups read the same topic in parallel. The collector's success no longer depends on any consumer being up. Analytics down? Its consumer group lags and catches up. Zero customer impact. Zero data loss.</p>
<p>The consumer pattern</p>
<p>Three things matter. One: a consumer group, so partitions parallelize across instances. Two: manual offset commit — commit only after successful processing (at-least-once delivery). Three: a retry topic and a dead-letter topic. Consumer lag is the one health metric.</p>
<p>The outbox pattern — atomic produce with a DB write</p>
<p>If the same handler writes to Azure SQL and then produces to Kafka, those are two systems — a crash between them loses or duplicates the event. For events tied to a committed DB change, use the outbox: write the event to an outbox table in the same SQL transaction as the business data; a relay reads unsent rows and publishes them.</p>
<p>Idempotent consumers — at-least-once + dedup = effectively-once</p>
<p>Kafka gives at-least-once delivery by default. Consumers must be idempotent. A dedup key with a unique constraint is the simplest tool. At-least-once + idempotent = effectively-once, and it's far simpler than chasing true exactly-once.</p>
<p>The two superpowers REST never gave us</p>
<p>Replay. A bug corrupted yesterday's rollup? Reset that one consumer group's offset and reprocess. The log is the source of truth.</p>
<p>Adding a consumer for free. New consumer group on the same topic. The producer doesn't change. ~1 day instead of ~1 sprint.</p>
<p>When NOT to reach for Kafka</p>
<p>Don't replace request/response with Kafka — queries stay synchronous. Kafka is operational weight; use managed Kafka or weigh Service Bus / Event Hubs on Azure first. Exactly-once is a trap. Ordering is per-partition, not global. You traded synchronous errors for asynchronous lag — watch lag or you've hidden the problem.</p>
<p>The closing mental model</p>
<p>A synchronous call couples you to the callee being up. An event in a log couples you to the log being up. For anything that's "this happened" rather than "tell me this," publishing to a durable log decouples producers from consumers in time.</p>
<p>Three-minute video walkthrough on YouTube: <a href="https://youtu.be/O21rbuQdM1Y">https://youtu.be/O21rbuQdM1Y</a></p>
<p>Full deep-dive on PrepStack: <a href="https://prepstack.co.in/blog/replaced-rest-with-kafka-cut-failures-90-percent">https://prepstack.co.in/blog/replaced-rest-with-kafka-cut-failures-90-percent</a></p>
<p>If this saved you a 3 AM page, a 💖 or bookmark helps it reach more backend engineers.</p>
]]></content:encoded></item><item><title><![CDATA[Our Azure Bill Spiked Overnight — Here's Exactly How We Cut It 60% (7 Real Fixes)]]></title><description><![CDATA[We run a multi-tenant analytics SaaS on Azure (.NET 9 / ASP.NET Core, Angular 19, ~110k MAU, ~3,200 req/sec). The bill was a boring ~\(4,800/mo for a year, then it climbed to ~\)12,100/mo in three wee]]></description><link>https://prepstack.hashnode.dev/our-azure-bill-spiked-overnight-here-s-exactly-how-we-cut-it-60-7-real-fixes</link><guid isPermaLink="true">https://prepstack.hashnode.dev/our-azure-bill-spiked-overnight-here-s-exactly-how-we-cut-it-60-7-real-fixes</guid><category><![CDATA[Azure]]></category><category><![CDATA[finops]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Randhir Jassal]]></dc:creator><pubDate>Sat, 13 Jun 2026 13:08:27 GMT</pubDate><content:encoded><![CDATA[<p>We run a multi-tenant analytics SaaS on Azure (.NET 9 / <a href="http://ASP.NET">ASP.NET</a> Core, Angular 19, ~110k MAU, ~3,200 req/sec). The bill was a boring ~\(4,800/mo for a year, then it climbed to ~\)12,100/mo in three weeks with no big feature shipped. Here are the seven causes and the fixes that cut it 60%.</p>
<h2>Where the money went (before to after)</h2>
<ul>
<li><p>Log / telemetry ingestion: \(3,100 to \)340/mo (a Debug level left on; 95 GB/day to 9 GB/day)</p>
</li>
<li><p>Compute / autoscale: \(3,400 to \)1,500/mo (scaled out but never in; 8 instances 24/7)</p>
</li>
<li><p>Orphaned resources: \(1,900 to \)0 (a forgotten load-test env + unattached disks/IPs)</p>
</li>
<li><p>Egress / bandwidth: \(1,200 to \)280/mo (large files, no CDN)</p>
</li>
<li><p>SQL + Redis: \(1,600 to \)900/mo (premium tiers + no reservations)</p>
</li>
<li><p>Storage transactions: \(500 to \)190/mo (millions of tiny blob ops, hot tier)</p>
</li>
<li><p>AI / LLM tokens: \(400 to \)190/mo (uncached RAG, oversized model)</p>
</li>
<li><p>Total: ~\(12,100 to ~\)4,700/mo (-60%, below the original baseline)</p>
</li>
</ul>
<h2>What it covers</h2>
<ul>
<li><p>Turning the lights on first: tagging, a cost dashboard, anomaly alerts</p>
</li>
<li><p>The diagnostic queries (az consumption + KQL log-ingestion-by-source)</p>
</li>
<li><p>Before/after config for each fix (sampling, autoscale Bicep, blob lifecycle, CDN, LLM caching)</p>
</li>
<li><p>A FinOps checklist so it doesn't recur</p>
</li>
<li><p>The honest limits of cost optimization</p>
</li>
</ul>
<h2>The lesson</h2>
<p>Most runaway bills are a visibility problem, not an architecture one. Two config mistakes and some deleted waste did most of the work. Cost is a production signal: tag everything, dashboard it, and alert on spend-per-day the way you alert on p95. ~3 days to fix, paid back in ~2.</p>
<p>Full post with real config, queries, and dollar figures: <a href="https://prepstack.co.in/blog/azure-bill-spiked-how-we-cut-it-60-percent">https://prepstack.co.in/blog/azure-bill-spiked-how-we-cut-it-60-percent</a></p>
]]></content:encoded></item><item><title><![CDATA[.NET 11 vs .NET 10: We Benchmarked Both on a Real Production App (Should You Upgrade?)]]></title><description><![CDATA[We run a multi-tenant analytics SaaS on ASP.NET Core (~110k MAU, ~3,200 req/sec peak, ~95k LOC) and benchmarked .NET 9, .NET 10 (current LTS), and .NET 11 previews on the same harness and the same pro]]></description><link>https://prepstack.hashnode.dev/net-11-vs-net-10-we-benchmarked-both-on-a-real-production-app-should-you-upgrade</link><guid isPermaLink="true">https://prepstack.hashnode.dev/net-11-vs-net-10-we-benchmarked-both-on-a-real-production-app-should-you-upgrade</guid><category><![CDATA[dotnet]]></category><category><![CDATA[C#]]></category><category><![CDATA[performance]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Randhir Jassal]]></dc:creator><pubDate>Fri, 12 Jun 2026 19:46:30 GMT</pubDate><content:encoded><![CDATA[<p>We run a multi-tenant analytics SaaS on <a href="http://ASP.NET">ASP.NET</a> Core (~110k MAU, ~3,200 req/sec peak, ~95k LOC) and benchmarked .NET 9, .NET 10 (current LTS), and .NET 11 previews on the same harness and the same production workload, not synthetic microbenchmarks.</p>
<h2>The numbers (9 to 10, GA, near-zero code changes)</h2>
<ul>
<li><p>Throughput / instance: +11%</p>
</li>
<li><p>API p95: 132 ms to 120 ms</p>
</li>
<li><p>Working set / instance: 415 MB to 380 MB</p>
</li>
<li><p>Native AOT cold start: 84 ms to 61 ms</p>
</li>
<li><p>Native AOT image size: 41 MB to 33 MB</p>
</li>
<li><p>Language: C# 13 to C# 14</p>
</li>
</ul>
<h2>What it covers</h2>
<ul>
<li><p>Runtime/JIT free wins (AVX10.2, devirtualization, loop opts)</p>
</li>
<li><p>GC/memory (DATAS right-sizing the heap)</p>
</li>
<li><p><a href="http://ASP.NET">ASP.NET</a> Core built-in OpenAPI + minimal-API validation</p>
</li>
<li><p>C# 14: the field keyword + extension members (~700 LOC deleted)</p>
</li>
<li><p>Native AOT startup and container size</p>
</li>
<li><p><a href="http://Microsoft.Extensions.AI">Microsoft.Extensions.AI</a> becoming first-class</p>
</li>
<li><p>The actual 9 to 10 migration (~1.5 engineer-days for 95k LOC)</p>
</li>
</ul>
<p>.NET 11 previews show another ~6-9% throughput, p95 down 5-7%, and ~10% faster startup, but it is preview (GA Nov 2026).</p>
<h2>Verdict</h2>
<p>Upgrade to the LTS for the free, measured wins; pilot the .NET 11 preview in CI, ship after GA (Nov 2026). And benchmark your own hot paths, generic benchmarks don't reflect your workload.</p>
<p>Full post with before/after code and the decision checklist: <a href="https://prepstack.co.in/blog/dotnet-11-vs-dotnet-10-benchmarked-production">https://prepstack.co.in/blog/dotnet-11-vs-dotnet-10-benchmarked-production</a></p>
]]></content:encoded></item><item><title><![CDATA[Angular State Management in 2026 — NgRx, Signals, NGXS, Akita Compared (with Real Numbers)]]></title><description><![CDATA[TL;DR — We rebuilt Mattrx's Angular state layer over 8 months (Angular 16 → 19, ~22k LOC TS). State LOC 8,400 → 3,100 (-63%), state-related bundle 38 KB → 18 KB, /campaigns re-renders per filter keyst]]></description><link>https://prepstack.hashnode.dev/angular-state-management-in-2026-ngrx-signals-ngxs-akita-compared-with-real-numbers</link><guid isPermaLink="true">https://prepstack.hashnode.dev/angular-state-management-in-2026-ngrx-signals-ngxs-akita-compared-with-real-numbers</guid><category><![CDATA[Angular]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[NgRx]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Randhir Jassal]]></dc:creator><pubDate>Thu, 11 Jun 2026 16:40:16 GMT</pubDate><content:encoded><![CDATA[<p>TL;DR — We rebuilt Mattrx's Angular state layer over 8 months (Angular 16 → 19, ~22k LOC TS). State LOC 8,400 → 3,100 (-63%), state-related bundle 38 KB → 18 KB, /campaigns re-renders per filter keystroke 47 → 3, "where does this state live?" PR comments 14/week → 2.</p>
<p>The win wasn't picking the right library. It was picking the right library for each kind of state.</p>
<p>Full deep-dive (with all 6 code samples, bundle table, re-render benchmarks, decision tree, and the Mattrx production layout): <a href="https://prepstack.co.in/blog/angular-state-management-comparison-ngrx-signals-ngxs-akita-guide">https://prepstack.co.in/blog/angular-state-management-comparison-ngrx-signals-ngxs-akita-guide</a></p>
<p>The mental model first</p>
<p>Most teams treat state as one thing. There are four kinds, and each has a different right answer.</p>
<p>1. Local UI state — dropdown open, active tab, current input. Lives in one component. Right answer: signal() in the component.</p>
<p>2. Server cache — current list from the API. Lives in a service, cached, invalidated. Right answer: toSignal(http.get(...)) — or NgRx Entity if mutations are complex.</p>
<p>3. Shared feature state — selected rows, bulk-edit draft. Lives in a service. Right answer: signal in a feature service.</p>
<p>4. App-wide workflow state — multi-step transitions with audit log, retry, time-travel debugging. Right answer: NgRx (SignalStore for new code; classic if you need full Redux pattern with Effects).</p>
<p>Trying to use one library for all four — which everyone tried with NgRx around 2019 — is what generated the "NgRx is too much boilerplate" backlash. The library wasn't wrong; the scope it was applied to was.</p>
<p>The same feature, six ways</p>
<p>Mattrx's /campaigns page (list, debounced search, multi-select, optimistic archive, audit log) implemented in six libraries:</p>
<p>— Pure Signals: 0 KB, 70 LOC, 2 files. Mattrx default — covers 80% of state.</p>
<p>— NgRx SignalStore: ~6 KB, 75 LOC, 2 files. Where new NgRx code goes in 2026. Boilerplate gap with Signals is now 5 LOC, not 140.</p>
<p>— NgRx ComponentStore: ~5 KB. Feature-local heavy workflows.</p>
<p>— NgRx classic Store + Effects + Entity: ~25 KB, 210 LOC, 5 files. Full Redux pattern. Still right when you need airtight Action/Reducer separation.</p>
<p>— NGXS: ~14 KB, 140 LOC. Smaller-boilerplate alternative to classic NgRx. Not adopted at Mattrx — no concrete advantage over SignalStore in 2026.</p>
<p>— Akita: ~10 KB, 100 LOC, 4 files. Deprecated (maintenance mode since 2023).</p>
<p>The surprising re-render result</p>
<p>The big jump in render granularity — 47 re-renders per keystroke down to 3 — does not come from picking NgRx vs Signals. It comes from moving to Signals at all, regardless of library wrapping them.</p>
<p>NgRx classic with store.selectSignal() performs the same as pure Signals. BehaviorSubject + | async still pays the one-per-consumer cost.</p>
<p>If you are already on NgRx classic, you do not need to migrate libraries. You just need to switch your selectors from select() to selectSignal().</p>
<p>The 2026 rule of thumb</p>
<p>In one line:</p>
<p>Signals for state. RxJS for streams. NgRx SignalStore when the state is shared, workflow-heavy, and benefits from DevTools or time-travel.</p>
<p>The full deep-dive with all six code samples, the complete bundle table, re-render benchmarks, DevTools comparison, decision tree, and the Mattrx production layout (library per feature) is on PrepStack:</p>
<p><a href="https://prepstack.co.in/blog/angular-state-management-comparison-ngrx-signals-ngxs-akita-guide">https://prepstack.co.in/blog/angular-state-management-comparison-ngrx-signals-ngxs-akita-guide</a></p>
<p>If this saved you a code-review argument, a 💖 or bookmark helps it reach more Angular devs.</p>
]]></content:encoded></item></channel></rss>