<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Senira Mendis — Software Engineer — Blog</title>
    <link>https://senira-mendis-portfolio.vercel.app/blog</link>
    <atom:link href="https://senira-mendis-portfolio.vercel.app/feed.xml" rel="self" type="application/rss+xml" />
    <description>Engineering write-ups from Senira Mendis, Full-Stack Developer.</description>
    <language>en-us</language>
    <lastBuildDate>Tue, 22 Sep 2026 14:01:52 GMT</lastBuildDate>
    <item>
      <title>Why I Used Pessimistic Locking to Stop Double-Bookings in AgriLease</title>
      <link>https://senira-mendis-portfolio.vercel.app/blog/pessimistic-locking-agrilease</link>
      <guid isPermaLink="true">https://senira-mendis-portfolio.vercel.app/blog/pessimistic-locking-agrilease</guid>
      <pubDate>Mon, 10 Aug 2026 00:00:00 GMT</pubDate>
      <description>A look at the concurrency bug that nearly broke AgriLease&apos;s booking engine during peak season, and why optimistic locking wasn&apos;t enough to fix it.</description>
      <content:encoded><![CDATA[<p>AgriLease is a sharing-economy platform for agricultural machinery in Sri Lanka — think of it as a booking system where dozens of farmers can be racing to reserve the same tractor for the same narrow planting window. That&apos;s a concurrency problem hiding inside what looks like a simple booking form.</p><p>Early on, two requests could both read &apos;this tractor is free on the 14th&apos;, both pass validation, and both write a booking row — a classic race condition. Optimistic locking (checking a version number before committing) reduced the failures but didn&apos;t eliminate them under real load, because the retry logic on the client just tried the same doomed booking again.</p><p>The fix was pessimistic locking at the database level: `SELECT ... FOR UPDATE` on the relevant availability row for the duration of the booking transaction, so a second request is forced to wait rather than race. It costs a little throughput under contention, but for a resource with hard physical availability — a single tractor can only be in one field at a time — correctness matters far more than raw concurrency.</p><p>The bigger lesson: not every problem needs the trendier solution. Optimistic locking is usually the right default for low-contention writes, but the moment a resource is physically singular and demand spikes at the same time (peak planting season, everyone booking the same three tractors), pessimistic locking is the boring, correct answer.</p>]]></content:encoded>
      <category>Backend</category>
      <category>PostgreSQL</category>
      <category>Concurrency</category>
    </item>
    <item>
      <title>What Being Scrum Master on Dopmin&apos;s Web Platform Actually Taught Me</title>
      <link>https://senira-mendis-portfolio.vercel.app/blog/scrum-master-lessons-dopmin</link>
      <guid isPermaLink="true">https://senira-mendis-portfolio.vercel.app/blog/scrum-master-lessons-dopmin</guid>
      <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
      <description>Running sprints for a small cross-functional team is a different skill from writing the code — here is what stuck after leading Dopmin through several sprint cycles.</description>
      <content:encoded><![CDATA[<p>Leading Dopmin&apos;s corporate web platform as Scrum Master, on top of writing a lot of the code myself, taught me that the hardest part of Scrum isn&apos;t the ceremonies — it&apos;s protecting the team&apos;s focus between them.</p><p>A two-week sprint dies by a thousand small interruptions: a client message that becomes an unplanned mid-sprint request, a &quot;quick&quot; bug that eats a full day, a scope conversation that should have happened at planning instead happening on day 8. My job stopped being just about code and became about noticing when scope was quietly drifting and saying so out loud before it became a crisis at the retro.</p><p>The other thing that surprised me: velocity numbers are far less useful than the conversation you have about why they moved. A sprint that &quot;under-delivered&quot; but surfaced a real architectural risk early is a good sprint. A sprint that hit every point but shipped something nobody double-checked against the actual requirement is a quiet failure.</p><p>Since then, I try to run retros around one question: what almost went wrong that we caught in time, and what didn&apos;t we catch? That question surfaces more useful signal than a burndown chart ever has.</p>]]></content:encoded>
      <category>Agile</category>
      <category>Scrum</category>
      <category>Leadership</category>
    </item>
    <item>
      <title>Electron IPC State Sync: The Part Nobody Warns You About</title>
      <link>https://senira-mendis-portfolio.vercel.app/blog/electron-ipc-lessons</link>
      <guid isPermaLink="true">https://senira-mendis-portfolio.vercel.app/blog/electron-ipc-lessons</guid>
      <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
      <description>Building the Dopmin Web Scraper&apos;s Electron app meant learning the hard way that main/renderer process separation is a discipline, not a default.</description>
      <content:encoded><![CDATA[<p>Electron makes it deceptively easy to reach across the main/renderer boundary and just... call something. It works, right up until your app has real concurrent state — in the Dopmin Web Scraper&apos;s case, a query-expansion module scaling out permutations while a concurrent scraping engine is mid-run against dynamic web elements.</p><p>The failure mode is subtle: the renderer shows one version of &quot;current progress,&quot; the main process has already moved past it, and now your UI is lying to the user by a few seconds — just enough to look broken without ever throwing an error.</p><p>The fix was treating IPC like an actual API contract instead of a convenient tunnel: typed message shapes, a single source of truth for state living in the main process, and the renderer only ever rendering what it was explicitly sent — never assuming, never polling ad hoc. Once that discipline was in place, adding encrypted local state persistence on top was straightforward, because the state was finally well-defined enough to serialize safely.</p><p>If you&apos;re building anything in Electron beyond a static shell, decide on your IPC contract before you write the first scraping job, not after the third race condition report.</p>]]></content:encoded>
      <category>Electron</category>
      <category>Node.js</category>
      <category>Desktop Apps</category>
    </item>
  </channel>
</rss>