<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://mourjo.me/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mourjo.me/blog/" rel="alternate" type="text/html" /><updated>2026-08-01T12:12:09+02:00</updated><id>https://mourjo.me/blog/feed.xml</id><title type="html">Mourjo Sen’s Blog</title><subtitle>Thoughts of a software engineer about life, tech and everything in between. A place to put down my thoughts for my own recollection more than anything else. If you like it, be sure to drop me an email!</subtitle><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><entry><title type="html">Bengal said No to Computers - I won’t say No to AI</title><link href="https://mourjo.me/blog/musings/2026/08/01/bengal-said-no-to-computers-i-wont-say-no-to-ai/" rel="alternate" type="text/html" title="Bengal said No to Computers - I won’t say No to AI" /><published>2026-08-01T11:44:58+02:00</published><updated>2026-08-01T11:44:58+02:00</updated><id>https://mourjo.me/blog/musings/2026/08/01/bengal-said-no-to-computers-i-wont-say-no-to-ai</id><content type="html" xml:base="https://mourjo.me/blog/musings/2026/08/01/bengal-said-no-to-computers-i-wont-say-no-to-ai/"><![CDATA[<p>I stumbled upon an uncomfortable question while working on a demo about property-based testing. For years, I have been an advocate of Jqwik, a remarkable open-sourced library that implements property-based testing in Java. Well before the AI revolution, I had been talking on and off stage about it. Recently, I discovered that Jqwik’s latest version carries an anti-AI clause in its usage policy.</p>

<p>Is it unethical to use AI? I don’t think the answer is simple. I battle with two sides of the argument at once: the author is right to be concerned while I can’t afford to agree with him.</p>

<hr />

<p>My non-negotiable - my bottom line, the reason I do anything - is to put food on the table for my family. Part of that means doing what the industry expects - to stay relevant, for one thing, to keep up with the new speed of doing things, for another. If AI makes all of our jobs irrelevant tomorrow, I’ll gladly become a farmer, clean homes - even steal bread if I have to. Providing for my family is not a philosophical position, it is as real as it gets. It is survival. I do not have the luxury of opting out of what the industry demands.</p>

<p>That does not however mean that I would stifle the work of others - far from it. I would like to believe that we can mutually respect each other while disagreeing on specific topics. I have deep respect for the author’s work and his stance on AI. That is why, in my experiments with Jqwik, I have only used version 1.9, the last release before the anti-AI clause was added.</p>

<p>But the irony of it all is in why I am playing with Jqwik at all: to catch what AI misses.</p>

<p>Jqwik is one of the few tools we have to catch AI’s faults. It is a testing library that lets you define invariants and properties that are validated at runtime. Unlike handwritten tests, which sample test cases, Jqwik explores the problem space for inconsistencies. It was always a powerful tool but today it is invaluable. The ability to interrogate what we build solves many problems that AI brings, ranging from hallucinations to subtle logical gaps.</p>

<p>Doesn’t that highlight the power of human ingenuity? A library written entirely by hand that catches AI’s mistakes? Ironically, if the tools we need are the ones that discourage interfacing with AI, what are we left to correct AI with?</p>

<hr />

<p>You have heard of Bangalore. But you probably have not heard of Kolkata, a city in Bengal that was once one of the most prominent cities in India. There was a saying I grew up with: what Bengal thinks today, India thinks tomorrow. That is no longer true. Today, no one thinks of Bengal at all.</p>

<p>In the 70s, West Bengal’s communist government resisted computerization of its industries, banks and schools. While the rest of India partook in the digital revolution, Bengal resisted change. The state and the city fell behind by decades, if not a century, fading into oblivion today.</p>

<p>The politicians of my homeland made a choice that generations of young adults are still paying for today. What could have been a prosperous IT hub of the east is now a dystopian land embroiled in corruption and brain drain. The 70s government resisted change. It was a principled stance that was wrong and devastatingly costly.</p>

<p>I don’t want to - nor have the luxury to - make the same mistake of resisting change. For better or for worse, I am learning how to engineer software alongside AI. Refusing the industry’s trend risks my bottomline of providing for my family. It is a risk I am not willing to gamble on, and neither should you.</p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[I stumbled upon an uncomfortable question while working on a demo about property-based testing. For years, I have been an advocate of Jqwik, a remarkable open-sourced library that implements property-based testing in Java. Well before the AI revolution, I had been talking on and off stage about it. Recently, I discovered that Jqwik’s latest version carries an anti-AI clause in its usage policy.]]></summary></entry><entry><title type="html">The Glass Dome Of AI Productivity</title><link href="https://mourjo.me/blog/musings/2026/07/23/the-glass-dome-of-ai-productivity/" rel="alternate" type="text/html" title="The Glass Dome Of AI Productivity" /><published>2026-07-23T11:38:17+02:00</published><updated>2026-07-23T11:38:17+02:00</updated><id>https://mourjo.me/blog/musings/2026/07/23/the-glass-dome-of-ai-productivity</id><content type="html" xml:base="https://mourjo.me/blog/musings/2026/07/23/the-glass-dome-of-ai-productivity/"><![CDATA[<p>My dad taught me <a href="https://mourjo.me/blog/musings/2025/09/28/impact-of-a-pivot/">how to program</a>. He taught me how to think about problem solving. One of his key lessons, still with me today after 20 years, was to teach oneself by reading out loud. A lesson, when read out loud, becomes much more understandable, almost magically.</p>

