<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ppl on Daffa Abhipraya</title><link>https://blog.abhipraya.dev/tags/ppl/</link><description>Recent content in Ppl on Daffa Abhipraya</description><generator>Hugo</generator><language>en-us</language><copyright>© Daffa Abhipraya</copyright><lastBuildDate>Wed, 15 Apr 2026 00:00:00 +0700</lastBuildDate><atom:link href="https://blog.abhipraya.dev/tags/ppl/index.xml" rel="self" type="application/rss+xml"/><item><title>PPL: AI at Different Cognitive Distances [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-ai-literacy/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-ai-literacy/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks where AI was the primary productivity multiplier across very different task shapes. Week 1 leaned on iterative CI debugging (ten MRs of &amp;ldquo;run pipeline, read failure, ask AI, apply fix&amp;rdquo;) and integration-test infrastructure design. Week 2 leaned on bulk test generation (400+ mutation-killing assertions), bash-with-tricky-primitives design (a &lt;code>flock&lt;/code>-based slot allocator), and cross-agent documentation research. The common thread across all six patterns: AI is best when the task is a known pattern applied to new context, and weakest when the task is about how primitives interact in a specific environment.&lt;/p></description></item><item><title>PPL: Code Review at Two Altitudes [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-code-review/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-code-review/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks of code review on teammate MRs. Week 1 (2-8 Apr) covered three substantial MRs: hl&amp;rsquo;s CSV import modal (!155), hl&amp;rsquo;s settings management system (!128), and qenthm&amp;rsquo;s ML risk scoring engine (!166) — 44 review points including 7 P0s. Week 2 (9-15 Apr) covered fadhliraihan&amp;rsquo;s AR Staff read-only client access (!171) and fernanda.nadhiftya&amp;rsquo;s communication history UI (!177) — 6 review points including 2 P0s caught through manual testing that static reading missed. Fifty review points total, nine P0s, reviews posted as threaded discussions on every MR.&lt;/p></description></item><item><title>PPL: Mutation Testing, From Setup to Score [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-tdd/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-tdd/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks on mutation testing across the SIRA codebase. The first week wired &lt;a href="https://github.com/boxed/mutmut">mutmut&lt;/a> (Python) and &lt;a href="https://stryker-mutator.io/">Stryker&lt;/a> (TypeScript) into the pipeline and uncovered the uncomfortable truth: 91% line coverage on the services layer translated to a mutation score just above zero in places. The second week closed that gap by writing 400+ targeted tests across two rounds, driving the API mutation score from ~66% to 80.3%. This blog tells the full arc.&lt;/p></description></item><item><title>PPL: Quality as a Feedback Loop [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-code-quality/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-code-quality/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks of quality infrastructure: wiring in the tools that measure test quality (week 1), then shortening the feedback loop so that signal actually influences the code being written (week 2). Starting state: 91% line coverage, no mutation testing, integration test coverage missing from SonarQube. Ending state: combined unit + integration coverage in SonarQube, mutmut + Stryker running per-MR with results in the CI comment, API mutation score 80.3%.&lt;/p></description></item><item><title>PPL: Security, From App Code to Supply Chain [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-security/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-security/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks of security work that span the whole stack. Week 1 (2-8 Apr) closed two OWASP A01 (Broken Access Control) gaps — one in our own code, one caught during a teammate&amp;rsquo;s code review — and added Sentry observability to Celery tasks so background job failures stop disappearing silently. Week 2 (9-15 Apr) patched a high-severity axios SSRF (GHSA-3p68-rc4w-qgx5) that &lt;code>pnpm audit&lt;/code> caught in a routine MR pipeline. The pattern across the two weeks: fix boundaries in our own code, and keep dependencies patched via automated audit.&lt;/p></description></item><item><title>PPL: Separation of Concerns, Feature Code to Infra [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-programming/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-programming/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks of work that ended up demonstrating the same principle at two very different scales. In the application code: isolating invoice number generation as a pure function (SIRA-123, MR !130) and composing authentication onto GET endpoints via FastAPI dependency injection (SIRA-82, MR !154). In the infrastructure: designing &lt;code>scripts/ci-supabase-slot.sh&lt;/code> (SIRA-274, MR !197) — a 110-line bash script that claims one of three parallel Supabase stacks and hands back an isolated environment to the caller. Different problem domains, same discipline: give each unit of code one reason to change.&lt;/p></description></item><item><title>PPL: Two Weeks of CI/Testing Discipline [Sprint 2, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w3-discipline/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w3-discipline/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two weeks (2 April through 15 April) produced &lt;strong>25 merged MRs&lt;/strong> and roughly 40 commits, overwhelmingly CI and testing infrastructure with a handful of features and a security upgrade layered in. The work forms one continuous arc: wire mutation testing into CI → make it work on MR pipelines → write tests that actually pass the quality bar → shard the Supabase stack so CI runs three jobs in parallel. Every MR was mergeable on its own and every walk-back is visible in the commit log, not hidden with force-pushes.&lt;/p></description></item><item><title>PPL: Self-Hosted Error Monitoring with Custom Instrumentation</title><link>https://blog.abhipraya.dev/ppl/part-a/monitoring/</link><pubDate>Sat, 11 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/monitoring/</guid><description>&lt;p>Error monitoring is one of those things that feels optional until the first production bug slips through unnoticed. A user reports &amp;ldquo;the page is broken,&amp;rdquo; you check the server, everything looks fine, and three hours later you discover a background task has been silently failing since the last deploy. This blog covers how we built monitoring that catches those failures before users do.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> Our project is hosted on an internal GitLab instance, so we use the term &lt;strong>MR (Merge Request)&lt;/strong> throughout this blog. If you&amp;rsquo;re coming from GitHub, MRs are the equivalent of &lt;strong>Pull Requests (PRs)&lt;/strong>.&lt;/p></description></item><item><title>PPL: Communication and Knowledge Sharing [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w2-communication/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w2-communication/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2 Week 2 (April 3 to 9) communication work centered on three substantial code reviews posted as GitLab discussion threads, merge coordination for 13+ teammate MRs, and Linear ticket management. The reviews caught several critical bugs that would have reached production, including a broken SQL join and an auth bypass.&lt;/p>
&lt;hr>
&lt;h2 id="1-threaded-mr-reviews-with-severity-grading">
 &lt;a class="anchor" href="#1-threaded-mr-reviews-with-severity-grading" data-anchor="1-threaded-mr-reviews-with-severity-grading" aria-hidden="true">#&lt;/a>
 1. Threaded MR Reviews with Severity Grading
