<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amir Marcel</title>
    <description>The latest articles on DEV Community by Amir Marcel (@amirmarcel).</description>
    <link>https://dev.to/amirmarcel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034778%2Fa2d2625d-5078-4054-a82c-10f51e403377.jpg</url>
      <title>DEV Community: Amir Marcel</title>
      <link>https://dev.to/amirmarcel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amirmarcel"/>
    <language>en</language>
    <item>
      <title>Your Tests Are Green. Your Distributed System Still Has a Bottleneck.</title>
      <dc:creator>Amir Marcel</dc:creator>
      <pubDate>Fri, 31 Jul 2026 17:07:54 +0000</pubDate>
      <link>https://dev.to/amirmarcel/your-tests-are-green-your-distributed-system-still-has-a-bottleneck-1eim</link>
      <guid>https://dev.to/amirmarcel/your-tests-are-green-your-distributed-system-still-has-a-bottleneck-1eim</guid>
      <description>&lt;p&gt;When I started building claims-pipeline, I thought I was building an event-driven backend.&lt;/p&gt;

&lt;p&gt;It turned out I was really building a lesson in distributed systems.&lt;/p&gt;

&lt;p&gt;The project itself is fairly straightforward:&lt;/p&gt;

&lt;p&gt;Generate synthetic healthcare claims&lt;br&gt;
Publish them through SNS&lt;br&gt;
Process them with SQS-backed workers&lt;br&gt;
Compute deterministic provider rankings&lt;br&gt;
Store the results in PostgreSQL&lt;br&gt;
Serve rankings through a FastAPI service&lt;br&gt;
Scale workers automatically with Kubernetes and KEDA&lt;/p&gt;

&lt;p&gt;The interesting part wasn't getting the happy path working.&lt;/p&gt;

&lt;p&gt;It was discovering what happened when I stopped thinking like a developer and started thinking like production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;The Architecture&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Here's the entire pipeline.&lt;/p&gt;

&lt;p&gt;Synthetic Claims&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    SNS Topic&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
     SQS Queue&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
 Validation Worker&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
  Scoring Queue&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
  Scoring Worker&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
   PostgreSQL&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
  Ranking API&lt;/p&gt;

&lt;p&gt;The goals were simple:&lt;/p&gt;

&lt;p&gt;asynchronous processing&lt;br&gt;
independently scalable workers&lt;br&gt;
safe retries&lt;br&gt;
dead-letter queues&lt;br&gt;
deterministic provider scoring&lt;br&gt;
observability under load&lt;/p&gt;

&lt;p&gt;Every consumer is idempotent.&lt;/p&gt;

&lt;p&gt;Every failure can be replayed.&lt;/p&gt;

&lt;p&gt;Every ranking is deterministic.&lt;/p&gt;

&lt;p&gt;The language model never computes a score; instead, it only explains one that's already been calculated.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Everything Passed&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Unit tests passed.&lt;/p&gt;

&lt;p&gt;Integration tests passed.&lt;/p&gt;

&lt;p&gt;End-to-end tests passed.&lt;/p&gt;

&lt;p&gt;The scoring algorithm behaved exactly as expected.&lt;/p&gt;

&lt;p&gt;The retry logic worked.&lt;/p&gt;

&lt;p&gt;Dead-letter replay worked.&lt;/p&gt;

&lt;p&gt;At that point, it's tempting to believe the system is "done."&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Then I Ran a Load Test&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
I pushed 10,000 synthetic claim events through the pipeline.&lt;/p&gt;

&lt;p&gt;KEDA began scaling workers based on queue depth.&lt;/p&gt;

&lt;p&gt;Exactly as designed.&lt;/p&gt;

&lt;p&gt;Validation workers climbed from one replica to five and stayed there while the queue drained.&lt;/p&gt;

&lt;p&gt;Then something unexpected happened.&lt;/p&gt;

&lt;p&gt;The scoring workers kept bouncing.&lt;/p&gt;

&lt;p&gt;5 replicas&lt;br&gt;
↓&lt;/p&gt;

&lt;p&gt;3 replicas&lt;br&gt;
↓&lt;/p&gt;

&lt;p&gt;2 replicas&lt;br&gt;
↓&lt;/p&gt;

&lt;p&gt;5 replicas&lt;br&gt;
↓&lt;/p&gt;

&lt;p&gt;4 replicas&lt;/p&gt;

&lt;p&gt;Throughput became inconsistent.&lt;/p&gt;

&lt;p&gt;Nothing had failed.&lt;/p&gt;

&lt;p&gt;No alerts fired.&lt;/p&gt;