<p>Reading my own code, just like reading a chapter, or this piece, makes the topic clearer in my head. I often used to review my own code on Github before giving it to another human. I’d see improvements that I missed in my own editor. This fundamental aspect of how I used to write code has now changed with AI.</p>

<p>Writing code by hand has the implicit side-effect of spending more time with the code - thereby allocating more mental bandwidth to it. The classic book “Thinking Fast and Slow” talks about this: the cognitive process of complex thoughts is necessarily slow. That’s just how we are wired. Having to type the code, write the test, refactor the source, we implicitly spent more time thinking about the topic. With AI, the time we spend on any piece of code is shortened, if not entirely eliminated. It works really well for a while, for the repeating, mundane aspects of coding, but for every non-trivial change, our brain has to keep up with a much faster stream of incoming information now. We just aren’t capable of processing it all at that speed. I can’t even read every line of generated code, let alone think about it.</p>

<p>That would have been fine. After all, I don’t read the final executable binary in machine code either. Except, there is one tiny caveat: AI is probabilistic. There is a false dichotomy here that we have come to accept - either AI is always right or it is sometimes right. If it’s the first, we don’t need to bother at all about the code. But if it’s the second, we need to understand what AI writes, and that requires us, at the very least, to read the code.</p>

<p>AI’s probabilistic productivity can reliably be grounded by having a <a href="https://hai.stanford.edu/ai-definitions/what-is-human-in-the-loop">human in the loop</a>, which would require making AI work at the slower speed of human cognition. If we want to review AI-written code with the same diligence as we did with human-written code, the whole system needs to become slower and the selling points of AI would be severely diminished. Instead, I tend to trust (hope) that AI is right. I find myself using AI to test AI-generated code and to summarize AI-generated code. The flaw in that way of working is the glaring single point of failure.</p>

<p>Because AI is right most of the time, we expect to rely on it all of the time. If we don’t understand how AI is changing our systems, soon the systems we own will become unrecognizable to us. Is productivity worth the price of clarity? Can we keep going at a speed that our cognitive processes cannot keep up? I reckon there will be a limit that we will hit soon. Whether we aim for the stars or not, we are living under a glass dome of productivity.</p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[My dad taught me how to program. He taught me how to think about problem solving. One of his key lessons, still with me today after 20 years, was to teach oneself by reading out loud. A lesson, when read out loud, becomes much more understandable, almost magically.]]></summary></entry><entry><title type="html">India’s Growth Story At The Airport</title><link href="https://mourjo.me/blog/musings/2025/10/13/indias-growth-story-at-the-airport/" rel="alternate" type="text/html" title="India’s Growth Story At The Airport" /><published>2025-10-13T15:29:55+02:00</published><updated>2025-10-13T15:29:55+02:00</updated><id>https://mourjo.me/blog/musings/2025/10/13/indias-growth-story-at-the-airport</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/10/13/indias-growth-story-at-the-airport/"><![CDATA[<p>Every time I see people taking photos of an aircraft before boarding, it reminds me of India’s growth story. The fact that there are still people who are taking their first flight means Indian economy has grown to allow this person to take a flight. You hardly ever see this in EU. Doesn’t that make you hopeful for the future in India?</p>

<p>I remember my first flight – I even remember my parents’ first international trip. Nowadays it all seems so much easier than it used to be. The fact is, it is not easier, it is that the economic growth over the last twenty years happened right in front of us.</p>

<p>I recall a taxi ride with an ex-colleague who lived his entire life in Calgary, Canada. I asked him if he notices any difference in the day-to-day from how it used to be when he was a child. In fact, he said not only did things not change, he often sees his parents also recall the same experiences. While that in itself, is not good or bad, the fact that economic change is not common first occurred to me in that taxi ride.</p>

