<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Dominik Vrbic</title>
        <link>https://dominikvrbic.com</link>
        <description>Notes on engineering leadership, AI &amp; data strategy, and turning research into product.</description>
        <lastBuildDate>Thu, 07 May 2026 15:29:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Dominik Vrbic</title>
            <url>https://dominikvrbic.com/favicon.ico</url>
            <link>https://dominikvrbic.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[What 45,000 plant-science questions taught us about platform UI]]></title>
            <link>https://dominikvrbic.com/articles/45000-plant-science-questions</link>
            <guid isPermaLink="false">https://dominikvrbic.com/articles/45000-plant-science-questions</guid>
            <pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>A pull request landed on our mobile app last week. A small UX cleanup: a banner that used to list every “pinned” answer during a measurement loop, trimmed down to one. Cleaner, simpler, less visual clutter. Good change.</p>
<p>Except how it picked the one:</p>
<pre class="language-ts"><code class="language-ts"><span class="token keyword">const</span> firstAutoProceededWithPlot <span class="token operator">=</span> flowNodes<span class="token punctuation">.</span><span class="token method function property-access">find</span><span class="token punctuation">(</span>
  <span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span>
    n<span class="token punctuation">.</span><span class="token property-access">type</span> <span class="token operator">===</span> <span class="token string">"question"</span> <span class="token operator">&amp;&amp;</span>
    <span class="token function">isAutoincrementEnabled</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>
    <span class="token operator">!</span><span class="token operator">!</span><span class="token function">getAnswer</span><span class="token punctuation">(</span>iterationCount<span class="token punctuation">,</span> n<span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">)</span><span class="token operator">?.</span><span class="token method function property-access">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>
    <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">(plot)</span><span class="token regex-delimiter">/</span><span class="token regex-flags">i</span></span><span class="token punctuation">.</span><span class="token method function property-access">test</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>n<span class="token punctuation">.</span><span class="token property-access">name</span> <span class="token operator">??</span> <span class="token string">""</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>n<span class="token punctuation">.</span><span class="token property-access">content</span><span class="token operator">?.</span>text <span class="token operator">??</span> <span class="token string">""</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>The banner only renders if one of your pinned questions contains the literal string “plot” in its label or text. If it does, you get a header that reads <strong>“Your current plot.”</strong> If it doesn’t, the banner silently disappears.</p>
<p>I get the instinct. You look at the test experiments on your desk and half the questions are “Plot number”, “Plot ID”, “plot”; the UX team wants to declutter the banner; you ship the regex and move on.</p>
<p>The problem is that openJII isn’t a JII-only app. It’s a platform. And platforms don’t get to decide what their users call things.</p>
<h2>The data, briefly</h2>
<p>Reviewing the PR, my first reaction was a hunch: <em>“what if they call it plant, or leaf, or translate it into anything else?”</em> Hunches are cheap. They’re also not particularly persuasive on their own.</p>
<p>The next step would normally be expensive: go get numbers. Except the numbers were already sitting in a notebook nearby. A few weeks earlier I’d run a semantic analysis on <strong>45,451 free-text questions across 19,658 PhotosynQ projects</strong>. PhotosynQ is the commercial plant-science measurement platform some of JII’s members helped build over the last decade. openJII is the open-source platform we run today, with feature parity and more, and JII retained ownership of the data, which is why this analysis was open to us in the first place.</p>
<p>The analysis answered a different research question (<em>what metadata concepts do plant-science teams actually use?</em>), but the answer also addressed the PR.</p>
<p>After three layers of cleanup (rule-based concept tagging across 22 concepts and 182 multilingual terms, fuzzy clustering on the top 500 normalised forms, and character-n-gram TF-IDF clustering on the long tail), here’s the cross-experiment ranking. Number of distinct projects that use each identifier concept:</p>
<table><thead><tr><th>concept</th><th style="text-align:right">projects</th><th style="text-align:right">label variants</th></tr></thead><tbody><tr><td>treatment</td><td style="text-align:right">5,592</td><td style="text-align:right">539</td></tr><tr><td>rep</td><td style="text-align:right">4,074</td><td style="text-align:right">492</td></tr><tr><td>plant</td><td style="text-align:right">3,783</td><td style="text-align:right">1,213</td></tr><tr><td>leaf</td><td style="text-align:right">3,265</td><td style="text-align:right">1,274</td></tr><tr><td>genotype</td><td style="text-align:right">2,104</td><td style="text-align:right">361</td></tr><tr><td><strong>plot</strong></td><td style="text-align:right"><strong>1,729</strong></td><td style="text-align:right"><strong>291</strong></td></tr><tr><td>color</td><td style="text-align:right">1,258</td><td style="text-align:right">269</td></tr><tr><td>species</td><td style="text-align:right">938</td><td style="text-align:right">251</td></tr><tr><td>variety</td><td style="text-align:right">801</td><td style="text-align:right">161</td></tr><tr><td>sample</td><td style="text-align:right">795</td><td style="text-align:right">251</td></tr></tbody></table>
<p><strong>Plot is sixth.</strong></p>
<p>Not universal. Not even dominant. It sits behind treatment, rep, plant, leaf, and genotype, all ordinary enough that any “cleaner UX” argument that applies to plot applies equally to each of them.</p>
<p>And that’s just the English variants. The 1,729 projects that <em>do</em> use a plot concept use <strong>291 distinct spellings</strong>: <code>Plot</code>, <code>N PLOT</code>, <code>Plot no</code>, <code>Plot#</code>, <code>plot number</code>, <code>nplot</code>. Spanish <code>parcela</code> and <code>parcelas</code>. French <code>parcelle</code>. German <code>Parzelle</code>. Portuguese <code>parcela</code>. Chinese <code>xiao qu</code> (小区). Russian Cyrillic. The hardcoded <code>/plot/i</code> match catches some of the English variants and literally none of the others.</p>
<p>Even when the keyword rules are widened to cover Spanish, French, German, Portuguese, and Chinese (as the full analysis does), <strong>only 62.8% of labels match any keyword concept at all</strong>. The remaining 37% are conversational (“Which treatment was applied?”, “¿Cuál es la variedad?”, “测量条件是什么?”), domain-specific (<code>DAS</code>, <code>DAP</code>, <code>Cultivar</code>, <code>NDVI</code>), or experiment-local. No regex on “plot” reaches them.</p>
<h2>The platform lesson</h2>
<p>The data makes a point more firmly than any gut check could: <strong>if you hardcode “plot” into your platform UI (whether it’s a regex, a conditional, or a string), you are deciding on behalf of every tenant that their experiments revolve around plots.</strong> A decade of PhotosynQ’s history shows the majority don’t. They revolve around treatments, replicates, plants, leaves, genotypes, whatever the scientist is actually measuring.</p>
<p>A platform doesn’t get to make that call.</p>
<p>The right fix isn’t to stretch the regex. It’s to move the semantics out of free text:</p>
<ol>
<li><strong>Typed question identifiers.</strong> Give the platform a concept of <em>“this question is the plot identifier”</em> (or the treatment identifier, or the replicate counter) as a property of the question itself, not a substring of its label. A researcher designing an experiment opts in explicitly.</li>
<li><strong>A “show in summary” flag on the question.</strong> The same mechanism works for anyone’s workflow: treatment, rep, genotype, or something the platform authors never anticipated. You tell the platform which question matters; the platform doesn’t guess.</li>
<li><strong>Internationalised banner copy.</strong> “Your current plot” becomes “Your current {label}”, sourced from the question’s actual name, in the researcher’s language.</li>
</ol>
<p>These are easy decisions to skip in the name of shipping a small UX fix. They’re exactly the decisions that compound when you’re building a platform meant to serve teams beyond your own four walls.</p>
<h2>Why I like this story</h2>
<p>The PR review comment rewrote itself. Instead of <em>“I think this is fragile”</em>, I could paste a concrete ranking table and a chart. The discussion moved from “convince me” to “okay, what do we actually want to build?” in one round.</p>
<p>That’s the part I keep coming back to. Most of what a platform team decides never gets a clean public case study. Decisions get made in Slack, in a whiteboard session, under a deadline. The ones informed by real data are the exceptions.</p>
<p>When you’re building an open platform for science, the pressure to hardcode domain vocabulary into your UI is constant. And the cost shows up, quietly, in every experiment your platform fails to serve.</p>]]></content:encoded>
            <author>dominikvrbic01@gmail.com (Dominik Vrbic)</author>
        </item>
        <item>
            <title><![CDATA[Boosting efficiency with data-driven software development]]></title>
            <link>https://dominikvrbic.com/articles/data-driven-software-development</link>
            <guid isPermaLink="false">https://dominikvrbic.com/articles/data-driven-software-development</guid>
            <pubDate>Wed, 26 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>In the ever-changing world of software development, one thing has become clear: the need to adapt and innovate is a constant. One way many of us in the field have found to maintain relevance and efficiency is through the adoption of a data-driven approach. Drawing on experiences from a successful implementation at INFO, this post aims to provide insights on integrating data-driven methodologies into your software development lifecycle.</p>
