<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tom's blog]]></title><description><![CDATA[Tom's blog]]></description><link>https://tomquirk.me</link><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 17:27:35 GMT</lastBuildDate><atom:link href="https://tomquirk.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Reflecting on my AI adoption timeline]]></title><description><![CDATA[While talking to a friend recently about how we’re using AI at work, I realised that I couldn’t remember the last time I meaningfully hand-coded something.
This was a pretty profound realisation. I’ve basically become one of those people who don’t wr...]]></description><link>https://tomquirk.me/reflecting-on-my-ai-adoption-timeline</link><guid isPermaLink="true">https://tomquirk.me/reflecting-on-my-ai-adoption-timeline</guid><category><![CDATA[AI]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[startup]]></category><dc:creator><![CDATA[Tom Quirk]]></dc:creator><pubDate>Fri, 13 Feb 2026 03:35:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770953595778/e5365b10-355a-46ca-ac2f-9ccbba487f05.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While talking to a friend recently about how we’re using AI at work, I realised that I couldn’t remember the last time I meaningfully hand-coded something.</p>
<p>This was a pretty profound realisation. I’ve basically become one of those people who don’t write code by hand anymore. And yet, there I was, a mere 12 months ago, when AI was going through one of it’s first “it’s over” hype phase, 1000% sure that “agentic coding” was BS and I’d never be shipping with it.</p>
<p>Oops 😅.</p>
<p>The rate-of-change has been wild to say the least. And it has freaked me out enough to want to write this post. <strong>I want to capture this moment in time before I forget how I got here in the first place</strong>. A little historical artefact. I don’t know interesting or useful this will be to anyone, but at the least, it could be fun to share this with my future kids one day.</p>
<p>(Skip to the end to see my current setup)</p>
<h2 id="heading-pre-june-2025">Pre-June 2025</h2>
<p>My first “AI coding” experience was a common one: GitHub Copilot; I bought the GitHub Copilot annual plan in November 2023. Purely tab completion, it was freaking magical. Type some code, and it would auto-complete the function for you. The group chats I was in at the time were hysterical.</p>
<p>At some point, the whole “agentic” phenomena was born. I remember there was some early open source efforts experimenting with agent loops, but none of them meaningfully took off. I do remember the Devin hype, and being as skeptical as everyone about it. In any case, I hadn’t touched any “agentic” tool up until this point.</p>
<p>Around May 2025 or so, I installed Cursor.</p>
<p>Cursor and Windsurf had already been out for a while, but I’d heard so many mixed reviews that I never bothered to try either. I was still a big AI skeptic at this point. I was working in a legacy codebase at the time, a classic mishmash of half-completed tech debt projects sprawling many repositories, and with no type safety or no tests, it was truly a minefield. I thought there was just no way AI would ever be good enough, have enough context, to be helpful in that environment.</p>
<p>Anyway back to Cursor. Cursor was “the shiz” at this time. All over the Twitter timeline, it had cemented itself as <em>the</em> brand and product in the AI coding space. By now it had some agentic capability via its famous sidebar (it might have had it from the start, I don’t quite remember). I was finally compelled enough to install and take it for a spin on something real.</p>
<p>At the time I was maintaining a popular OSS project, <code>linkedin-api</code>, an unofficial Python wrapper for the LinkedIn API. As a little growth hack idea, I wanted to spin up a bunch of how-to guides for using the library: “How to programatically post on LinkedIn with Python”, that kind of thing.</p>
<p>The perfect test task for Cursor, I thought!</p>
<p>I told the Cursor agent something like, “look at my docs and come up with 5 simple and popular usecases you think would be good candidates for how-to-style articles". And then, “Ok, now write the help guide for each one”.</p>
<p>I remember it being a bit fiddly, but fundamentally it got the job done.</p>
<p>This was probably my “oh sh*t” moment. That it could take a slightly abstract problem, break up into multiple steps, do basic edits across files, etc. But most importantly: <em>actually</em> be useful, and <em>actually</em> save me time, locally on my machine, <em>in my editor</em>.</p>
<p>I purchased the Cursor $20/month plan that day.</p>
<p>I was on Twitter a lot more, frantically trying to keep up with the AI discourse from my little beachside home in Australia. For various reasons, I felt like I wasn’t in the right job to fully take advantage of these tools, and I remember feeling like, “if I leave this too long, I feel like this isn’t gonna be good for me”.</p>
<p>So, on June 3rd I started a new job as a Founding Engineer for a startup. My real AI journey begins here…</p>
<h2 id="heading-june-3-2025">June 3, 2025</h2>
<p>Looking back, this was maybe the <strong>most opportune time</strong> to take a founding engineer role. A role whose whole mandate was to build a lot, quickly, from scratch, and alone (we’ll delve into the specifics of the role later on in the post). Completely a fluke on my part, but interesting to observe in retrospect.</p>
<p>June (and into July) 2025 was Claude Code’s break-out moment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770521381207/ce76c20b-638f-4f9d-b932-6fc5945a72dd.png" alt="Claude Code NPM downloads chart" class="image--center mx-auto" /></p>
<p>And literally in my first week, Dax and crew release OpenCode in GA.</p>
<p><a target="_blank" href="https://x.com/thdxr/status/1935801226362302730"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770521610701/e2641083-ea3a-4601-b53d-de1947e966fa.png" alt class="image--center mx-auto" /></a></p>
<p>I got my new company’s approval to pay for a year of Cursor (for which I was super grateful), but being my first week, and also being a startup, I was too scared to ask for <em>another</em> subscription to Claude.</p>
<p>But lucky for me, my GitHub Copilot subscription still had about 6 months on it. And even luckier, you could plug GitHub Pilot into Opencode and use Anthropic models.</p>
<p>So in summary, my toolkit starting around <strong>June 3rd, 2025</strong> was:</p>
<ul>
<li><p>Cursor. IDE, best-in-class tab completion and small-scale agent stuff</p>
</li>
<li><p>Opencode (powered by GitHub Copilot) for my agentic TUI</p>
</li>
</ul>
<p>I was off to the races. Just me, Cursor, and Opencode, building a full SaaS with 1000s of users from scratch.</p>
<p>Let’s talk about how I was using AI at the time, and where I was finding most leverage.</p>
<h3 id="heading-ai-utility-as-a-founding-engineer">AI utility as a founding engineer</h3>
<p>I’ll ramble a bit about the context of the company I was joining here, not to shill, but because I think it’s relevant to understanding how AI was useful in my first major project at the company.</p>
<p>Tax Nuggets Academy is an online learning platform for accountants; live events and an on-demand video library of Australian tax education where all consumption is tracked as verifiable Continuous Professional Development time (CPD). “Founding engineer” is my title (still here as of Feb 2026), but really I was just the first engineering hire who would fill the ex-technical cofounder’s shoes; the company was founded well before I joined around 2021, and already had strong product market fit and revenue.</p>
<p>The problem was their whole “online” product was a collection of off-the-shelf tools stitched together in a hacky way; they barely had any of their own code. They did things that didn’t scale to get to PMF, but now they were trying to scale the product because they didn’t own their tech.</p>
<p>So, my mandate was simple: rebuild this whole thing from scratch yourself. You’ve got 6 months. Make no mistakes :D.</p>
<p>So like any rebuild, I essentially had 2 main tasks:</p>
<ol>
<li><p>Build and deploy the application from scratch</p>
</li>
<li><p>Migrate all the data to the new system</p>
</li>
</ol>
<h3 id="heading-ai-for-data-migration-mostly-vibes">AI for data migration: mostly vibes</h3>
<p>I’m going to start with the second task, because it was the most interesting as it relates to AI.</p>
<p>The data was in a few different places:</p>
<ul>
<li><p>MongoDB</p>
</li>
<li><p>Uscreen (a video CMS)</p>
</li>
<li><p>Firestore</p>
</li>
<li><p>Bubble (yeah, don’t ask)</p>
</li>
</ul>
<p>And I wanted to pull it all into a single Postgres database (the promised land).</p>
<p>Enter my second “oh sh*t” AI moment. Uscreen’s API wasn’t sufficient for our needs, so we needed a customer scraper. So, I let OpenCode and Sonnet 4.5 loose on this whole thing.</p>
<p>More-or-less, I told it, “here’s the source HTML for this page, here’s my authed cookie, build a scraper to scrape every video, transform and load it to our new database, etc.”</p>
<p>It wrote a full Uscreen scraper in Python/BeautifulSoup to pull the entirety of our data off of Uscreen, back it up to Google Cloud Storage, transform it as necessary and push into our new Postgres data model (the Uscreen API wasn’t sufficient for this btw, hence, scraper).</p>
<p><strong>I barely read any of this code;</strong> it was one-time use, not running in production, and I had the rest of the system to build. I can’t underestimate how much time and pain this saved me.</p>
<p>My reaction to this was so visceral, I wrote a blog post on how I felt like I was cheating in my job - I really did feel this way, that I’d be “found out” for using AI at work. Crazy times.</p>
<h3 id="heading-building-the-app">Building the app</h3>
<p>For building the actual app, AI was definitely useful, but I still ended up doing a lot of it by hand. As the only person responsible for this project, my job, and to large extent my reputation and career were on the line, and there’s no way I could trust whatever Sonnet was spewing out for auth, or access control, or billing or other core business logic. I felt I needed full control and oversight over most of it.</p>
<p>Some it was definitely vibes though.</p>
<p>One thing I felt very strongly about was building an <strong>agent-friendly codebase</strong>. The criteria for this continues to evolve but for me, it meant the following:</p>
<ul>
<li><p>Monorepo (using turborepo)</p>
</li>
<li><p>Everything-as-code: Infra (pulumi), emails (react-email), billing config (Stripe)</p>
</li>
<li><p>End-to-end, strict typesafety: NextJS, shared monorepo packages for common stuff like database types, etc.</p>
</li>
<li><p>Use popular, well-documented tools and services if required: Firebase Auth</p>
</li>
</ul>
<p>I feel this has continued to pay dividends for us, and hopefully those dividends accelerate in the future.</p>
<p><strong>UI</strong></p>
<p>A bunch of the UI <em>code</em> was AI-generated for sure. Once I’d installed my desired ShadCN components and tweaked the component styles to my liking, AI was super useful in the grunt work of implementing stuff like data tables with filter/sort and pagination.</p>
<p>That being said, I hand-coded most of the UI. Mainly because we didn’t have a designer or any designs to work with: design and implementation was a fluid process where I’d build something, use it, tweak it (or redo it altogether).</p>
<p>To this day I still don’t trust AI to build me good UI, that integrates well with my existing product. Maybe it’s just me!</p>
<p><strong>But, I did selectively use Cursor Agent mode</strong>. To this day, my main usecase for Cursor is the ability to highlight a block of code, <code>Command-Shift-I</code> to add the reference to chat, and tell the agent to do something. “Pull this out into its own component”, “make this look like this instead of that”. This feels quicker to me in select cases: it saves the agent a few greps, minimises ambiguity, and reduces blast radius.</p>
<p><strong>Mental game</strong></p>
<p>This was a pretty stressful period of my life: the weight of this whole rebuild on my shoulders, no one in the weeds with me, I was constantly on the edge of burnout. There was a lot of work to be done and not much time to do it; it simply required long hours.</p>
<p>But having AI in my back pocket really helped with my mental fatigue. Not having to stress over every little hand-coding detail, not having to be in deep thought for many hours every day. Call it a skill issue, but honestly I’m not sure I would have held it together had it not been for AI.</p>
<h2 id="heading-november-december-2025">November / December 2025</h2>
<p>We <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7392009344210497536/">launched</a> the rebuilt product on November 3rd, and I finally had some room to breath and catch up with the latest AI stuff. In late November, <a target="_blank" href="https://www.anthropic.com/news/claude-opus-4-5">Opus 4.5 drops</a>. And this is where my terminal usage really started to overtake Cursor.</p>
<p>For context, apparently I ended up a top 22% Cursor user despite only using it hardcore for 5-6 months of the year.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770684567447/868bdd3a-cb44-47a1-9bd2-d59efcdab6f8.png" alt class="image--center mx-auto" /></p>
<p>But December was my first foray into full-blown agentic TUI goodness.</p>
<p>It started with just one opencode session: create this feature, push and create a draft PR for me to review. But of course, I immediately felt bottlenecked by both the agent’s slowness (tokens per second) and my own code review speed.</p>
<p>So naturally, it evolved to a second opencode session in parallel, working on some other unrelated bug, or helping me plan my next feature. And then a third, and so on.</p>
<p>I was beginning to see the power.</p>
<p>Note that at this stage I’m STILL not plugged in to any MCP, I’ve got the most basic AGENTS.md imaginable, no skills, nothing fancy.</p>
<h2 id="heading-february-2026-now">February 2026 (now)</h2>
<p>I - the human - am well and truly the bottleneck to velocity at this point. Me, and the roadmap. Being the only one thinking about product and also building, my roadmap planning hasn’t been able to keep up with my shipping pace.</p>
<p>I resonate so strongly with <a target="_blank" href="https://x.com/karpathy/status/2004607146781278521">Karpathy’s recent sentiment</a>, that “I have a sense that I could be 10X more powerful if I just properly string together what has become available over the last ~year”.</p>
<p>But, I’m improving.</p>
<p>For example, I’ve now got some MCPs installed, most notably Linear. I’m a huge advocate for async work and trying to keep a single source of truth for company work and projects, and Linear is that place for us. But now, it can also be the source of truth for agents.</p>
<p><strong>My current toolset</strong></p>
<ul>
<li><p>Opencode hooked into work’s ChatGPT Teams (codex 5.3)</p>
</li>
<li><p>Linear Agent for the whole team: issue creation, triaging and delegation within Slack</p>
</li>
<li><p>Cursor agent for assigning small Linear tasks on mobile to create PRs.</p>
</li>
<li><p>Codex CLI <code>/review</code> command for reviews (I get paranoid that the model providers’ harnesses may actually be better, and I’m happy not having a good UX for a single, isolated command a few times a day).</p>
</li>
<li><p>Cursor as my IDE, but purely for reading code and isolated agent refactors. Composer, codex, opus or sonnet-only.</p>
</li>
</ul>
<p>I’ve tried worktrees and hardcore parallelisation. It’s decent, but again, see above: roadmap bottleneck. Maybe a skill issue - stay tuned I guess! I’ve tried the new Codex app too, which does seem to make worktrees a bit more manageable.</p>
<p><strong>Notably: I really like Opencode</strong>. I just hate the idea of committing to something like Claude Code, then someone else comes out with a better model (for instance, right now with Codex), and I’m stuck using something subpar.</p>
<p><strong>On costs</strong></p>
<p>My team already had the ChatGPT team plan, so I just asked for a seat; I think its $25 a month. This, plus the year of Cursor I already paid for is my <strong>only AI spend</strong>. This feels incredibly low to what I see online, but I’m still rarely hitting limits. Maybe I need less meetings :D.</p>
<p>As of right now, the meta has swung to OpenAI and Codex, so that’s who’s getting our money at the moment. Who knows, next month we could all be back to Opus or something else. Related: that’s the beauty of opencode: one TUI, one UX, don’t need to switch tools whenever the new models drop.</p>
<p><strong>On workflow</strong></p>
<p>Our typical flow is now something like this:</p>
<ol>
<li><p>Someone in the company has an idea or request</p>
</li>
<li><p>We tag Linear agent in Slack to create an issue</p>
<ol>
<li>If its a small, well-scoped thing, I’ll assign it to Cursor agent, which will go off, make the change and prepare a GitHub PR for me to review</li>
</ol>
</li>
<li><p>I’ll fire up opencode with Codex 5.3 <code>high</code>, paste the Linear issue URL in and say “help me plan this”</p>
</li>
<li><p>It comes up with a plan, writes it to the Linear issue =&gt; single source of truth that my [future] team could pick up and run with without me</p>
</li>
<li><p>Then: “build it”.</p>
</li>
<li><p>I review. Push PR.</p>
</li>
<li><p>Finally, fire up codex CLI for a <code>/review</code> on <code>xhigh</code></p>
</li>
</ol>
<p>I’m still reviewing everything very closely, still manually testing everything locally, on staging and in production. If the site goes down at 2am, my CEO is calling <strong>me</strong>, not codex ;). So I still feel very much responsible for everything that goes out.</p>
<p>But even then, the trend is heading in the opposite direction, and the pull to let go even more is stronger than ever.</p>
<p><strong>On the codebase</strong></p>
<p>More vibes necessitates more discipline in the codebase.</p>
<p>Your types have to be bomb-proof. You need E2E tests. Your code architecture and organisation has to be solid. You’ll likely want <code>agent-browser</code> .</p>
<p>You gotta do everything you can to make the agent’s life easier. And fortunately, this almost always makes the human’s life easier, too.</p>
<p><strong>On Velocity</strong></p>
<p>I’ve been fairly religiously assigning estimates in Linear (<a target="_blank" href="https://www.productplan.com/glossary/fibonacci-agile-estimation/">Fibonacci</a> estimation: 1, 2, 3, 5. It’s just what we used at GitLab and I liked it).</p>
<p>So I asked codex to look at Linear for all the stuff I’ve shipped in the last month, and estimate how long it would have taken without AI. <strong>2.3x faster it reckons!</strong> Note that I probably only spend 70% of my time building at the moment. Obviously a crude methodology, but likely directionally correct.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770953249725/0f0d059f-4f7f-469c-afe1-99314c4d1483.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-time-is-now">The time is now</h2>
<p>As someone who got into this industry to build useful things, it’s a great time to be a capable software engineer. I can now build stuff and deliver value at least 2x faster, which is a helluva drug.</p>
<p>It will required you to let go a bit. To achieve real gains, you can’t be sweating over every character anymore. Code is a means to an end, not the end itself. Always has been, but now more than ever.</p>
<p>If you have AI tips, please DM me. Let the tokens flow :)</p>
]]></content:encoded></item><item><title><![CDATA[The Quiet Guilt of Coding with AI]]></title><description><![CDATA[Every time AI completes code for me, I feel like a fraud.
We’re in the thick of the “AI is good or bad for code” debate in the software development community. Vibe-coders praise AI for productivity gains, while skeptics caution its use for various, o...]]></description><link>https://tomquirk.me/the-quiet-guilt-of-coding-with-ai</link><guid isPermaLink="true">https://tomquirk.me/the-quiet-guilt-of-coding-with-ai</guid><category><![CDATA[AI]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Tom Quirk]]></dc:creator><pubDate>Tue, 15 Apr 2025 05:06:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/IqSaG9zv2e0/upload/b193f57ac1873b0812192f9f5ff85c15.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every time AI completes code for me, I feel like a <strong><em>fraud</em></strong>.</p>
<p>We’re in the thick of the “AI is good or bad for code” debate in the software development community. Vibe-coders praise AI for productivity gains, while skeptics caution its use for various, often valid, reasons. As someone with a day job and also a handful of side projects, I can relate to both sides, and I’m using Copilot and Cursor on the daily.</p>
<p>But here’s a perspective - a phenomenon - that I don’t see talked about much. <strong>I feel guilty using AI.</strong></p>
<p>I don’t know why, but I feel like I’m cheating. Like I’m doing something illegal, or at least unethical. It’s difficult to articulate, but it just feels <em>wrong</em>.</p>
<p>I don’t mean this in some hyperbolic sense. When AI completes the function I was about to write in one-shot, perfectly, I feel…inadequate. Less-than. In such instances, AI is objectively making me a more productive developer, and the value I can provide to society has increased. I guess I <em>should</em> feel good about this. But it’s leaving a bad taste in my mouth.</p>
<p>Is it pride?</p>
<p>On reflection, I do feel proud that I’m capable of creating something from scratch, of building something “with my bare hands”. In some ways, the AI revolution for programming is similar to the photography revolution. To get a portrait of your family pre-cameras, you’d commission a painter. The painter would attempt to recreate your family on canvas brush and paint over hours, or days, sweating the details to produce a 1-of-1 artwork. A skill that, to do well, requires years of practice.</p>
<p>Now imagine handing the painter a digital camera. Instead of taking hours to produce a single portrait, they can ‘produce’ hundreds of family portraits in an hour.</p>
<p>Objectively, this painter-now-photographer is more efficient at their task of producing family portraits. But to them (or at least some of them), I’d guess they have a similar feeling to me: that using the camera is cheating. That if someone ‘found out’ that they were using a camera, they’d be ridiculed (not the best example, but you get the idea). That one day, they’ll be called upon to create a portrait without a camera, but they’ll have forgotten how to paint. That it betrays the reason they got into painting in the first place.</p>
<p>Is it my education?</p>
<p>The university system - perhaps to a fault - optimises for rote knowledge and the ability to complete tasks under ‘exam conditions’. Back when I was completing my engineering degree, it was common to share assignments to get a collective leg-up and save time. But to pass the course, you had to pass exams, and to pass exams, you had to be able to do stuff ‘by hand’, from scratch - writing algorithms, solving math problems and so on. Maybe I have some latent anxiety from this period, where I’m (unjustifiably) worried that one day AI will ‘go away’, and I’ll be standing there as the senior engineer with no clothes on, unable to write code.</p>
<p>Perhaps this all a realisation that, while AI is a net-positive for software and the world, the act of programming, the profession of software engineering, is evolving to something I just vibe <em>less</em> with. Commanding an AI editor to do stuff around my codebase makes me more efficient at my job, yes. But like the painters of old, I feel guilty that it’s not me putting my brush to canvas.</p>
<p>Clutching on to pre-AI software development approaches is clearly suboptimal. And maybe it’s on me to accept and embrace this new reality to cure my guilt. But maybe there’s something to a little guilt-free coding every once in a while, too.</p>
<p>Do you experience AI guilt?</p>
]]></content:encoded></item><item><title><![CDATA[The biggest sin in software engineering.]]></title><description><![CDATA[I may have found the biggest sin in software engineering. I wonder if you feel the same. It’s not about testing. Not about documentation. Not even about variable naming.
Committing this sin means more bugs. It means a less maintainable codebase. It m...]]></description><link>https://tomquirk.me/the-biggest-sin-in-software-engineering</link><guid isPermaLink="true">https://tomquirk.me/the-biggest-sin-in-software-engineering</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[software development]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[Tom Quirk]]></dc:creator><pubDate>Thu, 14 Jul 2022 03:08:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657352269921/ZwgCfzf85.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I may have found the biggest sin in software engineering. I wonder if you feel the same. It’s not about testing. Not about documentation. Not even about variable naming.</p>
<p>Committing this sin means more bugs. It means a less maintainable codebase. It means longer, lousier code review. And worst of all, it doesn’t <em>feel</em> that bad; it’s an innocent crime and not an obviously terrible idea. But, it is.</p>
<p>The sin, my friends, is big pull requests. </p>
<p>A big PR is the biggest sin in software engineering.</p>
<p><em>Wait!</em> Don’t leave yet. I know, this sounds like another obnoxious Hackernews dev tip. But eliminating big PRs from your life will put you ahead of 90% of software teams.</p>
<p>The remedy to big PRs is, not surprisingly, small PRs (doctors hate him!). But this is easier said than done. Let’s explore the problem of big PRs, and consider strategies for making them smaller.</p>
<h2 id="heading-the-problem-with-big-prs">The problem with big PRs</h2>
<p>As I mentioned, on the surface, big PRs don’t seem sinful. After all, a big PR implies you’ve done a lot of work. Yay for you! But sadly your big PR has three negative side effects:</p>
<ul>
<li>Longer time-to-merge.</li>
<li>More bugs.</li>
<li>Harder maintainability.</li>
</ul>
<p>When it comes to PRs, size matters.</p>
<p>So, how big is too big? Well, 1000+ lines changed, certainly. But I'm skeptical seeing PRs at the 500 lines changed level, too. I'll take a stance: if your PR is above 500 lines changed, it might be too big.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657352395958/hCAMWwdCG.png" alt="Frame 3 (1).png" /></p>
<p>Let's dig into <em>why</em>.</p>
<h3 id="heading-longer-time-to-merge">Longer time-to-merge</h3>
<p>Time-to-merge means what you think it means: it’s the lifespan of your PR. The time it takes for a PR to merge. Time-to-merge is a function of your code review process; the longer the code review, the longer the time-to-merge.</p>
<p>Our goal should be to get time-to-merge as low as possible. 7 days is okay. 2 days or less is best.</p>
<p>If you have PRs open for longer than 2 weeks, you have a big problem. And that problem is likely that your PRs are too big.</p>
<p>The longer a PR stays open, the longer it takes for your user to get the feature. Obviously. What’s less clear is that the quicker you can get code into production, the better. Every dev learns that code takes on another life once it’s in production. And there’s only one way to know how your code will perform in production: ship it.</p>
<p>Big PRs tend to stay open for longer. For one, it’s harder to keep them up to date with the main branch. Not much worse than rebasing a massive PR that’s a week out of date. You know the feeling!</p>
<p>Beyond merge hell, review cycles tend to be longer. </p>
<p>As a code reviewer, you’ll spend more time reviewing big PRs. You need more focus. And you’ll leave more comments, of lower quality, leading to more back-and-forth with the PR author. Reviewing a big PR throws your whole day off.</p>
<p>All told, big PRs result in wasted time. I hesitate to use the word “waste”. But given there is a remedy (smaller PRs), this is time you can save.</p>
<h3 id="heading-more-bugs">More bugs</h3>
<p>Imagine you’re reviewing a PR with 1000 lines changed across 23 files. How <em>good</em> is your code review going to be?</p>
<p>Be honest. Your code review will suck. You’ll catch fewer bugs, leave less instructive feedback, and be less patient with the author.</p>
<p>Nobody likes reviewing big PRs. So why would they yield a good review?</p>
<p>On the flip side, reviews on smaller PRs are almost always better reviews. The code review is no longer an overwhelming task. You’ll be more inclined to dig deeper into the code, more inclined to test the feature, and far more likely to find bugs.</p>
<p>The same principle applies to the PR author. A big PR means you’re keeping a lot of context in your head at once. We all like to think we’re good enough to do this, but we aren’t. Smaller PRs reduce the scope of the problem. With smaller PRs, you’ll take more time with code quality. You’ll introduce fewer bugs. And you’ll build better software.</p>
<h3 id="heading-poorer-maintainability">Poorer maintainability</h3>
<p>Ever tried reverting a big PR? It’s not fun.</p>
<p>The more code introduced in a single PR, the harder it is to revert.</p>
<p>And remember how big PRs yield worse code reviews? Well, that’s going to affect the maintainability of your codebase.</p>
<p>What about how it’s harder to author a <em>good</em> big PR? Maintainability suffers here, too.</p>
<h2 id="heading-create-smaller-prs">Create smaller PRs</h2>
<p>So, create smaller PRs! There are two approaches I’ve seen work well: feature flags, and stacked diffs (or chained PRs).</p>
<h3 id="heading-use-feature-flags">Use feature flags</h3>
<p>When I learned how to use feature flags in development, I felt like I was unlocked.</p>
<p>A feature flag is a toggle you use to show or hide a feature in your app. Don’t want to release a feature to users yet? Put it behind a feature flag until it’s ready.</p>
<p>Feature flags can be as simple as boolean items in local storage, or as complex as using a third-party SaaS. Choose whatever your team will actually use.</p>
<p>“But Tom!”, you might say. “Why not simply have a feature branch? We can create smaller PRs that target the feature branch, and when the feature is ready, we merge it to <code>main</code>!”.</p>
<p>Many teams do this. But if big PRs are the biggest sin in software, then long-lived feature branches are the runner-up. Long-lived feature branches suffer all the same problems as big PRs. It’s painful to keep them in sync with the main branch. Your code stays out of production for longer, so you might get a big surprise when you finally merge it.</p>
<p>Feature flags solve this problem. Add a feature flag, and start merging small PRs behind the feature flag. When the feature is ready, turn it in and give it a test. When you’re confident in the feature, remove the feature flag.</p>
<p>Let’s say you had to implement the following feature:</p>
<blockquote>
<p>When the user enters a value and clicks “Add Juice”, open a confirmation modal.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657352299928/l2--s3xL2.png" alt="Frame 2.png" /></p>
<p>You could build the whole thing in one go. But then you’d end up with a 1000+ line PR. And now we know, big PRs are a problem.</p>
<p>Instead, you could build this behind a feature flag. In your first PR, you’d add the feature flag, along with code for the input field and the “Add Juice” button. The feature <strong>isn’t done</strong> yet; clicking the button doesn’t do anything! But that’s okay because we’re using a feature flag. Then in the second PR, you’d add the code for the modal. You might even have a third PR to refine the UX further.</p>
<p>Instead of a thick 1000+ line PR, you’ve shipped 3 small PRs. Woot!</p>
<p>Feature flags are your friend. </p>
<h3 id="heading-stack-your-prs">Stack your PRs</h3>
<p>Sometimes feature flags are overkill. Sometimes all you need is to stack your PRs.</p>
<p>Stacking, or chaining PRs is what it sounds like: instead of one big PR, you could have three small PRs linked in a chain. The third PR depends on the second, and the second PR depends on the first.</p>
<p>This works well in situations where you need to refactor a bunch of code before building your feature. Instead of one big PR that includes the refactor and the feature, create one PR that only includes the refactor, and a second PR that includes the feature.</p>
<p>This way, each PR is reviewed and tested independently. Your time-to-merge will certainly be shorter, and hopefully, you’ve received a better code review.</p>
<p>If you’re really lucky, you might not even have to chain the PRs. If you can find a way to break up your big PR into multiple independent chunks, then you have achieved mastery. Pass your on-call shift to someone else!</p>
<hr />
<p>Big PRs are a problem. They increase time-to-merge and lead to a buggier and less maintainable product. Do yourself and your code reviewers a favor. Create smaller PRs, with the help of feature flags and stacked PRs. Sometimes you can’t get around creating a big meaty PR. No worries. It just means you owe your team a small PR next time.</p>
<p>I tweet about building software in web3 and crypto - follow me over there: https://twitter.com/aeolianeth</p>
]]></content:encoded></item></channel></rss>