<p>Air travel was rare and luxurious. We hardly ever went out to restaurants for meals. Studying abroad or moving to a different country seemed unrealistic. Medications needed traveling to different cities. We saved more than we spent, we spent cautiously, we expected hardships. Our food habits incorporated scarcity – we ate what we could afford – the Sunday lunch was special because of the one day we allowed ourselves meat curry. Buses were the norm, taxis were special. Car ownership was a milestone in life. We had TVs, but only few had large screens. We shared our toys and handed them down. We learnt that wanting is mutually exclusive of happiness. We smiled not because we had things but because we had each other.</p>

<p>Look at the world now. Coffee in Bangalore is costlier than in Amsterdam. Air travel is the default. Eating out is the norm. We cook what we want to eat, we plan holidays to exotic places, and we buy what we want. All fueled by India’s growth story. My grandfather famously said that his pension was a larger sum of money than his salary during his working years.</p>

<p>Like many others, we are used to India growing while we became adults. Yet, the whole of India did not – not at the same time. So if you see someone’s grandpa flying for the first time or someone taking a selfie with an aircraft, know that that is India’s growth in front of you. While you and I have enjoyed some of it for a while, not everyone in India has. We are in this together and we are not there yet. Celebrate the excitement of the first flight. Encourage the selfies. India wins if we all win.</p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[Every time I see people taking photos of an aircraft before boarding, it reminds me of India’s growth story. The fact that there are still people who are taking their first flight means Indian economy has grown to allow this person to take a flight. You hardly ever see this in EU. Doesn’t that make you hopeful for the future in India?]]></summary></entry><entry><title type="html">Impact Of A Pivot</title><link href="https://mourjo.me/blog/musings/2025/09/28/impact-of-a-pivot/" rel="alternate" type="text/html" title="Impact Of A Pivot" /><published>2025-09-28T05:55:32+02:00</published><updated>2025-09-28T05:55:32+02:00</updated><id>https://mourjo.me/blog/musings/2025/09/28/impact-of-a-pivot</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/09/28/impact-of-a-pivot/"><![CDATA[<p>An average child’s greatest obstacle is unawareness. I have never been outstanding at anything. As a child, expecting to find passion in anything was pointless. I was surviving high school from one test to the next - unaware of what I liked or what I wanted to do in life. That’s when a pivot changed my life forever.</p>

<p>While a decision is an obvious choice, a pivot hides itself in the cornucopia of life. It is a choice that remains dormant, unless someone steps in to recognize the need to change course. In the seventh grade, we were first introduced to the subject of Computer Science (CS). It was no more than just another subject I needed to pass.</p>

<p>One warm humid evening, Baba came home to an abysmal CS test result. I was clearly struggling but for some reason, he was convinced that my poor result was a symptom of improper guidance rather than the more common reaction - my insufficient dedication and seriousness. Instead, Baba took a deeper look at things. He decided to teach himself Java and explain it to me, step by step, one day at a time - from haphazard class notes to college level readings.</p>

<p>Baba was known to be a gifted teacher, as his cousins fondly recall to this day. It was never only the method that set him apart though - it was also his outlook. In a society where being an engineer was not only expected but was the be-all-and-end-all, this was the single most radical permission I ever got from my parents: the permission to explore my interests. It took me through the meandering path through English majors, Law school, CS degree from an unknown college ultimately to a masters program abroad.</p>

<p>Tenacious people spend their lives searching for the one thing they are meant to do. I was just lucky. A bad test result and an observant parent, that’s all it took for me. Instead of punishment, he told me to find my passion. Instead of disciplining me, he gave me direction.</p>

<p>One day a few months later, I realized I was no longer struggling. It felt almost natural. It became the reason to wake up in the morning. The horizon expanded, opportunities became abundant. The deeper I went, the more I wanted. That ember he kindled raged into a life-long bonfire.</p>

<p>I naively thought it was always my own calling. In reality, my calling was a faint murmur deafened amidst life’s cacophony, unnoticed by myself even. The gift of a great parent is not to fix what is broken, but to reveal what is possible. The extraordinary impact of a pivot, the lifesaver of an average child.</p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[An average child’s greatest obstacle is unawareness. I have never been outstanding at anything. As a child, expecting to find passion in anything was pointless. I was surviving high school from one test to the next - unaware of what I liked or what I wanted to do in life. That’s when a pivot changed my life forever.]]></summary></entry><entry><title type="html">The Feeling Of Being Tiny</title><link href="https://mourjo.me/blog/musings/2025/08/05/the-feeling-of-being-tiny/" rel="alternate" type="text/html" title="The Feeling Of Being Tiny" /><published>2025-08-05T06:51:14+02:00</published><updated>2025-08-05T06:51:14+02:00</updated><id>https://mourjo.me/blog/musings/2025/08/05/the-feeling-of-being-tiny</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/08/05/the-feeling-of-being-tiny/"><![CDATA[<p>With every clikety-clack of the train inching deeper into the mountainous terrain, I felt myself shrinking – a fleeting, precious feeling of insignificance. Engulfed in the colossal landscape, a quiet introspection took hold.</p>