<h2>The case for a data-driven approach</h2>
<p>When used effectively, data can catalyse informed decision-making, improve efficiency, allocate resources better, and accelerate project delivery. Honing in on key performance metrics (project timelines, bug rates, customer satisfaction) uncovers valuable insights into a process’s actual effectiveness. Adopting frameworks like DORA (DevOps Research and Assessment) provides a holistic perspective on a project’s health.</p>
<img alt="Implementation strategy: data analysis and metrics definition → implementation and integration → monitoring and continuous improvement" loading="lazy" width="695" height="344" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimplementation-strategy.0-cdbttyqzvi_.webp&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimplementation-strategy.0-cdbttyqzvi_.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fimplementation-strategy.0-cdbttyqzvi_.webp&amp;w=1920&amp;q=75">
<h2>What you actually get from it</h2>
<ol>
<li><strong>Cost reduction and improved efficiency.</strong> Data-driven decisions help identify inefficiencies, streamline processes, and eradicate unnecessary costs. The result is better resource utilisation, faster project delivery, and increased productivity.</li>
<li><strong>Enhanced project management.</strong> Data analysis makes potential bottlenecks and risks visible, allowing the team to address them proactively. Better project planning, higher success rates.</li>
<li><strong>Personalised customer experiences.</strong> Understanding customer behaviours and preferences via data analytics lets you tailor your services, enhancing satisfaction and fostering loyalty.</li>
</ol>
<h2>The common concerns</h2>
<p>Despite the benefits, there are real concerns around privacy, data security, and bias. They’re manageable:</p>
<ol>
<li><strong>Privacy.</strong> Implement robust protocols to protect sensitive data; use anonymisation techniques to ensure individual privacy.</li>
<li><strong>Bias in data analysis.</strong> Use diverse and representative datasets. Monitor regularly to identify and rectify bias as it arises.</li>
<li><strong>Data security.</strong> Establish strict security protocols to prevent breaches; conduct regular audits to maintain data integrity.</li>
</ol>
<img alt="Three sequential controls: implement privacy protocols, conduct bias checks, ensure data security" loading="lazy" width="210" height="243" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fconcerns.0hjn61d1kz8vu.webp&amp;w=256&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fconcerns.0hjn61d1kz8vu.webp&amp;w=640&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fconcerns.0hjn61d1kz8vu.webp&amp;w=640&amp;q=75">
<h2>Charting your course</h2>
<ol>
<li><strong>Data analysis and metrics definition.</strong> Begin with a thorough analysis of your project timelines, bug rates, customer satisfaction levels, and any other relevant metrics. Define specific metrics that align with your organisation’s goals.</li>
<li><strong>Implementation and integration.</strong> Develop data collection mechanisms and integrate data analysis tools into your existing infrastructure. Train your team to ensure a smooth transition.</li>
<li><strong>Monitoring and continuous improvement.</strong> Watch the data, analyse it regularly, use the insights to inform decisions and improve where necessary.</li>
</ol>
<h2>Wrap-up</h2>
<p>Adopting a data-driven approach can do wonders to make your software development process more efficient and customer-centric. By harnessing data analytics, you equip your team to make more informed decisions and elevate customer satisfaction.</p>
<p>This transformation isn’t a solo endeavour; it requires a collaborative effort, thorough training, and ongoing monitoring. Addressing potential concerns like privacy, security, and bias is essential to keep the approach ethical and effective.</p>
<p>In the midst of digital transformation, embracing data can unlock an organisation’s full potential, supercharge the customer experience, and drive business growth.</p>]]></content:encoded>
            <author>dominikvrbic01@gmail.com (Dominik Vrbic)</author>
        </item>
        <item>
            <title><![CDATA[Notes from the field: my second week at JII, training at the Accra photosynthesis hackathon]]></title>
            <link>https://dominikvrbic.com/articles/ghana-photosynthesis-hackathon</link>
            <guid isPermaLink="false">https://dominikvrbic.com/articles/ghana-photosynthesis-hackathon</guid>
            <pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>From 9 to 13 March 2026, the <a href="https://computational-biology-aachen.github.io/2026-photosynthesis-hackathon/">first dedicated photosynthesis hackathon</a> ran in Accra, Ghana. It was hosted by IITA and organised between RWTH Aachen, the Jan IngenHousz Institute, the University of Cambridge, and IITA itself, with 35 participants drawn from 14 countries across Europe and Africa: data scientists, plant physiologists, geneticists, breeders, and bioinformaticians. I had been preparing for it from outside JII for a few months, and started full-time as JII’s Technical Program Manager just before flying out. Week two on the job. I was there as one of the trainers.</p>