&lt;/h2>
&lt;p>Three MRs were reviewed this week, all following the same structured approach: positive callouts first, then issues grouped by severity (P0 blockers through P3 nitpicks), with code fix suggestions for every issue.&lt;/p></description></item><item><title>PPL: New Learnings Applied to SIRA [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w2-aptitude/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w2-aptitude/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2 Week 2 (April 3 to 9) was dominated by a mutation testing sprint: setting up mutmut and Stryker in CI, building integration test infrastructure from scratch, and writing mutation-killing tests. The week also delivered three application features and a meta-level improvement to AI workflow. Each area required learning at least one technology or concept not covered in standard Fasilkom coursework.&lt;/p>
&lt;hr>
&lt;h2 id="1-mutation-testing-with-mutmut-python">
 &lt;a class="anchor" href="#1-mutation-testing-with-mutmut-python" data-anchor="1-mutation-testing-with-mutmut-python" aria-hidden="true">#&lt;/a>
 1. Mutation Testing with mutmut (Python)
&lt;/h2>
&lt;p>Mutation testing is a technique where a tool injects small faults (&amp;ldquo;mutants&amp;rdquo;) into your source code (flipping operators, changing constants, removing lines) and checks whether your test suite catches each fault. If a test fails, the mutant is &amp;ldquo;killed.&amp;rdquo; If no test fails, the mutant &amp;ldquo;survives,&amp;rdquo; meaning your tests have a blind spot.&lt;/p></description></item><item><title>PPL: Quality Gates That Actually Block Bad Code</title><link>https://blog.abhipraya.dev/ppl/part-a/qa/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/qa/</guid><description>&lt;p>Quality tools are useless if they are not enforced. Our project had SonarQube analyzing every commit, schema fuzzing running against our API, and load tests measuring latency, but every single one of them was set to &lt;code>allow_failure: true&lt;/code>. Violations were silently ignored, real bugs slipped through, and nobody noticed because the pipeline was always green. This blog covers how we turned those advisory checks into blocking gates, built automated CI reporting so reviewers could actually see the results, and watched the tools catch a JWT vulnerability, a production crash, and 31 code quality violations that had been accumulating for weeks.&lt;/p></description></item><item><title>PPL: SOLID Principles in a FastAPI Invoice System</title><link>https://blog.abhipraya.dev/ppl/part-a/solid/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/solid/</guid><description>&lt;p>SIRA (Smart Invoice Reminder AI) is a system that automates invoice collection reminders. It monitors payment status, scores client risk using a weighted formula, and sends personalized reminders by email or messaging. The backend is built with FastAPI, Supabase (Postgres via REST), Celery for background jobs, and Redis as the message broker.&lt;/p>
&lt;p>This blog walks through how SOLID principles shaped the architecture at three levels: the overall layer structure, service-level design patterns, and individual function design. The goal is not to explain what SOLID stands for (there are plenty of articles for that), but to show what it looks like in production code and why certain design choices were made over simpler alternatives.&lt;/p></description></item><item><title>PPL: When 91% Test Coverage Means Nothing</title><link>https://blog.abhipraya.dev/ppl/part-a/tdd/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/tdd/</guid><description>&lt;p>We had 91% line coverage and felt good about it. Then we ran mutation testing and scored 0%. Every line of our service layer was executed by tests, but almost nothing was actually verified. This is the story of how we discovered the gap between &amp;ldquo;code was run&amp;rdquo; and &amp;ldquo;code was checked,&amp;rdquo; and what we changed to close it.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> Our project is hosted on an internal GitLab instance, so we use the term &lt;strong>MR (Merge Request)&lt;/strong> throughout this blog. If you&amp;rsquo;re coming from GitHub, MRs are the equivalent of &lt;strong>Pull Requests (PRs)&lt;/strong>.&lt;/p></description></item><item><title>PPL: Work Ethic and Commitment [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w2-commitment/</link><pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w2-commitment/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2, Week 2 (April 3 to 9) output summary:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Metric&lt;/th>
 &lt;th>Count&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>MRs authored and merged&lt;/td>
 &lt;td>19&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs reviewed (in-depth, threaded)&lt;/td>
 &lt;td>3 (44 review points)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs merged as maintainer&lt;/td>
 &lt;td>13+ (from teammates)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Commits&lt;/td>
 &lt;td>50+&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Domains covered&lt;/td>
 &lt;td>5 (CI/CD, testing infrastructure, API features, documentation, infra)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="quantitative-output">
 &lt;a class="anchor" href="#quantitative-output" data-anchor="quantitative-output" aria-hidden="true">#&lt;/a>
 Quantitative Output
&lt;/h2>
&lt;h3 id="mrs-authored-and-merged">
 &lt;a class="anchor" href="#mrs-authored-and-merged" data-anchor="mrs-authored-and-merged" aria-hidden="true">#&lt;/a>
 MRs Authored and Merged
&lt;/h3>
&lt;p>19 MRs created and merged to &lt;code>main&lt;/code> this week, broken down by category:&lt;/p></description></item><item><title>PPL: AI Literacy [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-ai-literacy/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-ai-literacy/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>With 19 MRs landing this week across CI scripting, application features, and infra fixes, AI assistance was involved throughout. The most interesting usage this week was not code generation but rather iterative verification: describe what the output should look like, generate, run against real CI output, observe differences, fix, repeat.&lt;/p>
&lt;hr>
&lt;h2 id="ai-for-complex-bash-scripting">
 &lt;a class="anchor" href="#ai-for-complex-bash-scripting" data-anchor="ai-for-complex-bash-scripting" aria-hidden="true">#&lt;/a>
 AI for Complex Bash Scripting
&lt;/h2>
&lt;p>The CI report scripts (&lt;code>scripts/ci-report.sh&lt;/code>) grew from 0 to 356 lines in MR !126 alone, and another 111 lines across !129, !132, and !140. Writing bash that parses CI job output, formats it as GitLab-flavored markdown, and posts it via the GitLab API is exactly the kind of code that AI handles well — repetitive structure, lots of string manipulation, easy to verify.&lt;/p></description></item><item><title>PPL: Code Quality [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-code-quality/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-code-quality/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I overhauled the CI quality feedback loop with four related MRs. Before this week, quality checks existed but results had to be hunted down manually in GitLab&amp;rsquo;s CI panel. After this week, every MR automatically receives quality reports as inline comments, and the SonarQube gate blocks merges that would lower the quality standard.&lt;/p>
&lt;hr>
&lt;h2 id="sonarqube-quality-gate-in-mr-comments">
 &lt;a class="anchor" href="#sonarqube-quality-gate-in-mr-comments" data-anchor="sonarqube-quality-gate-in-mr-comments" aria-hidden="true">#&lt;/a>
 SonarQube Quality Gate in MR Comments
&lt;/h2>
&lt;p>MR !132 (SIRA-231) added a &lt;code>sonar-scan&lt;/code> post-processing step that fetches the quality gate result from SonarQube and posts it directly as an MR comment via the GitLab API.&lt;/p></description></item><item><title>PPL: Communication and Knowledge Sharing [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w1-communication/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w1-communication/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2 Week 1 (Mar 24 to 30) communication focused on two detailed code reviews posted as GitLab discussion threads, merge coordination for the team, and direct code contributions to address review findings.&lt;/p>
&lt;hr>
&lt;h2 id="1-threaded-mr-reviews-with-severity-grading">
 &lt;a class="anchor" href="#1-threaded-mr-reviews-with-severity-grading" data-anchor="1-threaded-mr-reviews-with-severity-grading" aria-hidden="true">#&lt;/a>
 1. Threaded MR Reviews with Severity Grading
&lt;/h2>
&lt;p>This sprint I shifted to posting reviews as &lt;strong>GitLab discussion threads&lt;/strong> rather than plain comments. Discussion threads can be individually resolved by the MR owner after addressing each issue, making it clear which feedback has been handled and which is still open.&lt;/p></description></item><item><title>PPL: Development Discipline [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-discipline/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-discipline/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This was the most productive week of the project so far. 19 merge requests were opened and merged across five work streams: a major CI/CD overhaul, three new application features, web/UX improvements, email integration, and infrastructure fixes for dev workspaces.&lt;/p>
&lt;hr>
&lt;h2 id="cicd-sprint--8-mrs-in-the-pipeline">
 &lt;a class="anchor" href="#cicd-sprint--8-mrs-in-the-pipeline" data-anchor="cicd-sprint--8-mrs-in-the-pipeline" aria-hidden="true">#&lt;/a>
 CI/CD Sprint — 8 MRs in the Pipeline
&lt;/h2>
&lt;p>Most of the week&amp;rsquo;s focus was a series of CI improvements aimed at making quality feedback automatic and visible on every MR.&lt;/p></description></item><item><title>PPL: New Learnings Applied to SIRA [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w1-aptitude/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w1-aptitude/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2 Week 1 (Mar 24 to 30) combined feature development with infrastructure hardening. The week produced two full-stack features (multi-device session management and email integration), a developer tooling improvement (Superset workspace isolation), and iterative CI quality gate improvements. Each required learning at least one technology from scratch.&lt;/p>
&lt;hr>
&lt;h2 id="1-multi-device-session-management">
 &lt;a class="anchor" href="#1-multi-device-session-management" data-anchor="1-multi-device-session-management" aria-hidden="true">#&lt;/a>
 1. Multi-Device Session Management
&lt;/h2>
&lt;p>SIRA needed per-device session tracking: users should see which devices are logged in, and admins should be able to revoke sessions remotely. This isn&amp;rsquo;t a standard Supabase Auth feature; it required a custom session layer on top of the existing JWT auth.&lt;/p></description></item><item><title>PPL: Programming [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-programming/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-programming/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two major features this week, email integration (SIRA-160) and session management (SIRA-214), both followed the SOLID principles that have been established in the codebase. The email feature in particular offered a good opportunity to show SRP in action: sending an email and rendering an email template are genuinely different responsibilities, and keeping them separate made both easier to test.&lt;/p>
&lt;hr>
&lt;h2 id="email-integration-separating-transport-from-rendering">
 &lt;a class="anchor" href="#email-integration-separating-transport-from-rendering" data-anchor="email-integration-separating-transport-from-rendering" aria-hidden="true">#&lt;/a>
 Email Integration: Separating Transport from Rendering
&lt;/h2>
&lt;p>SIRA-160 (MR !108) introduced three distinct services:&lt;/p></description></item><item><title>PPL: Security [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-security/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-security/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Two security-relevant features merged this week that directly address OWASP Top 10 vulnerabilities: blocking inactive accounts from authenticating (SIRA-215) and enforcing session limits with proper invalidation (SIRA-214). SAST results are now also surfaced automatically on every MR via the CI report system.&lt;/p>
&lt;hr>
&lt;h2 id="owasp-a07-authentication-failures">
 &lt;a class="anchor" href="#owasp-a07-authentication-failures" data-anchor="owasp-a07-authentication-failures" aria-hidden="true">#&lt;/a>
 OWASP A07: Authentication Failures
&lt;/h2>
&lt;p>OWASP A07 covers broken authentication, including cases where deactivated or suspended accounts can still access the system. Before SIRA-215 (MR !104), deactivating a user in the admin panel only prevented login via the UI — a valid JWT issued before deactivation still passed every auth check.&lt;/p></description></item><item><title>PPL: TDD [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-tdd/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-tdd/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Three new features landed this week that each required TDD from scratch: multi-device session management (SIRA-214), invoice cancellation (SIRA-125), and blocking inactive accounts (SIRA-215). All three followed the red-green cycle and included mock isolation for external dependencies.&lt;/p>
&lt;hr>
&lt;h2 id="session-management-testing-stateful-logic-with-mocks">
 &lt;a class="anchor" href="#session-management-testing-stateful-logic-with-mocks" data-anchor="session-management-testing-stateful-logic-with-mocks" aria-hidden="true">#&lt;/a>
 Session Management: Testing Stateful Logic with Mocks
&lt;/h2>
&lt;p>SIRA-214 (MR !120) introduced &lt;code>SessionService&lt;/code>, which manages active sessions per user with a device limit. The service has non-trivial state: upsert if session already exists, kick oldest if at capacity, validate ownership before revoking.&lt;/p></description></item><item><title>PPL: Team Dev Management [Sprint 2, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w2-code-review/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w2-code-review/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Three substantial MRs reviewed this week: Bertrand&amp;rsquo;s cancel invoice feature (SIRA-125), Erdafa&amp;rsquo;s blue/green deployment system (SIRA-134 to SIRA-141), and Rifqi&amp;rsquo;s invoice grouping by status (SIRA-132). Total review points: 29 across P0/P1/P2/P3 severity.&lt;/p>
&lt;hr>
&lt;h2 id="mr-118--cancel-invoice-feature-bertrand">
 &lt;a class="anchor" href="#mr-118--cancel-invoice-feature-bertrand" data-anchor="mr-118--cancel-invoice-feature-bertrand" aria-hidden="true">#&lt;/a>
 MR !118 — Cancel Invoice Feature (Bertrand)
&lt;/h2>
&lt;p>The implementation was solid on the happy path: TDD discipline visible from commit history, clean Router-Service-DB layering, rollback migration included. Review focused on API-level guard gaps that the frontend UI hid but the API did not enforce.&lt;/p></description></item><item><title>PPL: Work Ethic and Commitment [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-c/s2w1-commitment/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s2w1-commitment/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 2, Week 1 (Mar 24 to Apr 2) output summary:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Metric&lt;/th>
 &lt;th>Count&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>MRs authored and merged&lt;/td>
 &lt;td>13&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs reviewed (in-depth, threaded)&lt;/td>
 &lt;td>2&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs merged as maintainer&lt;/td>
 &lt;td>6+ (from teammates)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Commits (across all branches)&lt;/td>
 &lt;td>100+&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Domains covered&lt;/td>
 &lt;td>9 (CI/CD, infra, backend, frontend, database, DevOps tooling, email, SonarQube, Supabase staging)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Linear tickets created&lt;/td>
 &lt;td>10 (2 CI tickets + 8 BDD subtasks)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="quantitative-output">
 &lt;a class="anchor" href="#quantitative-output" data-anchor="quantitative-output" aria-hidden="true">#&lt;/a>
 Quantitative Output
&lt;/h2>
&lt;h3 id="mrs-authored-and-merged">
 &lt;a class="anchor" href="#mrs-authored-and-merged" data-anchor="mrs-authored-and-merged" aria-hidden="true">#&lt;/a>
 MRs Authored and Merged
&lt;/h3>
&lt;p>8 MRs created and merged to &lt;code>main&lt;/code> this week:&lt;/p></description></item><item><title>PPL: AI-Assisted Sprint Planning [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-ai-literacy/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-ai-literacy/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I used Claude Code (Anthropic&amp;rsquo;s CLI agent) as my primary development partner for Sprint 2 planning, feature brainstorming, code implementation, and CI debugging. The session produced 20 PBIs, 64 subtasks, 3 service file changes, and a merged MR, all in one sitting.&lt;/p>
&lt;h2 id="ai-for-sprint-planning">
 &lt;a class="anchor" href="#ai-for-sprint-planning" data-anchor="ai-for-sprint-planning" aria-hidden="true">#&lt;/a>
 AI for Sprint Planning
&lt;/h2>
&lt;h3 id="feature-gap-analysis">
 &lt;a class="anchor" href="#feature-gap-analysis" data-anchor="feature-gap-analysis" aria-hidden="true">#&lt;/a>
 Feature Gap Analysis
&lt;/h3>
&lt;p>I asked Claude to explore the entire codebase (frontend + backend + infra) and catalog what&amp;rsquo;s implemented vs missing. It launched parallel exploration agents that traced through routes, services, workers, and the database schema, producing a comprehensive feature inventory.&lt;/p></description></item><item><title>PPL: Beyond Unit Tests [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-tdd/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-tdd/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I pushed our testing strategy well beyond standard unit tests. The project already had 433 backend and 200 frontend tests with 91% line coverage, but I wanted to answer a harder question: &lt;strong>do our tests actually catch bugs, or do they just execute code?&lt;/strong>&lt;/p>
&lt;p>I added four advanced testing approaches: property-based testing (Hypothesis + fast-check), behavioral testing (pytest-bdd with Gherkin), mutation testing (mutmut + Stryker), and test isolation verification (pytest-randomly). The results were eye-opening.&lt;/p></description></item><item><title>PPL: Fixing a JWT Vulnerability [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-security/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-security/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I addressed a critical security vulnerability flagged by SonarQube and hardened the CI pipeline so security issues block merges instead of being silently reported.&lt;/p>
&lt;h2 id="the-jwt-vulnerability-sonarqube-s5659">
 &lt;a class="anchor" href="#the-jwt-vulnerability-sonarqube-s5659" data-anchor="the-jwt-vulnerability-sonarqube-s5659" aria-hidden="true">#&lt;/a>
 The JWT Vulnerability (SonarQube S5659)
&lt;/h2>
&lt;p>SonarQube flagged &lt;code>dependencies.py:34&lt;/code> as a &lt;strong>CRITICAL vulnerability&lt;/strong>: &lt;code>jwt.get_unverified_header()&lt;/code> reads the JWT header without verifying the signature. This enables the &lt;strong>alg:none attack&lt;/strong>, where an attacker crafts a token with &lt;code>&amp;quot;alg&amp;quot;: &amp;quot;none&amp;quot;&lt;/code> in the header, causing the library to skip signature verification entirely.&lt;/p></description></item><item><title>PPL: From 31 Violations to Zero [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-code-quality/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-code-quality/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I enforced strict quality gates across the entire CI pipeline. The project previously had &lt;code>allow_failure: true&lt;/code> on SonarQube and security scans, meaning violations were reported but never blocked merges. I changed that.&lt;/p>
&lt;h2 id="sonarqube-31-violations--0">
 &lt;a class="anchor" href="#sonarqube-31-violations--0" data-anchor="sonarqube-31-violations--0" aria-hidden="true">#&lt;/a>
 SonarQube: 31 Violations → 0
&lt;/h2>
&lt;h3 id="the-violations">
 &lt;a class="anchor" href="#the-violations" data-anchor="the-violations" aria-hidden="true">#&lt;/a>
 The Violations
&lt;/h3>
&lt;p>SonarQube flagged 31 issues across the codebase:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>1 CRITICAL vulnerability&lt;/strong>: &lt;code>jwt.get_unverified_header()&lt;/code> reading JWT headers without signature verification&lt;/li>
&lt;li>&lt;strong>3 CRITICAL code smells&lt;/strong>: duplicated string literals, nested component definitions&lt;/li>
&lt;li>&lt;strong>27 other issues&lt;/strong>: unused variables, missing &lt;code>Readonly&amp;lt;&amp;gt;&lt;/code> on props, duplicate CSS blocks, array index keys&lt;/li>
&lt;/ul>
&lt;h3 id="the-fixes">
 &lt;a class="anchor" href="#the-fixes" data-anchor="the-fixes" aria-hidden="true">#&lt;/a>
 The Fixes
&lt;/h3>
&lt;p>&lt;strong>Backend&lt;/strong> (3 files): Refactored JWT decode to try HS256 first and fall back to asymmetric on &lt;code>DecodeError&lt;/code>, eliminating the unverified header call entirely. Extracted duplicated literals to constants.&lt;/p></description></item><item><title>PPL: Service Layer Instrumentation [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-programming/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-programming/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I implemented custom Sentry span instrumentation across 3 key API services to track sub-operation latency in GlitchTip. The interesting part wasn&amp;rsquo;t the Sentry API itself; it was &lt;strong>where&lt;/strong> to place the instrumentation in our layered architecture, and how the existing SOLID patterns made the change trivially simple.&lt;/p>
&lt;h2 id="following-the-architecture-spans-in-services-not-routers">
 &lt;a class="anchor" href="#following-the-architecture-spans-in-services-not-routers" data-anchor="following-the-architecture-spans-in-services-not-routers" aria-hidden="true">#&lt;/a>
 Following the Architecture: Spans in Services, Not Routers
&lt;/h2>
&lt;p>SIRA follows a strict Router → Service → DB pattern. Routers handle HTTP concerns (auth, status codes). Services hold business logic. DB queries are isolated in &lt;code>db/queries/&lt;/code>.&lt;/p></description></item><item><title>PPL: Sprint Planning at Scale [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-discipline/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-discipline/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>As Team Lead, this week&amp;rsquo;s primary work was Sprint 2 planning. I conducted a full feature gap analysis of the app, brainstormed new features with stakeholder input, created detailed tickets in Linear, set up cross-PBI dependency chains, and rebalanced the sprint when it became overloaded.&lt;/p>
&lt;p>The result: &lt;strong>20 new PBIs with 64 subtasks&lt;/strong>, all with acceptance criteria, technical notes, and proper sprint/cycle assignments.&lt;/p></description></item><item><title>PPL: Ticket Quality as Code Review [Sprint 2, Week 1]</title><link>https://blog.abhipraya.dev/ppl/part-b/s2w1-code-review/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s2w1-code-review/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week&amp;rsquo;s review work had two dimensions: reviewing the &lt;strong>ticket board&lt;/strong> for sync issues (a form of &amp;ldquo;code review&amp;rdquo; for project management), and letting &lt;strong>SonarQube quality gates&lt;/strong> act as automated code review for the monitoring MR.&lt;/p>
&lt;h2 id="ticket-board-audit">
 &lt;a class="anchor" href="#ticket-board-audit" data-anchor="ticket-board-audit" aria-hidden="true">#&lt;/a>
 Ticket Board Audit
&lt;/h2>
&lt;p>After creating 64 new tickets, I audited all 150+ active tickets (Backlog, Todo, In Progress, In Review) for contradictions and stale data. This is essentially code review applied to project planning.&lt;/p></description></item><item><title>PPL: When 91% Test Coverage Means Nothing</title><link>https://blog.abhipraya.dev/ppl/part-a/tdd-and-qa/</link><pubDate>Sun, 15 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/tdd-and-qa/</guid><description>&lt;p>We had 91% line coverage and felt good about it. Then we ran mutation testing and scored 0%. Every line of our service layer was executed by tests; almost nothing was actually verified. This is the story of how six advanced testing tools exposed the gap between &amp;ldquo;code was run&amp;rdquo; and &amp;ldquo;code was checked,&amp;rdquo; and what that means for any team relying on coverage as a quality signal.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> Our project is hosted on an internal GitLab instance, so we use the term &lt;strong>MR (Merge Request)&lt;/strong> throughout this blog. If you&amp;rsquo;re coming from GitHub, MRs are the equivalent of &lt;strong>Pull Requests (PRs)&lt;/strong>.&lt;/p></description></item><item><title>PPL: AI Literacy [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-ai-literacy/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-ai-literacy/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week tested whether the AI development infrastructure built in Sprint 1 Weeks 1-2 actually pays off under heavy workload. With 14 MRs and 10 code reviews in one week, every tool in the AI stack was used extensively. I also updated the CLAUDE.md documentation based on new patterns discovered during development.&lt;/p>
&lt;h2 id="claudemd-as-living-documentation">
 &lt;a class="anchor" href="#claudemd-as-living-documentation" data-anchor="claudemd-as-living-documentation" aria-hidden="true">#&lt;/a>
 CLAUDE.md as Living Documentation
&lt;/h2>
&lt;p>The project&amp;rsquo;s AI instruction document (&lt;code>CLAUDE.md&lt;/code>) was updated this week to reflect new patterns encountered during development. Commit &lt;code>80b127b&lt;/code> added &lt;code>fullName&lt;/code> to the documented auth context values in &lt;code>apps/web/CLAUDE.md&lt;/code>:&lt;/p></description></item><item><title>PPL: Code Quality [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-code-quality/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-code-quality/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I extended the CI quality pipeline with two significant additions: a &lt;strong>migration dry-run validation job&lt;/strong> that catches invalid SQL before merge (MR !67), and ongoing enforcement of zero SonarQube issues across the codebase. The pre-commit hook stack also proved its value by catching issues during the heavy development push of 14 MRs.&lt;/p>
&lt;h2 id="new-ci-job-migration-dry-run-validation">
 &lt;a class="anchor" href="#new-ci-job-migration-dry-run-validation" data-anchor="new-ci-job-migration-dry-run-validation" aria-hidden="true">#&lt;/a>
 New CI Job: Migration Dry-Run Validation
&lt;/h2>
&lt;p>The most impactful quality improvement this week was adding &lt;code>migrate:check&lt;/code> to the CI pipeline (MR !67, SIRA-98). This job runs &lt;code>supabase db push --dry-run&lt;/code> on every MR pipeline, validating that migration files are syntactically correct and compatible with the remote database before the MR can be merged.&lt;/p></description></item><item><title>PPL: Code Review [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-code-review/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-code-review/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week I reviewed &lt;strong>10 teammates&amp;rsquo; merge requests&lt;/strong>, leaving substantive comments with code samples, performance suggestions, and architectural fixes. Three reviews included fix commits that I pushed directly to the teammate&amp;rsquo;s branch to unblock merging.&lt;/p>
&lt;h2 id="review-summary">
 &lt;a class="anchor" href="#review-summary" data-anchor="review-summary" aria-hidden="true">#&lt;/a>
 Review Summary
&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>MR&lt;/th>
 &lt;th>Author&lt;/th>
 &lt;th>Feature&lt;/th>
 &lt;th>Review Type&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>!75&lt;/td>
 &lt;td>soydoradesu&lt;/td>
 &lt;td>Client management pages&lt;/td>
 &lt;td>Duplicate code + missing field&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!72&lt;/td>
 &lt;td>hl&lt;/td>
 &lt;td>Dashboard stale data fix&lt;/td>
 &lt;td>TanStack Query invalidation gap&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!64&lt;/td>
 &lt;td>soydoradesu&lt;/td>
 &lt;td>Frontend foundation&lt;/td>
 &lt;td>Comprehensive review + fix commits&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!49&lt;/td>
 &lt;td>qenthm&lt;/td>
 &lt;td>Overdue detection worker&lt;/td>
 &lt;td>Consistency + naming review&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!45&lt;/td>
 &lt;td>froklax&lt;/td>
 &lt;td>Staff management&lt;/td>
 &lt;td>Auth/DB desync risk&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!46&lt;/td>
 &lt;td>adipppp&lt;/td>
 &lt;td>Auth deadlock fix&lt;/td>
 &lt;td>Correctness verification&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!50&lt;/td>
 &lt;td>dafandikri&lt;/td>
 &lt;td>Dev environment fix&lt;/td>
 &lt;td>Review + 4 fix commits pushed&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!40&lt;/td>
 &lt;td>soydoradesu&lt;/td>
 &lt;td>Frontend foundation&lt;/td>
 &lt;td>Cleanup commits pushed&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!52&lt;/td>
 &lt;td>adipppp&lt;/td>
 &lt;td>Payment seeder&lt;/td>
 &lt;td>FK resolution review&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!63&lt;/td>
 &lt;td>adipppp&lt;/td>
 &lt;td>JWT alg:none hardening&lt;/td>
 &lt;td>Security review&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="how-i-conduct-code-reviews">
 &lt;a class="anchor" href="#how-i-conduct-code-reviews" data-anchor="how-i-conduct-code-reviews" aria-hidden="true">#&lt;/a>
 How I Conduct Code Reviews
&lt;/h2>
&lt;p>Reviewing 10 MRs in a single week is only feasible because of a structured AI-assisted workflow. I use two Claude Code plugins depending on the MR type, then manually filter and post the findings.&lt;/p></description></item><item><title>PPL: Development Discipline [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-discipline/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-discipline/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>Sprint 1 Week 3 was the highest-output week of the project so far. I authored and merged &lt;strong>14 merge requests&lt;/strong> covering full-stack features, CI/CD pipeline improvements, production infrastructure fixes, and data integrity patches.&lt;/p>
&lt;h2 id="mr-summary">
 &lt;a class="anchor" href="#mr-summary" data-anchor="mr-summary" aria-hidden="true">#&lt;/a>
 MR Summary
&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>MR&lt;/th>
 &lt;th>Type&lt;/th>
 &lt;th>Title&lt;/th>
 &lt;th>Date&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>!20&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Layout, navigation &amp;amp; dashboard (full stack)&lt;/td>
 &lt;td>Mar 7&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!47&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Seed production admin and staff users with Nashta emails&lt;/td>
 &lt;td>Mar 7&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!12&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Invoice management (full stack)&lt;/td>
 &lt;td>Mar 8&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!54&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Add Payments link to sidebar navigation&lt;/td>
 &lt;td>Mar 8&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!66&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Rename out-of-order migration timestamps to unblock CI deploy&lt;/td>
 &lt;td>Mar 9&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!67&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Add migration dry-run validation to MR pipelines&lt;/td>
 &lt;td>Mar 9&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!68&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Add missing ENCRYPTION_KEY to deploy env generation&lt;/td>
 &lt;td>Mar 9&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!71&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Add proxy-headers to uvicorn for HTTPS redirect support&lt;/td>
 &lt;td>Mar 10&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!74&lt;/td>
 &lt;td>chore&lt;/td>
 &lt;td>Add Conductor workspace support&lt;/td>
 &lt;td>Mar 11&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!76&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Resolve invoice seeder subquery returning multiple rows&lt;/td>
 &lt;td>Mar 11&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!77&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Deduplicate invoice numbers in seed CSV&lt;/td>
 &lt;td>Mar 11&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!78&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Polish sidebar, header, profile edit, and invoices page consistency&lt;/td>
 &lt;td>Mar 11&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!79&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Remove sidebar header bottom border&lt;/td>
 &lt;td>Mar 11&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>!17&lt;/td>
 &lt;td>feat&lt;/td>
 &lt;td>Client management (full stack, merged by me)&lt;/td>
 &lt;td>Mar 9&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="breakdown-by-category">
 &lt;a class="anchor" href="#breakdown-by-category" data-anchor="breakdown-by-category" aria-hidden="true">#&lt;/a>
 Breakdown by category
&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Full-stack features:&lt;/strong> 4 MRs (!20, !12, !54, !17)&lt;/li>
&lt;li>&lt;strong>CI/CD pipeline:&lt;/strong> 3 MRs (!66, !67, !68)&lt;/li>
&lt;li>&lt;strong>Infrastructure:&lt;/strong> 2 MRs (!71, !74)&lt;/li>
&lt;li>&lt;strong>Data integrity:&lt;/strong> 3 MRs (!47, !76, !77)&lt;/li>
&lt;li>&lt;strong>UI polish:&lt;/strong> 2 MRs (!78, !79)&lt;/li>
&lt;/ul>
&lt;h2 id="commit-message-quality">
 &lt;a class="anchor" href="#commit-message-quality" data-anchor="commit-message-quality" aria-hidden="true">#&lt;/a>
 Commit Message Quality
&lt;/h2>
&lt;p>Every commit follows the conventional commits format: &lt;code>type(scope): description&lt;/code>. The scope tells you exactly which app/layer is affected, and the description is specific enough to understand the change without reading the diff.&lt;/p></description></item><item><title>PPL: Security [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-security/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-security/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week addressed four security areas: JWT algorithm validation hardening (reviewed MR !63), encryption key management in CI (MR !68), client PII encryption in the database seeder (reviewed MR !80), and ongoing SAST scanning via the CI pipeline.&lt;/p>
&lt;h2 id="jwt-algorithm-validation-preventing-algnone-attacks">
 &lt;a class="anchor" href="#jwt-algorithm-validation-preventing-algnone-attacks" data-anchor="jwt-algorithm-validation-preventing-algnone-attacks" aria-hidden="true">#&lt;/a>
 JWT Algorithm Validation: Preventing alg:none Attacks
&lt;/h2>
&lt;p>MR !63 (by adipppp, which I reviewed) hardened the JWT decoding logic against the &lt;strong>alg:none attack&lt;/strong>, one of the most well-known JWT vulnerabilities (OWASP A07:2021, Identification and Authentication Failures).&lt;/p></description></item><item><title>PPL: SOLID and Layered Architecture [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-programming/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-programming/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week&amp;rsquo;s invoice management feature (MR !12) required a full CRUD stack with filtering, sorting, auth enforcement, and business validation (status transitions, admin-only delete). Building this properly meant enforcing strict layer boundaries and handling the tension between HTTP concerns and domain logic.&lt;/p>
&lt;h2 id="the-three-layer-pattern-in-practice">
 &lt;a class="anchor" href="#the-three-layer-pattern-in-practice" data-anchor="the-three-layer-pattern-in-practice" aria-hidden="true">#&lt;/a>
 The Three-Layer Pattern in Practice
&lt;/h2>
&lt;p>SIRA&amp;rsquo;s backend follows Router → Service → DB, where each layer has a single responsibility:&lt;/p></description></item><item><title>PPL: Test-Driven Development [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w3-tdd/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w3-tdd/</guid><description>&lt;h2 id="what-i-worked-on">
 &lt;a class="anchor" href="#what-i-worked-on" data-anchor="what-i-worked-on" aria-hidden="true">#&lt;/a>
 What I Worked On
&lt;/h2>
&lt;p>This week shipped three full-stack features using strict TDD discipline: invoice management (CRUD + filtering), layout/dashboard (sidebar, header, dashboard with role-based navigation), and a payments sidebar navigation link. Each feature followed red-green-refactor with tagged commits.&lt;/p>
&lt;p>The project now has &lt;strong>392 backend tests&lt;/strong> and &lt;strong>195 frontend tests&lt;/strong> (587 total), up from 51 last week.&lt;/p>
&lt;h2 id="red-green-refactor-commit-discipline">
 &lt;a class="anchor" href="#red-green-refactor-commit-discipline" data-anchor="red-green-refactor-commit-discipline" aria-hidden="true">#&lt;/a>
 Red-Green-Refactor Commit Discipline
&lt;/h2>
&lt;p>Every feature branch this week followed tagged commits so the TDD flow is auditable from the git history alone.&lt;/p></description></item><item><title>PPL: Building a Production-Safe Migration Pipeline with Automated Rollback</title><link>https://blog.abhipraya.dev/ppl/part-a/data-seeding/</link><pubDate>Thu, 12 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/data-seeding/</guid><description>&lt;h2 id="why-database-migrations-need-safety-nets">
 &lt;a class="anchor" href="#why-database-migrations-need-safety-nets" data-anchor="why-database-migrations-need-safety-nets" aria-hidden="true">#&lt;/a>
 Why Database Migrations Need Safety Nets
&lt;/h2>
&lt;p>Imagine this scenario: a developer adds a new column to the invoices table, pushes to &lt;code>main&lt;/code>, and the CI/CD pipeline deploys it to production. Everything looks fine until the next morning, when the team discovers that the migration also dropped a constraint that was silently relied on by another service. Rolling back means manually writing SQL against the production database at 2 AM.&lt;/p></description></item><item><title>PPL: Building an Integrated Tool Ecosystem for a 9-Person University Team</title><link>https://blog.abhipraya.dev/ppl/part-a/teamwork-tools/</link><pubDate>Thu, 12 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-a/teamwork-tools/</guid><description>&lt;h2 id="why-tooling-is-a-team-problem-not-a-devops-problem">
 &lt;a class="anchor" href="#why-tooling-is-a-team-problem-not-a-devops-problem" data-anchor="why-tooling-is-a-team-problem-not-a-devops-problem" aria-hidden="true">#&lt;/a>
 Why Tooling Is a Team Problem, Not a DevOps Problem
&lt;/h2>
&lt;p>Most university software engineering courses teach you &lt;em>which&lt;/em> tools to use (Git for version control, Jira for tickets, Docker for deployment). What they rarely teach is &lt;strong>how tools interact with each other&lt;/strong>, and what happens when they don&amp;rsquo;t.&lt;/p>
&lt;p>In a professional environment, a single commit can trigger a cascade: CI runs tests, a Slack bot notifies the team, a coverage report lands in SonarQube, and the ticket moves to &amp;ldquo;In Review&amp;rdquo; on the project board. This doesn&amp;rsquo;t happen by accident. Someone has to build that integration layer, and in a university team, that someone is usually whoever cares enough about developer experience to do it.&lt;/p></description></item><item><title>PPL: Communication and Knowledge Sharing [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-c/s1w3-communication/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s1w3-communication/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>This week&amp;rsquo;s communication contributions fall into five categories: detailed code reviews, merge coordination, issue tracking, direct code contributions to teammates&amp;rsquo; MRs, and proactive Discord communication to unblock the team.&lt;/p>
&lt;hr>
&lt;h2 id="1-detailed-code-reviews-with-actionable-fixes">
 &lt;a class="anchor" href="#1-detailed-code-reviews-with-actionable-fixes" data-anchor="1-detailed-code-reviews-with-actionable-fixes" aria-hidden="true">#&lt;/a>
 1. Detailed Code Reviews with Actionable Fixes
&lt;/h2>
&lt;p>Reviewed 15 MRs this week. Each review goes beyond surface-level &amp;ldquo;LGTM&amp;rdquo;: they include issue categorization (Critical/Important), before/after code snippets, and concrete suggested fixes that the MR owner can directly apply.&lt;/p></description></item><item><title>PPL: New Learnings Applied to SIRA [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-c/s1w3-aptitude/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s1w3-aptitude/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 1 Week 3 (Mar 4 to 10) was infrastructure-heavy. The project needed production-grade tooling that goes well beyond standard coursework: reverse proxies, email alerting, migration safety nets, security scanning, structured logging, and AI-integrated code quality analysis. Each of these required learning a new technology from scratch and applying it directly to the project.&lt;/p>
&lt;hr>
&lt;h2 id="1-nginx-reverse-proxy-for-subdomain-routing">
 &lt;a class="anchor" href="#1-nginx-reverse-proxy-for-subdomain-routing" data-anchor="1-nginx-reverse-proxy-for-subdomain-routing" aria-hidden="true">#&lt;/a>
 1. Nginx Reverse Proxy for Subdomain Routing
&lt;/h2>
&lt;p>SIRA runs three services on a single Nashta VM, each needing its own subdomain:&lt;/p></description></item><item><title>PPL: Work Ethic and Commitment [Sprint 1, Week 3]</title><link>https://blog.abhipraya.dev/ppl/part-c/s1w3-commitment/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-c/s1w3-commitment/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>Sprint 1 Week 3 (Mar 4 to 10) output summary:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Metric&lt;/th>
 &lt;th>Count&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>MRs authored &amp;amp; merged&lt;/td>
 &lt;td>13&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs reviewed&lt;/td>
 &lt;td>15&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MRs merged as maintainer&lt;/td>
 &lt;td>~25&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Linear tickets created (cumulative)&lt;/td>
 &lt;td>69/98&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Domains covered&lt;/td>
 &lt;td>5 (CI/CD, infra, backend, frontend, database)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="quantitative-output">
 &lt;a class="anchor" href="#quantitative-output" data-anchor="quantitative-output" aria-hidden="true">#&lt;/a>
 Quantitative Output
&lt;/h2>
&lt;h3 id="mrs-authored-and-merged">
 &lt;a class="anchor" href="#mrs-authored-and-merged" data-anchor="mrs-authored-and-merged" aria-hidden="true">#&lt;/a>
 MRs Authored and Merged
&lt;/h3>
&lt;p>13 MRs created and merged to &lt;code>main&lt;/code> this week:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>MR&lt;/th>
 &lt;th>Type&lt;/th>
 &lt;th>Description&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/31">!31&lt;/a>&lt;/td>
 &lt;td>feat(ci)&lt;/td>
 &lt;td>Linear auto-tagger for GitLab MR linking&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/34">!34&lt;/a>&lt;/td>
 &lt;td>fix&lt;/td>
 &lt;td>Code quality issues from post-payment-merge review&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/35">!35&lt;/a>&lt;/td>
 &lt;td>fix(auth)&lt;/td>
 &lt;td>Infinite loading after login via auto-link app_users&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/37">!37&lt;/a>&lt;/td>
 &lt;td>feat(api)&lt;/td>
 &lt;td>Structured JSON HTTP access logging middleware&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/38">!38&lt;/a>&lt;/td>
 &lt;td>feat(ci)&lt;/td>
 &lt;td>Security SAST job with bandit and pnpm audit&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/39">!39&lt;/a>&lt;/td>
 &lt;td>feat(infra)&lt;/td>
 &lt;td>GlitchTip email alerting via Resend SMTP&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/41">!41&lt;/a>&lt;/td>
 &lt;td>feat(ci)&lt;/td>
 &lt;td>Manual trigger &amp;amp; rollback for production migrations&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/42">!42&lt;/a>&lt;/td>
 &lt;td>fix(ci)&lt;/td>
 &lt;td>Restore automatic migration, keep rollback as manual&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/44">!44&lt;/a>&lt;/td>
 &lt;td>feat(infra)&lt;/td>
 &lt;td>Nginx reverse proxy for subdomain routing&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/47">!47&lt;/a>&lt;/td>
 &lt;td>feat(db)&lt;/td>
 &lt;td>Seed production admin and staff users&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/54">!54&lt;/a>&lt;/td>
 &lt;td>feat(web)&lt;/td>
 &lt;td>Add Payments link to sidebar navigation&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/66">!66&lt;/a>&lt;/td>
 &lt;td>fix(db)&lt;/td>
 &lt;td>Rename out-of-order migration timestamps&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/67">!67&lt;/a>&lt;/td>
 &lt;td>feat(ci)&lt;/td>
 &lt;td>Migration dry-run validation for MR pipelines&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Plus &lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/68">!68&lt;/a> (hotfix: missing ENCRYPTION_KEY in deploy env).&lt;/p></description></item><item><title>PPL: AI-Augmented Development with Claude Code [Sprint 1, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w2-ai-literacy/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w2-ai-literacy/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;p>The SIRA project has a comprehensive AI development setup built around Claude Code. Rather than using AI as a one-off assistant, the project treats AI tooling as infrastructure - versioned, tested, and evolved alongside the codebase.&lt;/p>
&lt;pre class="mermaid">graph TB
 subgraph claude [Claude Code]
 CM["CLAUDE.md&lt;br>3 files, 19 commits"]
 MCP["MCP Servers&lt;br>4 integrations"]
 CMD["Commands&lt;br>3 slash commands"]
 SK["Skills&lt;br>3 project skills"]
 PL["Plugins&lt;br>3 enabled"]
 end

 subgraph ext [External Services]
 LN[Linear]
 SB[Supabase]
 SQ[SonarQube]
 GL[GitLab]
 end

 MCP --> LN
 MCP --> SB
 MCP --> SQ
 CMD --> GL
&lt;/pre>

&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Component&lt;/th>
 &lt;th>Count&lt;/th>
 &lt;th>What&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>CLAUDE.md files&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>Root + &lt;code>apps/web/&lt;/code> + &lt;code>apps/api/&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>AGENTS.md symlinks&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>Multi-tool compatibility (Cursor, Copilot, Codex)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MCP servers&lt;/td>
 &lt;td>4&lt;/td>
 &lt;td>Linear, Supabase, Context7, SonarQube&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Slash commands&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>&lt;code>/commit&lt;/code>, &lt;code>/commit-and-push&lt;/code>, &lt;code>/create-pr&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Project skills&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>frontend-design, vercel-react-best-practices, web-design-guidelines&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Plugins&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>GitLab, PR review toolkit, CLAUDE.md management&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="claudemd---three-files-19-commits">
 &lt;a class="anchor" href="#claudemd---three-files-19-commits" data-anchor="claudemd---three-files-19-commits" aria-hidden="true">#&lt;/a>
 CLAUDE.md - Three Files, 19 Commits
&lt;/h2>
&lt;p>&lt;code>CLAUDE.md&lt;/code> is the AI instruction document for the SIRA project. It was not written once and left static. It evolved through &lt;strong>19 commits&lt;/strong>, each one a direct response to a real friction point encountered during development.&lt;/p></description></item><item><title>PPL: Code Review Across Architecture and Runtime Bugs [Sprint 1, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w2-code-review/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w2-code-review/</guid><description>&lt;h2 id="overview">
 &lt;a class="anchor" href="#overview" data-anchor="overview" aria-hidden="true">#&lt;/a>
 Overview
&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>MR&lt;/th>
 &lt;th>Feature&lt;/th>
 &lt;th>Issues&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/10">!10&lt;/a> - SIRA-31&lt;/td>
 &lt;td>Payment recording (dafandikri)&lt;/td>
 &lt;td>2&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/12">!12&lt;/a> - SIRA-30&lt;/td>
 &lt;td>Invoice management (froklax)&lt;/td>
 &lt;td>7&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/17">!17&lt;/a> - SIRA-29&lt;/td>
 &lt;td>Client management (fadhli)&lt;/td>
 &lt;td>1&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://gitlab.lab.local/joint-research/ui/smart-invoice-reminder/SIRA/-/merge_requests/20">!20&lt;/a> - SIRA-27&lt;/td>
 &lt;td>Layout and dashboard (haliza)&lt;/td>
 &lt;td>1&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>11 issues total. Each review included specific file and line references.&lt;/p>
&lt;hr>
&lt;h2 id="mr-10---payment-recording-2-issues">
 &lt;a class="anchor" href="#mr-10---payment-recording-2-issues" data-anchor="mr-10---payment-recording-2-issues" aria-hidden="true">#&lt;/a>
 MR !10 - Payment Recording (2 issues)
&lt;/h2>
&lt;p>&lt;strong>Issue 1: &lt;code>.single()&lt;/code> causes an unhandled 500 on missing records.&lt;/strong>&lt;/p>
&lt;p>&lt;code>get_payment_by_id&lt;/code> used &lt;code>.single()&lt;/code> which throws &lt;code>PostgRESTError (PGRST116)&lt;/code> when no row is found, making the &lt;code>if payment is None&lt;/code> guard below it dead code. The fix is &lt;code>.maybe_single()&lt;/code>, which returns &lt;code>None&lt;/code> cleanly. The same fix was already applied in MR !23 for &lt;code>app_users.py&lt;/code>.&lt;/p></description></item><item><title>PPL: Static Analysis in SIRA [Sprint 1, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w2-code-quality/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w2-code-quality/</guid><description>&lt;h2 id="the-tool-stack">
 &lt;a class="anchor" href="#the-tool-stack" data-anchor="the-tool-stack" aria-hidden="true">#&lt;/a>
 The Tool Stack
&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Layer&lt;/th>
 &lt;th>Tool&lt;/th>
 &lt;th>Scope&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Frontend lint + format&lt;/td>
 &lt;td>&lt;a href="https://biomejs.dev">Biome&lt;/a>&lt;/td>
 &lt;td>TypeScript/TSX, 40+ enforced rules&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Frontend dead code&lt;/td>
 &lt;td>&lt;a href="https://knip.dev">Knip&lt;/a>&lt;/td>
 &lt;td>Unused exports, imports, dependencies&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Frontend types&lt;/td>
 &lt;td>&lt;code>tsc --noEmit&lt;/code>&lt;/td>
 &lt;td>TypeScript type checking&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Backend lint + format&lt;/td>
 &lt;td>&lt;a href="https://docs.astral.sh/ruff/">Ruff&lt;/a>&lt;/td>
 &lt;td>Python, F401/F841 + style rules&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Backend types&lt;/td>
 &lt;td>&lt;a href="https://mypy.readthedocs.io">mypy&lt;/a>&lt;/td>
 &lt;td>Python static types, strict mode&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CI quality gate&lt;/td>
 &lt;td>&lt;a href="https://sonarqube.cs.ui.ac.id">SonarQube&lt;/a>&lt;/td>
 &lt;td>Coverage, code smells, security hotspots&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="pre-commit-hooks-husky">
 &lt;a class="anchor" href="#pre-commit-hooks-husky" data-anchor="pre-commit-hooks-husky" aria-hidden="true">#&lt;/a>
 Pre-commit Hooks (Husky)
&lt;/h2>
&lt;p>Five checks run sequentially on every &lt;code>git commit&lt;/code>. A failure at any step blocks the commit:&lt;/p></description></item><item><title>PPL: Test-Driven Development in a FastAPI Project [Sprint 1, Week 2]</title><link>https://blog.abhipraya.dev/ppl/part-b/s1w2-tdd/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0700</pubDate><guid>https://blog.abhipraya.dev/ppl/part-b/s1w2-tdd/</guid><description>&lt;p>TDD forces you to think about the interface before the implementation. This post covers how it was applied in the Smart Invoice Reminder AI (SIRA) backend for Sprint 1.&lt;/p>
&lt;h2 id="test-distribution">
 &lt;a class="anchor" href="#test-distribution" data-anchor="test-distribution" aria-hidden="true">#&lt;/a>
 Test Distribution
&lt;/h2>
&lt;p>The API currently has 51 test functions across 4 files:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>File&lt;/th>
 &lt;th>Tests&lt;/th>
 &lt;th>Scope&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>tests/test_auth.py&lt;/code>&lt;/td>
 &lt;td>14&lt;/td>
 &lt;td>JWT validation, RBAC, DB queries&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>tests/test_payments.py&lt;/code>&lt;/td>
 &lt;td>24&lt;/td>
 &lt;td>Payment CRUD, business logic, edge cases&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>tests/test_db_schema_and_seed.py&lt;/code>&lt;/td>
 &lt;td>6&lt;/td>
 &lt;td>Migration integrity, seed validation&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>tests/test_logging_middleware.py&lt;/code>&lt;/td>
 &lt;td>7&lt;/td>
 &lt;td>HTTP access logging, request/response capture&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="red-green-refactor">
 &lt;a class="anchor" href="#red-green-refactor" data-anchor="red-green-refactor" aria-hidden="true">#&lt;/a>
 Red-Green-Refactor
&lt;/h2>
&lt;p>The authentication feature (&lt;a href="https://linear.app/ppl-sira/issue/SIRA-26">SIRA-26&lt;/a>) followed strict TDD commit discipline. Each backend function got its own RED commit (failing test) followed by a GREEN commit (passing implementation) before any cleanup.&lt;/p></description></item></channel></rss>