<p>At Lauterbrunnen, I stood in front of unyielding and majestic mountains. I had to crane my neck to even have a glimpse of the sky. Feeling tiny and irrelevant was a humbling and grounding experience.</p>

<p>The Alps stood for thousands of years. My ambition, fears and achievements – which seemed to be all-important just days ago – now were laughably small. That smallness felt like freedom. Freedom from the shackles of expectation, replaced with profound insignificance.</p>

<p>The immutable mountains were stoic, impervious and unperturbed. Nothing I do will ever change the Alps. My successes will not make them taller, my failures will not make them crumble.</p>

<p>I suddenly didn’t feel afraid of failing, taking chances, experimenting. There was no duress of expectation in Lauterbrunnen – there was only the indifferent beauty in the quiet vastness of the world.</p>

<p>Feeling tiny was liberating. Empowering. Unlimited.</p>

<p><img src="/blog/images/2025-08-05-the-feeling-of-being-tiny-1.jpeg" alt="Image Description" /></p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[With every clikety-clack of the train inching deeper into the mountainous terrain, I felt myself shrinking – a fleeting, precious feeling of insignificance. Engulfed in the colossal landscape, a quiet introspection took hold.]]></summary></entry><entry><title type="html">Reasons To Believe</title><link href="https://mourjo.me/blog/musings/2025/07/26/reasons-to-believe/" rel="alternate" type="text/html" title="Reasons To Believe" /><published>2025-07-26T11:24:00+02:00</published><updated>2025-07-26T11:24:00+02:00</updated><id>https://mourjo.me/blog/musings/2025/07/26/reasons-to-believe</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/07/26/reasons-to-believe/"><![CDATA[<p>The dawn quietly crept in through the cracked windows. Lazy and exhausted from a week-long trip, I teetered on the precipice of skipping the weekly workout class. My muscles whispered for a softer start to the weekend. Yet, propelled by some unseen force, I went. And there, amidst the prosaic mundanity of a routine class, something profound unravelled.</p>

<p>Five minutes into the class, Mr O., our instructor, a strong disciplinarian, whispered something in Dutch into the microphone. To my utter bewilderment, he then began to sing. A ripple of confused expressions spread through the room, mirroring my own confusion. The reason unfurled itself as my eyes scanned the class. One of our classmates, recently diagnosed with liver cancer, had achieved the impossible. After months of what I can only imagine to be a despairing hell, she stood among us, miraculously returned.</p>

<p>Mr O.’s song was a standing ovation to her perseverance and her indomitable spirit – a spirit that eclipses any strength training session. If such a moment doesn’t stir the soul, ignite a longing for a higher purpose, to be a better self, to find meaning in the ordinary, what possibly could?</p>

<p>I missed both classes last week and I almost didn’t go today – yet, through some inexplicable coincidence, I was there, just where I needed to be – to be part of something bigger, to get a chance to cry with someone, to partake in the celebration of life. Call it coincidence, call it divine intervention, call it a miracle. Just another Saturday’s prosaic mundanity transcended into poetic resurgence.</p>

<p>If you need reasons to believe, it’s right there – you just need to look.</p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[The dawn quietly crept in through the cracked windows. Lazy and exhausted from a week-long trip, I teetered on the precipice of skipping the weekly workout class. My muscles whispered for a softer start to the weekend. Yet, propelled by some unseen force, I went. And there, amidst the prosaic mundanity of a routine class, something profound unravelled.]]></summary></entry><entry><title type="html">S3: Secure Serverless Shortlinks On AWS</title><link href="https://mourjo.me/blog/musings/2025/07/10/secure-serverless-shortlinks-on-aws/" rel="alternate" type="text/html" title="S3: Secure Serverless Shortlinks On AWS" /><published>2025-07-10T05:54:18+02:00</published><updated>2025-07-10T05:54:18+02:00</updated><id>https://mourjo.me/blog/musings/2025/07/10/secure-serverless-shortlinks-on-aws</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/07/10/secure-serverless-shortlinks-on-aws/"><![CDATA[<p>Inspiration is hard to come by in many fields – but not so in software engineering. When I attended a course on AWS offerings last year, I decided to try it out myself. With a little bit of hard work, the result was a serverless URL shortener built on AWS called shortener called <a href="https://liliput.mourjo.me">Liliput</a>.</p>

<p>A URL shortener is not an uncommon project but I wanted to focus specifically on:</p>
<ul>
  <li><strong>Security</strong>: All operations in Liliput put security as a primary concern.</li>
  <li><strong>Atomicity</strong> with DyanamoDB for unique short links: DynamoDB supports <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.AtomicCounters">atomic counters</a>, that is used in the link generation logic</li>