<p>I cowrote <a href="https://openjii.org/en-US/blog/ghana-photosynthesis-hackathon">the official reflection on the openJII blog</a> with Jacky To. This is the more personal note: what stuck with me from the trainer side of the room, and what surprised me about the platform underneath.</p>
<img alt="The 35 hackathon participants posing together in front of the openJII Hackathon Ghana 2026 banner, with IITA and CGIAR branding visible" loading="lazy" width="1600" height="1067" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-group.0znkfu3c40asb.webp&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-group.0znkfu3c40asb.webp&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-group.0znkfu3c40asb.webp&amp;w=3840&amp;q=75">
<h2>Collaborative, not competitive</h2>
<p>Most hackathons run on adrenaline and ranking. This one didn’t. Five interdisciplinary teams worked on shared field datasets (cowpea from Nigeria, barley from the Netherlands and Ethiopia, common bean and potato from the Netherlands) and presented progress to each other every day. Common obstacles got resolved in a corridor, not at a final-day judging panel. By day three, teams were openly comparing notes on the same dataset rather than guarding lines of attack, and that’s when the more interesting findings started to emerge.</p>
<p>The design choice paid off. Convergent findings across independent teams (different methods, different crops) carry much more weight than any single team’s result, and that convergence is what most of the participants will be co-authoring in the months after the event.</p>
<img alt="A team of hackathon participants gathered around laptops, comparing analyses of a shared field dataset" loading="lazy" width="1600" height="1067" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-teams.0hxy-0dk8scbg.webp&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-teams.0hxy-0dk8scbg.webp&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-teams.0hxy-0dk8scbg.webp&amp;w=3840&amp;q=75">
<h2>What teams actually found</h2>
<p>Two threads stood out. First, several teams independently demonstrated that the <strong>temporal structure</strong> of photosynthesis measurements distinguishes genotypes more cleanly than canonical single-point parameters do. Looking at <em>the trace itself</em>, not just the extracted summaries, recovered genetic signal that standard analysis pipelines were quietly discarding. Second, multiple teams converged on <strong>previously underexplored portions of the fluorescence measurement</strong> (particularly later sections of the trace that no extraction pipeline currently uses) as the parts carrying the most heritable, breeding-relevant information.</p>
<p>The methodological corollary is uncomfortable for the field: the convention of compressing a rich time-resolved trace down to a handful of named scalar parameters is leaving a meaningful chunk of the genetic signal on the table. The hackathon didn’t prove this in a way that closes the question (that work is what the follow-on papers are for), but five teams independently arriving at variants of the same observation is hard to explain away.</p>
<img alt="Participants in a focused working session, reviewing time-series traces and notebooks on their laptops" loading="lazy" width="1600" height="1067" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-working.0fxy_1ij-~2js.webp&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-working.0fxy_1ij-~2js.webp&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-working.0fxy_1ij-~2js.webp&amp;w=3840&amp;q=75">
<h2>Training plant breeders is humbling</h2>
<p>The thing nobody tells you about being a trainer at an event like this: the participants are usually smarter than you are about the domain. Plant breeders, crop physiologists, statisticians, data scientists; they all came in with deep field expertise and reasonable expectations of what the data should look like. My job wasn’t to lecture; it was to help them get unstuck on tooling, on dataset shapes, on the mechanics of an analysis pipeline they wanted to build, and on the messy real-world details of working with field measurement data. The good moments were when a participant asked a question I couldn’t answer immediately, and we worked it out at a whiteboard together.</p>
<p>That’s a useful pattern for anyone running training inside a research community. The framing isn’t <em>teach the experts</em>; it’s <em>be the friction-reducer the experts need to do their best work</em>. Bring the tools, the structure, and the willingness to be wrong out loud, and let the domain depth come from the room.</p>
<img alt="A trainer at a whiteboard working through an analysis question with a small group of participants gathered around their laptops" loading="lazy" width="1600" height="1067" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-whiteboard.0~h.7c79wkfys.webp&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-whiteboard.0~h.7c79wkfys.webp&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhackathon-whiteboard.0~h.7c79wkfys.webp&amp;w=3840&amp;q=75">
<h2>What the platform did quietly</h2>
<p>The piece I’m proudest of, with my technical-program-manager hat on, is what <em>didn’t</em> happen at the start of the event. Every multi-team data hackathon I’ve been near has lost its first day or two to the dull friction of access: whose laptop sees which file, whose Python install can read which format, whose VPN is talking to whose data lake. We didn’t. Datasets were curated and delivered through openJII running on Databricks, and 35 people walked into a shared, zero-setup analytical environment from the first morning.</p>
<p>That’s not glamorous. It is, however, exactly the kind of platform-level work that compounds. The same lakehouse pattern that runs openJII’s silver and gold layers in production was the substrate for the event, which means the analytical workflows the teams built during the week aren’t throwaway. They live on the same infrastructure that will receive the next round of field data. The hackathon didn’t end on Friday so much as graduate.</p>
<p>This is the through-line for me, professionally. The data-platform practice I launched at INFO grew up around Databricks; an agritech digital-transformation engagement adopted it on my recommendation; openJII runs on it now; and an event like this is what it looks like when that decision is paying off in the room rather than on a slide.</p>
<h2>What it meant for openJII</h2>
<p>For openJII specifically, the event was an early proof of concept for what the platform exists to do: take field datasets across geographies and crops, give people sensible primitives to work on them, and step out of the way. The follow-on plans (co-authored documentation, regional African sensor hubs, dataset sharing through the open-science platform) are the things that turn a one-off hackathon into community infrastructure.</p>
<p>It’s an unusual way to start a job. But the week made clear what <em>good</em> would look like: the questions the participants were already asking are the questions the platform needs to be ready for.</p>]]></content:encoded>
            <author>dominikvrbic01@gmail.com (Dominik Vrbic)</author>
        </item>
        <item>
            <title><![CDATA[The case against Husky and Git hooks]]></title>
            <link>https://dominikvrbic.com/articles/husky-git-hooks-limitations</link>
            <guid isPermaLink="false">https://dominikvrbic.com/articles/husky-git-hooks-limitations</guid>
            <pubDate>Wed, 19 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you’re a software developer, you’ve probably encountered Git hooks and Husky. They sound like a good deal: predefined checks before each commit catch issues early. I thought the same, until the realities of the tools caught up with how teams actually work.</p>
