<?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: Vinod Erramsetty</title>
    <description>The latest articles on DEV Community by Vinod Erramsetty (@vinod_erramsetty_191b3e05).</description>
    <link>https://dev.to/vinod_erramsetty_191b3e05</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%2F4029219%2Fb52afbd3-916c-441d-8d2e-17e6acbf4982.png</url>
      <title>DEV Community: Vinod Erramsetty</title>
      <link>https://dev.to/vinod_erramsetty_191b3e05</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vinod_erramsetty_191b3e05"/>
    <language>en</language>
    <item>
      <title>7 Production Issues Every Spring Boot Developer Should Learn Before Becoming Senior</title>
      <dc:creator>Vinod Erramsetty</dc:creator>
      <pubDate>Mon, 03 Aug 2026 18:43:52 +0000</pubDate>
      <link>https://dev.to/vinod_erramsetty_191b3e05/7-production-issues-every-spring-boot-developer-should-learn-before-becoming-senior-1nk5</link>
      <guid>https://dev.to/vinod_erramsetty_191b3e05/7-production-issues-every-spring-boot-developer-should-learn-before-becoming-senior-1nk5</guid>
      <description>&lt;p&gt;After working on enterprise applications and distributed microservices, I have realized that the biggest challenges rarely come from writing business logic. They come from handling production traffic, failures, concurrency, and unexpected edge cases.&lt;/p&gt;

&lt;p&gt;Here are seven lessons that every Spring Boot developer should know before calling themselves a senior engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Never Assume an API Will Be Called Only Once
&lt;/h2&gt;

&lt;p&gt;One of the most common mistakes is assuming a client sends exactly one request.&lt;/p&gt;

&lt;p&gt;In reality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users refresh the page.&lt;/li&gt;
&lt;li&gt;Mobile apps retry automatically.&lt;/li&gt;
&lt;li&gt;API gateways retry requests.&lt;/li&gt;
&lt;li&gt;Kafka consumers may reprocess events.&lt;/li&gt;
&lt;li&gt;Network failures cause duplicate submissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your endpoint creates an order, payment, or booking every time it receives a request, duplicates are almost guaranteed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Approach
&lt;/h3&gt;

&lt;p&gt;Design APIs to be &lt;strong&gt;idempotent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use an Idempotency-Key.&lt;/li&gt;
&lt;li&gt;Store processed request IDs.&lt;/li&gt;
&lt;li&gt;Ignore duplicate requests safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Production systems should always expect duplicate requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Database Transactions Are Not Enough
&lt;/h2&gt;

&lt;p&gt;Many developers believe this solves everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A transaction protects changes &lt;strong&gt;inside a single database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; protect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka publishing&lt;/li&gt;
&lt;li&gt;Email sending&lt;/li&gt;
&lt;li&gt;External REST APIs&lt;/li&gt;
&lt;li&gt;Redis updates&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your database commits successfully but Kafka publishing fails, your system is already inconsistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Approach
&lt;/h3&gt;

&lt;p&gt;Use patterns such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactional Outbox&lt;/li&gt;
&lt;li&gt;Saga Pattern&lt;/li&gt;
&lt;li&gt;Event-driven architecture&lt;/li&gt;
&lt;li&gt;Retry with dead-letter queues&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Don't Trust External APIs
&lt;/h2&gt;

&lt;p&gt;Every external service will eventually fail.&lt;/p&gt;

&lt;p&gt;Your payment provider.&lt;/p&gt;

&lt;p&gt;Your authentication service.&lt;/p&gt;

&lt;p&gt;Your notification service.&lt;/p&gt;

&lt;p&gt;Even your own internal microservices.&lt;/p&gt;

&lt;p&gt;Never assume another service is always available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Protection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Retries&lt;/li&gt;
&lt;li&gt;Circuit Breakers&lt;/li&gt;
&lt;li&gt;Fallback logic&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Failing fast is usually better than waiting forever.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Logging Is More Valuable Than You Think
&lt;/h2&gt;

&lt;p&gt;When production goes down, nobody asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Was the code clean?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everyone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What happened?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Poor logging turns a five-minute issue into a five-hour investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Good Logs Include
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Correlation ID&lt;/li&gt;
&lt;li&gt;Request ID&lt;/li&gt;
&lt;li&gt;User ID (where appropriate)&lt;/li&gt;
&lt;li&gt;Service name&lt;/li&gt;
&lt;li&gt;Execution time&lt;/li&gt;
&lt;li&gt;Error details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid logging entire request bodies or sensitive information.&lt;/p&gt;

&lt;p&gt;Logs should help you debug—not create new security problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Performance Problems Usually Start in the Database
&lt;/h2&gt;

&lt;p&gt;Most slow APIs aren't caused by Java.&lt;/p&gt;

&lt;p&gt;They're caused by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing indexes&lt;/li&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Loading unnecessary data&lt;/li&gt;
&lt;li&gt;Multiple database calls inside loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before optimizing Java code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check SQL execution plans.&lt;/li&gt;
&lt;li&gt;Measure database latency.&lt;/li&gt;
&lt;li&gt;Cache frequently used data.&lt;/li&gt;
&lt;li&gt;Fetch only what you need.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always measure before optimizing.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Handle Concurrency Explicitly
&lt;/h2&gt;

&lt;p&gt;Concurrency bugs are among the hardest to reproduce.&lt;/p&gt;

&lt;p&gt;Imagine two requests arriving at exactly the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A
Request B

Both check:
Balance = ₹100

Both withdraw ₹100

Final Balance = -₹100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything worked correctly from each request's perspective.&lt;/p&gt;

&lt;p&gt;Together, they corrupted the data.&lt;/p&gt;