</ul>

<p>The source code is on <a href="https://github.com/mourjo/liliput">Github</a>. The home screen looks like this:</p>

<p><img src="/blog/images/2025-07-10-secure-serverless-shortlinks-on-aws-1.webp" alt="Image Description" /></p>

<h2 id="what-liliput-does">What Liliput does</h2>
<p>Liliput is a secure, serverless shortener of URLs with the following features:</p>
<ul>
  <li>Logged-in users can create short links (like <a href="https://liliput.mourjo.me/l/G4LZ1a">liliput.mourjo.me/l/G4LZ1a</a>)</li>
  <li>Logged-in users can also view their previously created links</li>
  <li>Anonymous users can visit shortlinks</li>
  <li>All short links are unique to a user - if the same long link is shorted multiple times, the same short URL is returned</li>
  <li>Usage counts (expansion count) are tracked and displayed on the UI</li>
  <li>Links can be expanded at most 25 times to prevent illegitimate usage (spam and phishing)</li>
  <li>Verified users can create links that can be expanded unlimited times (verification process is through the source code)</li>
</ul>

<p>Here is <a href="https://www.youtube.com/watch?v=EzdWG2OjYpE">a video</a> that demoes the application.</p>

<h2 id="focus-on-security">Focus on Security</h2>
<p>This application is secure because of the following features:</p>
<ul>
  <li>To create short links, you need a valid Google account</li>
  <li>Signing into Google uses the OAuth 2.0 with <a href="https://www.perplexity.ai/search/what-is-pkce-authentication-iipO5PXWQ.KvOOZvx_CQ7A">PKCE</a></li>
  <li>All cookies are<a href="https://raw.githubusercontent.com/mourjo/liliput/refs/heads/master/images/cookies.png"> HTTP-only</a> (Javascript cannot access it)</li>
  <li>Links can only be expanded 25 times to prevent misuse, unless the user is verified</li>
  <li>All APIs are rate limited and alerts for when rate limit is breached</li>
</ul>

<h2 id="system-design">System Design</h2>
<p>The system is built using the following:</p>
<ul>
  <li>DynamoDB for storage</li>
  <li>Amazon Cognito and Google Oauth for login flows</li>
  <li>Lambda functions for logic implementation (shown below)</li>
  <li>API gateway for mapping lambda functions to routes</li>
  <li>Authorizer in API gateway with another Lambda function</li>
  <li>Public DNS (<a href="https://liliput.mourjo.me">liliput.mourjo.me</a>) for APIs</li>
</ul>

<h4 id="logged-in-flows">Logged In Flows</h4>
<p><img src="https://raw.githubusercontent.com/mourjo/liliput/refs/heads/master/images/flowchart_01_homepage.svg" /></p>

<p><img src="https://raw.githubusercontent.com/mourjo/liliput/refs/heads/master/images/flowchart_02_create_link.svg" /></p>

<h4 id="anonymous-flows">Anonymous Flows</h4>
<p><img src="https://raw.githubusercontent.com/mourjo/liliput/refs/heads/master/images/flowchart_03_expand_link.svg" /></p>

<h2 id="learnings">Learnings</h2>
<ul>
  <li>Amazon Cognito integration with Google OAuth and managing login state in <a href="https://github.com/mourjo/liliput/blob/master/src/main/resources/index.html">a single-page Javascript</a> between the OAuth dance</li>
  <li>The authorizer middleware in API gateway with Lambda function</li>
  <li>Generating IAM policy document for giving access to only verified users</li>
  <li>Startup with Java based Lambda functions was quite slow</li>
</ul>

<h2 id="whats-next-for-liliput">What’s next for Liliput</h2>
<ul>
  <li>Verification without source code change</li>
  <li>Disable link expansions explicitly</li>
  <li>Switching between multiple Google accounts</li>
  <li>Searching for links shortened</li>
</ul>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[Inspiration is hard to come by in many fields – but not so in software engineering. When I attended a course on AWS offerings last year, I decided to try it out myself. With a little bit of hard work, the result was a serverless URL shortener built on AWS called shortener called Liliput.]]></summary></entry><entry><title type="html">I spoke at Techcamp Hamburg</title><link href="https://mourjo.me/blog/musings/2025/07/08/property-based-tests-to-preserve-invariants-talk-at-techcamp-hamburg/" rel="alternate" type="text/html" title="I spoke at Techcamp Hamburg" /><published>2025-07-08T21:51:06+02:00</published><updated>2025-07-08T21:51:06+02:00</updated><id>https://mourjo.me/blog/musings/2025/07/08/property-based-tests-to-preserve-invariants-talk-at-techcamp-hamburg</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/07/08/property-based-tests-to-preserve-invariants-talk-at-techcamp-hamburg/"><![CDATA[<p>“Accidental quality” — that’s what I call the hidden trap in traditional example-based tests.</p>