<img alt="Sequence diagram showing a developer committing through Git, Husky pre-commit hooks, then a Pull Request and PR-level checks, before merge into the main codebase" loading="lazy" width="720" height="402" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fworkflow.0qa3-9-0jwtvt.webp&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fworkflow.0qa3-9-0jwtvt.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fworkflow.0qa3-9-0jwtvt.webp&amp;w=1920&amp;q=75">
<h2>The trouble with Git hooks and Husky</h2>
<p>You know the drill: you’re cranking out code, you’re in the zone, you want to save your progress, and you commit. Then a cascade of linting errors and failed tests slaps you in the face. So much for a smooth flow.</p>
<p>When you’re knee-deep in development it’s normal to commit code at various stages, and it’s not always going to be picture-perfect. Git hooks and Husky aren’t so forgiving; they block commits for the slightest transgression.</p>
<p>The kicker: those rules are easier to dodge than they look. <code>git commit --no-verify</code> skips them entirely. So we end up with a system that’s both painful to deal with <em>and</em> trivial to bypass, a worst-of-both-worlds for code quality.</p>
<h2>Branch protection and PR-level checks</h2>
<p>The alternative is to move enforcement to where it matters most: branch protection and PR-level checks.</p>
<img alt="Sequence diagram showing the same workflow without Husky: developer commits freely to Git, pushes, opens a PR, and PR-level checks are the only quality gate before merge" loading="lazy" width="684" height="406" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fpr.0~lf67kkpe0hc.webp&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fpr.0~lf67kkpe0hc.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fpr.0~lf67kkpe0hc.webp&amp;w=1920&amp;q=75">
<p>With this approach you can commit code at any stage without being stonewalled by prematurely applied checks. The real scrutiny happens where it counts: when the code is about to enter the main codebase. And there’s no <code>--no-verify</code> escape hatch. Checks at the PR level are applied uniformly, keeping the codebase consistently clean and developer flow uninterrupted.</p>
<h2>Final thoughts</h2>
<p>I welcomed Git hooks and Husky as gatekeepers of code quality. After seeing them in action, I think we’re better off without them.</p>
<p>Enforce rules at the PR level. Let coding be about innovating and solving problems, not jumping through hoops on the way to a save point.</p>]]></content:encoded>
            <author>dominikvrbic01@gmail.com (Dominik Vrbic)</author>
        </item>
    </channel>
</rss>