&lt;p&gt;Solutions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimistic Locking&lt;/li&gt;
&lt;li&gt;Pessimistic Locking&lt;/li&gt;
&lt;li&gt;Distributed Locks&lt;/li&gt;
&lt;li&gt;Atomic database updates&lt;/li&gt;
&lt;li&gt;Idempotent operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Concurrency isn't a rare edge case.&lt;/p&gt;

&lt;p&gt;It's a daily production reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Monitoring Is Part of the Application
&lt;/h2&gt;

&lt;p&gt;If you can't observe your application, you can't operate it.&lt;/p&gt;

&lt;p&gt;Every production service should expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health checks&lt;/li&gt;
&lt;li&gt;Metrics&lt;/li&gt;
&lt;li&gt;Request latency&lt;/li&gt;
&lt;li&gt;Error rates&lt;/li&gt;
&lt;li&gt;JVM metrics&lt;/li&gt;
&lt;li&gt;Database latency&lt;/li&gt;
&lt;li&gt;Kafka consumer lag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern observability tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Micrometer&lt;/li&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;li&gt;OpenTelemetry&lt;/li&gt;
&lt;li&gt;ELK Stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best production incidents are the ones users never notice because your monitoring detected them first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Being a senior Spring Boot developer isn't about memorizing annotations or frameworks.&lt;/p&gt;

&lt;p&gt;It's about designing systems that continue to work when networks fail, traffic spikes, duplicate requests arrive, and dependencies become unavailable.&lt;/p&gt;

&lt;p&gt;Production engineering is less about writing more code and more about building software that remains reliable under real world conditions.&lt;/p&gt;

&lt;p&gt;If you're just starting your backend journey, focus on these concepts early. They'll have a much bigger impact on your career than learning another framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Java #SpringBoot #Microservices #Backend #SoftwareEngineering #SystemDesign #DistributedSystems #Kafka #Programming #DevOps
&lt;/h1&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>java</category>
      <category>springboot</category>
    </item>
    <item>
      <title>We hit an at-least-once delivery trap. Here is how we fixed the race conditions.</title>
      <dc:creator>Vinod Erramsetty</dc:creator>
      <pubDate>Tue, 14 Jul 2026 20:33:03 +0000</pubDate>
      <link>https://dev.to/vinod_erramsetty_191b3e05/we-hit-an-at-least-once-delivery-trap-here-is-how-we-fixed-the-race-conditions-49k3</link>
      <guid>https://dev.to/vinod_erramsetty_191b3e05/we-hit-an-at-least-once-delivery-trap-here-is-how-we-fixed-the-race-conditions-49k3</guid>
      <description>&lt;p&gt;If you are running an event-driven architecture with message queues like Kafka, you already know the drill, scalability is awesome, but network partitions, out-of-order delivery, and duplicate messages come with the territory.&lt;/p&gt;

&lt;p&gt;Our team recently learned this the hard way during a production overhaul. A flood of duplicate webhooks bypassed our application level checks, creating a nasty race condition that left our database in an inconsistent state.&lt;/p&gt;

&lt;p&gt;Most enterprise message brokers guarantee at-least-once delivery. That means your consumers will get duplicates eventually. If you are dealing with critical operations—like tracking payments or updating inventory processing a duplicate event will absolutely corrupt your data state.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Breakdown: When validations fail concurrently&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our issue was a subtle concurrent execution race condition. Two identical update events hit our API nodes just milliseconds apart.&lt;/p&gt;

&lt;p&gt;Because the workload was spread across multiple running container instances, both instances ran their database validation checks at the exact same moment. Both saw that the data didn't exist yet, both passed validation, and both written to the database. Our application level guardrails were completely useless against concurrent requests.&lt;/p&gt;

&lt;p&gt;To fix it, we had to implement the &lt;strong&gt;Idempotent Consumer Pattern&lt;/strong&gt; using an atomic locking and verification layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Layer 1: Distributed Locking via Redis (SETNX)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our first line of defense is a fast caching layer using Redis. Before any worker node is allowed to touch an incoming event, it must acquire an atomic distributed lock tied to the unique event_id.&lt;/p&gt;

&lt;p&gt;We use the atomic SETNX (Set if Not Exists) command with a strict 30 second TTL to ensure locks clear automatically if a container randomly dies mid-execution:&lt;/p&gt;

&lt;p&gt;If a duplicate message lands on worker B while worker A is still processing the original, worker B fails to get the lock and exits gracefully without touching our primary data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Layer 2: Relational database unique constraints as the final guardrail&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Redis handles 99% of the noise, but distributed networks are messy. If the Redis cluster encounters a failover or drops a packet, a duplicate could still leak through. We needed a bulletproof backup.&lt;/p&gt;

&lt;p&gt;We added an idempotency_keys table to our core relational database with a strict Unique Constraint on the key_hash column.&lt;/p&gt;

&lt;p&gt;Now, when a worker updates the business logic, it must write the event hash to this table inside the exact same database transaction block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker opens database transaction.&lt;/li&gt;
&lt;li&gt;Worker updates the core business data.&lt;/li&gt;
&lt;li&gt;Worker inserts the event hash into idempotency_keys.&lt;/li&gt;
&lt;li&gt;Worker commits the transaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a duplicate somehow slips past Redis, the database engine catches the duplicate key hash, throws a UniqueConstraintViolationException, and forces an immediate, atomic rollback of the entire transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Payoff&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Decoupling our validation from execution and enforcing atomic operations at both the memory and database levels completely resolved the issue. We threw a stress test of 10,000 concurrent duplicate requests per second at this pipeline, and we achieved 100% data consistency with practically zero performance overhead.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