<p>In my recent talk at Techcamp Hamburg, I spoke about how enumerating test cases creates a dangerous illusion of safety, while the real bugs hide in the blindspots we never think to check.</p>

<p>Property-based testing flips the narrative: it searches for bugs instead of just testing for them. If you have 1000 users and you are writing 50 handpicked test cases - are you sure you are not missing any particular use-case?</p>

<iframe width="100%" height="669" src="https://www.youtube.com/embed/5tCzdkPPX_s" title="Property Based Testing to Preseve Invariants - Fachtalk mit Mourjo Sen beim techCamp" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[“Accidental quality” — that’s what I call the hidden trap in traditional example-based tests. In my recent talk at Techcamp Hamburg, I spoke about how enumerating test cases creates a dangerous illusion of safety, while the real bugs hide in the blindspots we never think to check. Property-based testing flips the narrative: it searches for bugs instead of just testing for them. If you have 1000 users and you are writing 50 handpicked test cases - are you sure you are not missing any particular use-case?]]></summary></entry><entry><title type="html">Winning the InfoQ Writing Contest</title><link href="https://mourjo.me/blog/musings/2025/05/30/i-won-2025-infoq-writing-contest/" rel="alternate" type="text/html" title="Winning the InfoQ Writing Contest" /><published>2025-05-30T22:36:22+02:00</published><updated>2025-05-30T22:36:22+02:00</updated><id>https://mourjo.me/blog/musings/2025/05/30/i-won-2025-infoq-writing-contest</id><content type="html" xml:base="https://mourjo.me/blog/musings/2025/05/30/i-won-2025-infoq-writing-contest/"><![CDATA[<p>Back in 2010, I decided to switch from an English Literature degree to a Computer Science Bachelor’s degree – but I never lost touch with writing and today I am still pinching myself to realize that I won the second prize for InfoQ’s writing contest in 2025!</p>

<p><img src="/blog/images/2025-05-30-i-won-2025-infoq-writing-contest-1.jpeg" style="border: 2px solid black;" /></p>

<p>Check out <a href="https://www.infoq.com/articles/flow-metrics-microservices/">the article here</a> and the announcement of the winners on LinkedIn:</p>

<iframe src="https://www.linkedin.com/embed/feed/update/urn:li:share:7334265349820866561" height="700" width="100%" frameborder="0" allowfullscreen="" title="Embedded post" style="border: 2px solid black;"></iframe>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="musings" /><summary type="html"><![CDATA[Back in 2010, I decided to switch from an English Literature degree to a Computer Science Bachelor’s degree – but I never lost touch with writing and today I am still pinching myself to realize that I won the second prize for InfoQ’s writing contest in 2025!]]></summary></entry><entry><title type="html">Quick Meetings: Why You Need Property Based Tests</title><link href="https://mourjo.me/blog/tech/2025/05/25/quick-meetings-why-you-need-property-based-tests/" rel="alternate" type="text/html" title="Quick Meetings: Why You Need Property Based Tests" /><published>2025-05-25T09:04:24+02:00</published><updated>2025-05-25T09:04:24+02:00</updated><id>https://mourjo.me/blog/tech/2025/05/25/quick-meetings-why-you-need-property-based-tests</id><content type="html" xml:base="https://mourjo.me/blog/tech/2025/05/25/quick-meetings-why-you-need-property-based-tests/"><![CDATA[<p>Today I am open-sourcing my <a href="https://github.com/mourjo/quick-meetings">quick-meetings</a> repository. It has one aim - to show the importance of property based testing (or generative testing).</p>

<p>At its core, this project allows you to create meetings. It is a very simple application. What could possibly go wrong?</p>

<p><img src="/blog/images/2025-05-25-quick-meetings-why-you-need-property-based-tests-1.png" alt="Image Description" /></p>