&lt;p&gt;Every test was still green.&lt;/p&gt;

&lt;p&gt;Yet the system was clearly fighting itself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;The Bottleneck Was My Own Correctness Guarantee&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Earlier in the project I'd added per-provider advisory locks.&lt;/p&gt;

&lt;p&gt;The idea was straightforward.&lt;/p&gt;

&lt;p&gt;If two workers tried updating the same provider simultaneously, one would wait.&lt;/p&gt;

&lt;p&gt;No race conditions.&lt;/p&gt;

&lt;p&gt;No corrupted aggregates.&lt;/p&gt;

&lt;p&gt;Correctness guaranteed.&lt;/p&gt;

&lt;p&gt;Under normal load, it worked perfectly.&lt;/p&gt;

&lt;p&gt;Under heavy concurrency, it became the bottleneck.&lt;/p&gt;

&lt;p&gt;As KEDA added more workers, more pods competed for the same provider locks.&lt;/p&gt;

&lt;p&gt;The system was scaling.&lt;/p&gt;

&lt;p&gt;The work wasn't.&lt;/p&gt;

&lt;p&gt;The mechanism that guaranteed correctness had become the mechanism limiting throughput.&lt;/p&gt;

&lt;p&gt;That wasn't a Kubernetes problem.&lt;/p&gt;

&lt;p&gt;It wasn't an autoscaling problem.&lt;/p&gt;

&lt;p&gt;It was an architecture problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;No Test Could Have Shown Me This&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Every unit test was green because every unit behaved correctly.&lt;/p&gt;

&lt;p&gt;Every integration test passed because the components interacted correctly.&lt;/p&gt;

&lt;p&gt;None of those tests simulated dozens of workers competing for the same shared resource.&lt;/p&gt;

&lt;p&gt;Load testing exposed a property I hadn't actually tested:&lt;/p&gt;

&lt;p&gt;contention.&lt;/p&gt;

&lt;p&gt;Correctness and throughput are different qualities.&lt;/p&gt;

&lt;p&gt;A system can be perfectly correct while performing terribly under realistic concurrency.&lt;/p&gt;

&lt;p&gt;That was probably the biggest lesson from the project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Why the System Is Still Deterministic&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
One design decision I intentionally kept was separating deterministic logic from AI.&lt;/p&gt;

&lt;p&gt;Provider rankings are computed entirely by code.&lt;/p&gt;

&lt;p&gt;The LLM never participates in the decision.&lt;/p&gt;

&lt;p&gt;Instead, it receives grounded facts:&lt;/p&gt;

&lt;p&gt;provider score&lt;br&gt;
ranking&lt;br&gt;
quality metrics&lt;br&gt;
neighboring providers&lt;/p&gt;

&lt;p&gt;Its only job is explaining why the provider ranked where it did.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;p&gt;rankings are reproducible&lt;br&gt;
explanations can be evaluated independently&lt;br&gt;
business logic remains fully testable&lt;/p&gt;

&lt;p&gt;The AI augments the system.&lt;/p&gt;

&lt;p&gt;It doesn't control it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;What I'd Change Next&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
If this were evolving into a production service, the next step wouldn't be tuning KEDA.&lt;/p&gt;

&lt;p&gt;I'd revisit the synchronization strategy itself.&lt;/p&gt;

&lt;p&gt;Some possibilities include:&lt;/p&gt;

&lt;p&gt;partitioning work by provider&lt;br&gt;
reducing lock granularity&lt;br&gt;
batching updates&lt;br&gt;
redesigning aggregation to minimize contention&lt;/p&gt;

&lt;p&gt;The important lesson wasn't finding the perfect solution.&lt;/p&gt;

&lt;p&gt;It was discovering the real bottleneck before production users did.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Building this project reinforced something I think applies well beyond healthcare systems.&lt;/p&gt;

&lt;p&gt;Distributed systems aren't difficult because individual components are hard.&lt;/p&gt;

&lt;p&gt;They're difficult because every component can be perfectly correct, yet their interactions under real load reveal entirely different behavior.&lt;/p&gt;

&lt;p&gt;A green test suite tells you your code works.&lt;/p&gt;

&lt;p&gt;A realistic load test tells you whether your architecture does.&lt;/p&gt;

&lt;p&gt;The project is open source if you'd like to explore the implementation or follow along as it evolves.&lt;/p&gt;




&lt;p&gt;The complete project is open source:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/amirmarcel/claims-pipeline" rel="noopener noreferrer"&gt;https://github.com/amirmarcel/claims-pipeline&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>architecture</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