<h2 id="bug-1-non-json-response-body">Bug 1: Non-JSON response body</h2>
<p>If the accept header is something like <code>text/html</code>, Spring will try to return an HTML page, which is not what we want from an API - causing errors like this for the client:</p>
<pre><code>com.fasterxml.jackson.core.JsonParseException:
  Unexpected character ('&lt;' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
  at [Source: (String)"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Whitelabel Error Page&lt;/h1&gt;&lt;p&gt;This application has no explicit mapping for /error, so you are seeing this as a fallback.&lt;/p&gt;&lt;div id='created'&gt;Sat May 24 14:11:11 CEST 2025&lt;/div&gt;&lt;div&gt;There was an unexpected error (type=Method Not Allowed, status=405).&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;"; line: 1, column: 1]
</code></pre>

<p>Check out this <a href="https://github.com/mourjo/quick-meetings/tree/demo-1-server-never-returns-5xx">branch</a> on how this was detected with property based testing.</p>

<h2 id="bug-2-nullpointers-and-5xx">Bug 2: NullPointers and 5xx</h2>
<p>Your code should never return a 5xx for any kind of request - a catch-all response code like 5xx should only be reserved for infrastructure issues what you genuinely cannot predict.</p>

<p>But if you send a payload like this, the response will be – quite sadly – a 5xx:</p>

<pre><code>curl -X 'POST' 'http://localhost:9981/meeting' -s \
  -H 'Content-Type: application/json' \
  -d '{
  "userId": 260754,
  "name": "Bobby'\''s meeting"
}' | jq .

{
  "timestamp": "2025-05-25T07:14:37.890+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "trace": "java.lang.NullPointerException: Cannot invoke \"me.mourjo.quickmeetings.web.dto.MeetingDuration.from()\" because..."
}
</code></pre>

<p>Check out this <a href="https://github.com/mourjo/quick-meetings/tree/demo-1-server-never-returns-5xx">branch</a> on how property based tests found and resolved this bug.</p>

<h2 id="bug-3-gap-in-daylight-savings">Bug 3: Gap in Daylight Savings</h2>
<p>In the following snippet, a meeting creation request specifies the start-time at <strong>02:34</strong> and end-time at <strong>03:04</strong>, the error thrown says the start time is after the end time.</p>

<pre><code>Original Sample
---------------
  meetingArgs:
    MeetingArgs[fromDate=2025-03-30, fromTime=02:34:31, toDate=2025-03-30, toTime=03:04:31, timezone=Europe/Amsterdam]

  Original Error
  --------------
  java.lang.AssertionError:
    Expecting actual:
      "{"message":"Meeting cannot start (2025-03-30T03:34:31+02:00[Europe/Amsterdam]) after its end time (2025-03-30T03:04:31+02:00[Europe/Amsterdam])"}"
    to contain at least one of the following elements:
      ["Meeting created"]
    but none were found
</code></pre>

<p>This happens when the start or end time falls in a local timezone which is a “gap” due to daylight savings. On 30 March 2025, at <code>02:00:00</code> clocks in France were turned forward 1 hour to <code>03:00:00</code> - the time between 2 and 3 does not exist and is a gap.</p>

<p>This is caught again by property-based tests in <a href="https://github.com/mourjo/quick-meetings/tree/demo-2-invalid-date-range">this branch</a>.</p>

<h2 id="bug-4-query-to-check-overlapping-meetings">Bug 4: Query to check overlapping meetings</h2>

<p>The following query has a bug - it is quite hard to catch it at first glance (<code>$1</code> and <code>$2</code> are placeholders for the start and end time of a new meeting being created):</p>

<pre><code class="language-sql">SELECT *
FROM
meetings existing_meeting JOIN user_meetings um ON existing_meeting.id = um.meeting_id
WHERE (
  (existing_meeting.from_ts &lt;= $1 AND existing_meeting.to_ts &gt;= $1)
  OR
  (existing_meeting.from_ts &lt;= $2 AND existing_meeting.to_ts &gt;= $2)
)
</code></pre>

<p>Property based tests in <a href="https://github.com/mourjo/quick-meetings/tree/demo-3-meeting-creation-scenarios">this branch</a> reports the minimal case when this happens. This test fails when the second meeting is overlapping with the first but only if the second meeting <strong>starts before</strong>and <strong>ends after</strong> the first - note how the original sample finds a larger time overlap but the shrunk example finds the smallest failing case:</p>

<pre><code>Shrunk Sample (130 steps)
-------------------------
  meeting1Start: 2025-01-01T10:00:01
  meeting1DurationMins: 1
  meeting2Start: 2025-01-01T10:00
  meeting2DurationMins: 2

Original Sample
---------------
  meeting1Start: 2025-01-01T20:09:29
  meeting1DurationMins: 6
  meeting2Start: 2025-01-01T19:55:33
  meeting2DurationMins: 49
</code></pre>

<h2 id="bug-5-accepting-a-meeting-causes-an-overlap">Bug 5: Accepting a meeting causes an overlap</h2>

<p>In the previous bug, the fix ensured that the query to check overlapping meeting is correct but it is still possible to have overlapping meetings - as caught by property based tests in <a href="https://github.com/mourjo/quick-meetings/tree/demo-4-meeting-acceptations">this branch</a>.</p>

<p>Complex interleaving of user interactions – creating meetings, inviting others to meetings and accepting meeting invites – breaks the invariant that no person can be in two meetings at the same time. But which sequence of actions?</p>

<p><a href="https://github.com/mourjo/quick-meetings/tree/demo-4-meeting-acceptations">Property based tests</a> find minimal subset of operations that trigger the failure:</p>
<ul>
  <li>Alice and Charlie create overlapping meetings - this is okay because our invariant is no person can be at the same meeting, but if the attendees are different (here Alice and Charlie), it is perfectly fine</li>
  <li>It is also not a problem when Charlie is invited to Alice’s meeting</li>
  <li>It becomes a problem when Charlie accepts that meeting - because it overlaps with the meeting he created</li>
  <li>This is the minimal set of operations that shows the bug</li>
</ul>

<pre><code>OperationsGenTests.noOperationCausesAnOverlap:63 Invariant failed after the following actions: [
    Inputs{action=CREATE, user=alice, from=2025-06-09T10:21Z, to=2025-06-09T10:22Z}
    Inputs{action=INVITE, user=charlie, meetingIdx=0}
    Inputs{action=CREATE, user=charlie, from=2025-06-09T10:21Z, to=2025-06-09T10:22Z}
    Inputs{action=ACCEPT, user=charlie, meetingIdx=0}
]
</code></pre>

<p>But there are other more nuanced cases like this which sometimes get reported as well:</p>
<ul>
  <li>Charlie creates a meeting 0</li>
  <li>Bob is invited to meeting 0</li>
  <li>Alice creates a meeting 1 (overlapping with meeting 0)</li>
  <li>Bob is invited to meeting 1</li>
  <li>Bob accepts the invitation to meeting 0 (still no overlap)</li>
  <li>Bob accepts the invitation to meeting 1 (this causes an overlap for Bob)</li>
</ul>

<pre><code>OperationsGenTests.noOperationCausesAnOverlap:67 Invariant failed after the following actions: [
    Inputs{action=CREATE, user=charlie, from=2025-06-09T10:21Z, to=2025-06-09T10:22Z}
    Inputs{action=INVITE, user=bob, meetingIdx=0}
    Inputs{action=CREATE, user=alice, from=2025-06-09T10:21Z, to=2025-06-09T10:22Z}
    Inputs{action=INVITE, user=bob, meetingIdx=0}
    Inputs{action=ACCEPT, user=bob, meetingIdx=0}
    Inputs{action=ACCEPT, user=bob, meetingIdx=1}
]
</code></pre>

<h2 id="bug-6-empty-meetings">Bug 6: Empty meetings</h2>
<p>Once we have a way to test interleaving of operations, it is easy to extend that same framework - for example to preserve the invariant of no meeting should be empty.</p>

<p>While this seems like a gross miss, when we work on individual features, it is difficult to keep track of the larger product’s vision or expectations. This branch is the same - once we allow people to reject meeting invites, this bug manifests itself.</p>

<p>The minimal set of operations that fail is simple - this is provided by the test output as well:</p>
<ul>
  <li>Alice creates a meeting</li>
  <li>Alice rejects that same meeting</li>
</ul>

<pre><code>OperationsGenTests.noOperationCausesEmptyMeetings:70 Invariant failed after the following actions: [
    Inputs{action=CREATE, user=alice, from=2025-06-09T10:21Z, to=2025-06-09T10:22Z}
    Inputs{action=REJECT, user=alice, meetingIdx=0}
]
</code></pre>

<p>Check <a href="https://github.com/mourjo/quick-meetings/tree/demo-5-empty-meetings">this branch</a> for the details.</p>

<h2 id="conclusion">Conclusion</h2>
<p>Testing invariants is different from example based tests. It requires you to think of what the product you are building <em>should</em> do instead of <em>how</em> it should do it. As we saw above, it is very hard to both build a new feature and ensure there are no obscure bugs (like daylight savings) and that it performs as expected in the face of complex interleaving of actions (like accepting meetings which overlap with existing meetings).</p>

<p>Property based testing encourages you to think of your code as a black box of logic - to test its limits with permutations of inputs (both user input and state) - which is simply not possible to enumerate manually.</p>

<p>If you like this topic, I am going to be speaking at TechCamp in Hamburg in June - hope to see you there!</p>

<p><img src="/blog/images/2025-05-25-quick-meetings-why-you-need-property-based-tests-2.jpeg" alt="Image Description" /></p>]]></content><author><name>{&quot;twitter&quot; =&gt; &quot;mourjo_sen&quot;}</name></author><category term="tech" /><summary type="html"><![CDATA[Today I am open-sourcing my quick-meetings repository. It has one aim - to show the importance of property based testing (or generative testing).]]></summary></entry></feed>