<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Craft — Lakshmi Narasimhan</title><link>https://lakshminp.com/tags/craft/</link><description>I help developers build, deploy, and distribute their SaaS without hiring a team. Long-running notes on systems, AI internals, Carnatic music, fiction craft, and whatever else collides interestingly.</description><generator>Hugo + lakshminp theme</generator><language>en-us</language><lastBuildDate>Tue, 10 Mar 2026 00:00:00 +0000</lastBuildDate><managingEditor>Lakshmi Narasimhan</managingEditor><webMaster>Lakshmi Narasimhan</webMaster><copyright>© 2026 Lakshmi Narasimhan</copyright><atom:link href="https://lakshminp.com/tags/craft/feed.xml" rel="self" type="application/rss+xml"/><item><title>Anthropic Is Losing Money on You Every Month. What Are You Shipping?</title><link>https://lakshminp.com/2026/03/ai-subsidy-window-developers/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/03/ai-subsidy-window-developers/</guid><category>essays</category><category>craft</category><description>I do this thing at the end of every month where I look at my Claude usage stats and feel mildly guilty.
Not guilty enough to stop, obviously. But guilty in the way you feel when you’ve been eating at a nice restaurant and you suddenly realize your friend with the expense account has been covering all of it. You’d have ordered differently if you knew that at the start.</description><content:encoded><![CDATA[<p>I do this thing at the end of every month where I look at my Claude usage stats and feel mildly guilty.</p>
<p>Not guilty enough to stop, obviously. But guilty in the way you feel when you’ve been eating at a nice restaurant and you suddenly realize your friend with the expense account has been covering all of it. You’d have ordered differently if you knew that at the start.</p>
<p>Here’s what I know: I pay $200/month for Claude Max. Based on what I actually do with it — multi-hour Claude Code sessions, agents running in parallel, research deep-dives, content pipelines chewing through tokens like a hungry golden retriever — the API-rate equivalent of my usage is somewhere between $600 and $900. Every month.</p>
<p>Anthropic is losing money on me. On you. On every developer who’s turned this into a real part of how they build.</p>
<p>This isn’t an accident. This is the plan. And it has an expiration date.</p>
<h1 id="the-hemorrhage-is-real"><strong>The Hemorrhage Is Real</strong></h1>
<p>I was reading Sebastian Raschka’s <em><a href="https://www.manning.com/books/build-a-large-language-model-from-scratch" rel="external nofollow noopener" class="lnp-link">Build a Large Language Model from Scratch</a></em> last week and stumbled into a footnote that sent me down a rabbit hole. He cites Lambda Labs: it would take 355 years to train GPT-3 on a single V100 datacenter GPU. On a consumer RTX 8000: 665 years.</p>
<p>I know, I know — “but they use thousands of GPUs in parallel.” Yes. And those thousands of GPUs cost tens of millions of dollars for a single training run. That’s before we talk about the ongoing cost of serving that model to every user who hits the API every day. Training is the capital expenditure. Inference — every time you actually use Claude — is the operating cost. I’m talking about the second thing. Both are obscene.</p>
<p>Let’s look at what’s actually happening, because the numbers are — and I say this as someone who’s seen a lot of startup math — genuinely unhinged.</p>
<p>OpenAI’s revenue went from $3.7 billion in 2024 to over $20 billion ARR by end of 2025. Ten times in two years. Sounds like they’ve figured it out. Except their own internal projections show losses of $14 billion in 2026 — against $13 billion in revenue. The revenue explodes. The costs explode faster. Microsoft has put in $13 billion. SoftBank committed $41 billion across various tranches. A 2026 funding round valued the company at $730 billion. None of this is profit. All of it is gap-filling.</p>
<p>Anthropic is nearing $20 billion in annualized revenue as of early 2026 — up from $1 billion at the start of 2025. Google has put in over $3 billion in equity, plus a cloud infrastructure deal described as “tens of billions” in compute. Amazon has committed $8 billion. The Series G closed at a $380 billion valuation. These are not investments in a profitable business. These are bets on essential infrastructure, placed by people who are terrified of the alternative.</p>
<p>Google’s own AI division is entirely subsidized by search advertising. They watched OpenAI nearly disrupt their core business and decided that losing money on AI is preferable to losing the company. You can’t really argue with the logic. You can appreciate that the logic benefits you.</p>
<p>Here’s what makes this particularly strange: the more usage grows, the worse the unit economics get. OpenAI’s gross margins collapsed from roughly 40% to 33% in 2025 because inference costs quadrupled as usage scaled. They’re getting less efficient per dollar as they get bigger. The burn isn’t winding down. It’s accelerating.</p>
<p>They’re all playing the same game — lose money now, win the market, figure out profitability later. You’ve seen this movie. AWS subsidized startups through aggressive discounting from 2008-2015 and built the most profitable cloud business in history. Uber burned billions subsidizing rides below cost for seven years. Every streaming service ran at a loss from 2015-2022 while racing to lock in subscribers before the music stopped.</p>
<p>The pattern: 5-8 years of heavy subsidies. Prices normalize. The land grab ends. Survivors optimize for margin.</p>
<p>AI is somewhere in year 3-4 of this cycle.</p>
<h1 id="why-theyre-subsidizing-you-specifically"><strong>Why They’re Subsidizing You Specifically</strong></h1>
<p>Here’s the part most people miss.</p>
<p>It’s not just the gym membership model — yes, light users subsidize heavy users across the subscriber base. But for developers specifically, you serve a purpose that goes way beyond the math:</p>
<p><strong>You evangelize.</strong> Every blog post about Claude Code, every Hacker News comment about your workflow, every Slack recommendation to a colleague — that’s marketing no ad budget can replicate. Authentic practitioner enthusiasm is worth more than a campaign, and they get it from you for free.</p>
<p><strong>You’re the top of the enterprise funnel.</strong> The conversion path goes: you try Pro, you love it, you build something real, you show your team, your team shows leadership, leadership signs a $500K enterprise contract. That single deal is worth 2,500 Max subscribers. You’re not where the money is. You’re where the money comes from.</p>
<p><strong>You stress-test the product.</strong> Power users find the edges. You file the bug reports casual users never hit. This feedback loop is genuinely expensive to replicate through formal QA — and you’re doing it gratis.</p>
<p><strong>You build the ecosystem.</strong> Tutorials, repos, guides, courses. The content that helps a thousand other developers get value from the product? That’s unpaid work you’re doing for their platform.</p>
<p>You are, in the most literal sense, being paid for this in subsidized compute. It’s a trade. The question is whether you’re getting the better end of it.</p>
<p>(You are. Obviously. That’s the point.)</p>
<h1 id="how-long-does-the-window-stay-open"><strong>How Long Does the Window Stay Open?</strong></h1>
<p>Nobody knows. Anyone giving you a specific timeline is guessing, including me.</p>
<p>But the runway math doesn’t matter as much as the signals. Watch these:</p>
<p><strong>Usage limits tightening.</strong> Already happening. “Unlimited” has gotten more creative in its definition. Rate limits appear. Fair use policies materialize. You’ve noticed.</p>
<p><strong>Tier restructuring.</strong> The free tier gets worse. The basic tier gets capped. The premium tier develops features that used to be standard. The ladder shifts.</p>
<p><strong>API price changes.</strong> When enterprise revenue is strong enough to sustain the business, the argument for subsidizing consumers weakens. Check the API pricing page periodically.</p>
<p><strong>Enterprise-only features.</strong> When the best capabilities start requiring a sales call, the consumer product is no longer the growth driver.</p>
<p>My working model: 18-24 months of relatively stable economics. After that, genuine uncertainty.</p>
<p>The open-source wildcard could extend the window or change what “subsidized” even means. The gap between frontier models and the best open-weight models has compressed dramatically — we’re talking 6-12 months behind the frontier now, versus the 18-24 months people were citing a year ago. Running genuinely capable models locally on a Mac is already real, not theoretical. That’s a hedge against pricing pressure, but it doesn’t change the core argument. It just means the floor is higher than it was.</p>
<p>Either way: cheap access to frontier AI while the models keep getting dramatically better is the thing with the uncertain timeline. Don’t wait for a clear signal. By the time the signal is clear, the window is already closing.</p>
<h1 id="what-you-should-actually-be-building"><strong>What You Should Actually Be Building</strong></h1>
<p>This is where I have to resist the urge to give you a twenty-point tactical playbook. (I’m saving that for a separate post. Watch for it.)</p>
<p>The mental model is simple: use subsidized tools to build assets you own. Don’t just consume. Create.</p>
<p>For developers building SaaS, this means a few things specifically:</p>
<p><strong>Ship the MVP, not the perfect version.</strong> Claude Code does 70% of the implementation and you do the system design and judgment calls. A SaaS MVP that would have taken three months solo two years ago takes a weekend now. These economics are extraordinary and they will not last forever. The price of “wait until it’s ready” is time you don’t have.</p>
<p><strong>Build the content moat before everyone else does.</strong> Technical guides, deep-dives, tutorials on topics you actually know. This content ranks before your competitors get around to writing theirs. The window for content arbitrage — where AI-assisted quality beats raw human output at volume — is also temporary. The ones who started in 2025-2026 will own the long-tail traffic. The rest will write for audiences that already exist.</p>
<p><strong>Develop taste.</strong> This is the skill that survives every model improvement and every price normalization. Knowing whether AI output is actually good — whether the code is maintainable, whether the architecture makes sense, whether the essay says something real — is something that cannot be automated. It gets more valuable as AI gets cheaper. Invest in it.</p>
<p><strong>Build the audience.</strong> Newsletter subscribers, people who trust your recommendations, readers who show up when you publish. This is the asset that persists regardless of what happens to model pricing. You’re not renting audience from Anthropic. You own it.</p>
<p>The math I keep returning to: 18 months of focused effort with subsidized AI tools could produce 3-5 years of normal-pace output. The SaaS you’ve been procrastinating? You could ship three of them. The content backlog? Gone. The technical course based on your experience? Done.</p>
<p>That compounds. The skills sharpen. The audience grows. By the time pricing normalizes, you’ve already built the moat.</p>
<h1 id="the-only-question-that-matters"><strong>The Only Question That Matters</strong></h1>
<p>You pay 200/month.You′regetting200/<em>month</em>.<em>You</em>′<em>regetting</em>600-900/month in value. That arbitrage exists right now, today.</p>
<p>But the real arbitrage isn’t the monthly spread. It’s what you build during the window.</p>
<p>The people who win this period aren’t the ones who used Claude for the most impressive demo or the most clever prompt chain. They’re the ones who used cheap frontier AI access to build products, audiences, and content that persist after the subsidies end.</p>
<p>So: what are you shipping?</p>
<p>The clock’s running.</p>
<p><em>This post started as a rabbit hole triggered by a paragraph in Sebastian Raschka’s <a href="https://www.manning.com/books/build-a-large-language-model-from-scratch" rel="external nofollow noopener" class="lnp-link">Build a Large Language Model from Scratch</a> (Manning). His Substack is obviously worth following: <a href="https://substack.com/@rasbt" rel="external nofollow noopener" class="lnp-link">@rasbt</a>.</em></p>
]]></content:encoded></item><item><title>How to Escape the SRE Meeting-Industrial Complex</title><link>https://lakshminp.com/2026/02/sre-meeting-overload/</link><pubDate>Wed, 11 Feb 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/02/sre-meeting-overload/</guid><category>essays</category><category>craft</category><description>Monday morning. I opened Slack at 8:30. By 8:47 I had four meeting invites: a standup, a sync, a pre-mortem for a system that hasn’t broken yet, and a “quick alignment call” about the alignment call we had on Friday.
By 11am I’d spent two and a half hours talking about reliability. I’d spent zero hours improving it.
Someone on r/devops posted this week: “My team should be renamed to TalkOps.” Ninety-nine percent upvote ratio. Every SRE on the planet felt that in their chest.</description><content:encoded><![CDATA[<p>Monday morning. I opened Slack at 8:30. By 8:47 I had four meeting invites: a standup, a sync, a pre-mortem for a system that hasn’t broken yet, and a “quick alignment call” about the alignment call we had on Friday.</p>
<p>By 11am I’d spent two and a half hours talking about reliability. I’d spent zero hours improving it.</p>
<p>Someone on r/devops posted this week: “My team should be renamed to TalkOps.” Ninety-nine percent upvote ratio. Every SRE on the planet felt that in their chest.</p>
<h1 id="the-meeting-industrial-complex"><strong>The Meeting-Industrial Complex</strong></h1>
<p>Here’s what happens in platform engineering and SRE orgs at scale. The work is invisible. Nobody sees the deployment pipeline until it breaks. Nobody notices the monitoring until it doesn’t fire. The only proof that your team exists is&hellip; meetings.</p>
<p>So meetings multiply. Not because they’re useful, but because they’re visible. Your manager needs to justify headcount. Your team needs to “align” with five other teams. Every incident spawns a post-mortem, every post-mortem spawns action items, every action item spawns a planning meeting, and the planning meeting spawns a follow-up sync to check on the action items from the post-mortem about the incident that happened because nobody had time to do deep work because they were in too many meetings.</p>
<p>The recursion is beautiful, in a terrible way.</p>
<h1 id="deep-work-is-the-actual-product"><strong>Deep Work Is the Actual Product</strong></h1>
<p>I’ve been a Principal SRE for long enough to have an opinion that would get me in trouble at most companies: <strong>most reliability improvements happen in 2-hour blocks of uninterrupted focus, not in 30-minute standups.</strong></p>
<p>The monitoring rule that catches the subtle memory leak? That took a quiet afternoon staring at Grafana dashboards. The deployment pipeline fix that cut rollback time from 20 minutes to 90 seconds? That was a Saturday morning when Slack was silent.</p>
<p>The real work — the stuff that actually moves your error budget in the right direction — requires the kind of concentration that evaporates the instant someone says “can I get 15 minutes?”</p>
<p>Fifteen minutes is never fifteen minutes. It’s five minutes of context-switching in, fifteen minutes of meeting, and thirty minutes of trying to remember what you were doing before the meeting. That’s fifty minutes gone for fifteen minutes of “alignment.”</p>
<h1 id="the-side-project-tax"><strong>The Side Project Tax</strong></h1>
<p>Here’s where it gets personal. If you’re an SRE who also builds on the side — SaaS, open source, writing, whatever — the meeting-industrial complex doesn’t just eat your work day. It eats your creative energy.</p>
<p>I leave my day job some days having produced nothing but words. Spoken words. Words in Zoom calls. Words in Slack threads about Zoom calls. By the time I sit down to work on my own projects, my brain is cooked.</p>
<p>Not tired-from-solving-hard-problems cooked. Tired-from-performing-productivity cooked. There’s a difference. One is the good kind of exhaustion. The other is the kind where you stare at your side project and think “I’ll just do this tomorrow” for the 47th consecutive day.</p>
<p>The cruel irony: the skills that make you good at SRE — systems thinking, pattern recognition, automation instinct — are exactly the skills you need for building products. But TalkOps burns through your cognitive budget before you can apply those skills to anything that’s actually yours.</p>
<h1 id="fighting-back-without-getting-fired"><strong>Fighting Back (Without Getting Fired)</strong></h1>
<p>You can’t just decline every meeting. I’ve tried. People notice. “Not a team player” shows up in your review. The trick is strategic visibility reduction.</p>
<h2 id="1-the-async-post-mortem"><strong>1. The Async Post-Mortem</strong></h2>
<p>Most post-mortems don’t need a meeting. They need a document. Write the timeline, the root cause, the action items. Share it. Let people comment asynchronously. Reserve the live meeting for cases where there’s genuine disagreement about the fix.</p>
<p>I started doing this two years ago. Saved roughly 3 hours a week. Nobody complained. Several people thanked me.</p>
<h2 id="2-the-office-hours-model"><strong>2. The Office Hours Model</strong></h2>
<p>Instead of being available for “quick syncs” all day, block two hours for office hours. “Need my input? Come between 2-4pm Tuesday and Thursday.” Outside those hours, I’m heads-down. Slack messages get a response within 4 hours, not 4 minutes.</p>
<p>This feels rude until you realize that every senior engineer at a company like Google does exactly this. They just don’t announce it.</p>
<h2 id="3-the-1-page-rfc"><strong>3. The 1-Page RFC</strong></h2>
<p>Half the planning meetings exist because nobody wrote down what they want to build. A 1-page RFC — problem, proposed solution, tradeoffs, timeline — kills 3 meetings. Write it before the meeting gets scheduled. Share it. Cancel the meeting. “I think the RFC covers it. Drop comments if anything’s unclear.”</p>
<h2 id="4-protect-your-first-2-hours"><strong>4. Protect Your First 2 Hours</strong></h2>
<p>No meetings before 10:30. Not negotiable. Those first morning hours are when your brain is sharpest. Using them for standups is like using a surgical laser to heat soup.</p>
<p>If your standup is at 9am, that’s not a standup. That’s a productivity assassination. Push for async standups (Slack bots work fine) or at least move it to after lunch when everyone’s already in low-focus mode.</p>
<h2 id="5-make-the-work-visible-without-meetings"><strong>5. Make the Work Visible Without Meetings</strong></h2>
<p>The root cause of TalkOps is invisible work. Fix the visibility problem and you fix the meeting problem.</p>
<p>Weekly automated reports. Dashboards in shared channels. Monthly “here’s what platform eng shipped” newsletters. Make the work visible on your terms, in your format, on your schedule. If people can see what you’re doing, they stop scheduling meetings to ask.</p>
<h1 id="the-real-output"><strong>The Real Output</strong></h1>
<p>Every hour you reclaim from TalkOps is an hour you can spend on actual reliability work. Or actual side project work. Or actual thinking, which is the scarcest resource in any engineering organization.</p>
<p>The r/devops thread had a comment that stuck with me: “By the time I get a quiet hour, I’m already drained.”</p>
<p>That’s not a scheduling problem. That’s a systems problem. And if there’s one thing SREs should be good at, it’s fixing systems.</p>
<p>Start with your own calendar.</p>
]]></content:encoded></item><item><title>Open Source Is Starving While AI Makes Coding Free</title><link>https://lakshminp.com/2026/02/open-source-ai-coding-crisis/</link><pubDate>Tue, 03 Feb 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/02/open-source-ai-coding-crisis/</guid><category>essays</category><category>craft</category><description>Developer costs are plummeting toward zero. AI coding agents can scaffold an app in minutes. A solo founder with Claude can ship what used to take a team of five.
And yet, open source is in crisis.
Maintainers are burning out at record rates. Critical infrastructure projects survive on the goodwill of one or two exhausted volunteers. The xz backdoor wasn’t an anomaly — it was a symptom of a system running on fumes. The “one random person in Nebraska” meme stopped being funny years ago.</description><content:encoded><![CDATA[<p>Developer costs are plummeting toward zero. AI coding agents can scaffold an app in minutes. A solo founder with Claude can ship what used to take a team of five.</p>
<p>And yet, open source is in crisis.</p>
<p>Maintainers are burning out at record rates. Critical infrastructure projects survive on the goodwill of one or two exhausted volunteers. The xz backdoor wasn’t an anomaly — it was a symptom of a system running on fumes. The “one random person in Nebraska” meme stopped being funny years ago.</p>
<p>We have the cheapest labor in the history of software, and the projects that hold up the internet are still starving for contributors.</p>
<p>How?</p>
<h1 id="the-founding-myth"><strong>The Founding Myth</strong></h1>
<p>In 1997, Eric Raymond published <em>The Cathedral and the Bazaar</em>, the essay that became open source’s origin story. The argument was simple: software built like a cathedral — centrally planned, tightly controlled, released when perfect — loses to software built like a bazaar — messy, open, iterated in public by a swarm of contributors.</p>
<p>Linux beat the cathedral. The bazaar won. Raymond’s most famous line became gospel: “Given enough eyeballs, all bugs are shallow.”</p>
<p>Twenty-nine years later, the eyeballs are disappearing. The bazaar is starving. And a new kind of cathedral has risen — one Raymond never imagined.</p>
<h1 id="cheap-labor-doesnt-flow-to-maintenance"><strong>Cheap Labor Doesn’t Flow to Maintenance</strong></h1>
<p>Here’s the disconnect: AI-generated labor isn’t flowing to maintenance. It’s flowing to creation.</p>
<p>Vibe coding doesn’t fix bugs in abandoned logging libraries. It generates new apps. Agents build what they’re told, and nobody tells them “go triage issues on this unglamorous project that 40,000 packages depend on.”</p>
<p>Raymond’s bazaar worked because contributors had intrinsic motivation — they scratched their own itch. Agents don’t have itches. They have prompts.</p>
<p>The result: an explosion of new software built on a foundation that’s slowly rotting.</p>
<h1 id="linuss-law-is-breaking"><strong>Linus’s Law Is Breaking</strong></h1>
<p>Raymond’s key insight was that open development creates a natural immune system. Bugs get caught because many eyes are watching. The bazaar is self-correcting.</p>
<p>This assumed two things that were true in 1997 and are increasingly false today:</p>
<p><strong>First, that someone wrote the code.</strong> Vibe-coded software often has no human author who deeply understands it. The person who prompted it into existence may not be able to read it. The “author” is a model trained on the commons, producing plausible-looking code that works until it doesn’t. I’ve <a href="https://lakshminp.substack.com/p/claude-code-is-incredible-it-also" rel="external nofollow noopener" class="lnp-link">written about this failure mode</a> — code that compiles, passes tests, and is subtly, catastrophically wrong.</p>
<p><strong>Second, that someone reads the code.</strong> Open source review depends on humans who care enough to look. But when code is generated at machine speed, the review bottleneck becomes catastrophic. Maintainers are already drowning in AI-generated pull requests — superficially clean, structurally hollow. The immune system is being overwhelmed not by attackers, but by well-meaning slop.</p>
<p>“Given enough eyeballs, all bugs are shallow” only works if the eyeballs are open.</p>
<h1 id="the-new-cathedral"><strong>The New Cathedral</strong></h1>
<p>Raymond’s cathedral was Microsoft. Proprietary, closed, top-down. The bazaar beat it because openness was a structural advantage — more contributors, faster iteration, better feedback loops.</p>
<p>But look at what the bazaar runs on today.</p>
<p>Every vibe coder, every AI-assisted open source contributor, every agent spinning up code in a terminal — they’re all downstream of foundation models built inside the most cathedral-like institutions imaginable. Anthropic, OpenAI, Google DeepMind — these are cathedrals that would make 1990s Microsoft blush. Billions in compute, proprietary training data, closed weights, trade secrets wrapped in safety rhetoric.</p>
<p>The bazaar didn’t defeat the cathedral. It moved in upstairs.</p>
<p>Open source in 2026 means building with tools you can’t inspect, trained on data you can’t audit, controlled by companies whose incentives you can’t verify. The irony would make Raymond’s head spin: the most “open” era of software creation runs entirely on the most closed infrastructure ever built.</p>
<h1 id="vibe-coding-raymonds-dream-or-nightmare"><strong>Vibe Coding: Raymond’s Dream or Nightmare?</strong></h1>
<p>“Release early, release often.” Raymond preached this as the bazaar’s core advantage. Vibe coding takes it to its logical extreme — release in minutes, iterate in seconds, ship before lunch.</p>
<p>But Raymond’s version had a crucial qualifier nobody quotes: rapid releases were supposed to come with <em>listening to your users</em>. The feedback loop was the point. Ship fast so you can learn fast.</p>
<p>Vibe coding often skips the loop. Ship fast because shipping is easy. If it breaks, generate a new one. Software becomes disposable. Why debug when you can re-prompt?</p>
<p>This creates a bizarre inversion. The original bazaar was messy but <em>convergent</em> — many contributors pushing toward better software over time. The vibe-coded bazaar is messy and <em>divergent</em> — infinite forks, infinite rewrites, nothing accumulating into lasting infrastructure.</p>
<p>Raymond imagined a thousand people improving one thing. We got one person generating a thousand things.</p>
<h1 id="does-open-source-even-matter-the-same-way"><strong>Does Open Source Even Matter the Same Way?</strong></h1>
<p>Here’s the uncomfortable question: if anyone can vibe-code a replacement for your library in an afternoon, what does “open source” even mean?</p>
<p>The traditional argument was access. You shouldn’t have to pay Microsoft for a compiler. You shouldn’t be locked into Oracle’s database. Open source was freedom from vendor dependence.</p>
<p>But when the vendor is an AI model and the product is generated on demand, the bottleneck shifts. You’re not locked into specific software — you’re locked into the <em>capability to generate software</em>. The dependency moved up a layer of abstraction.</p>
<p>Open source used to mean: “here’s the code, do what you want.” The new version might mean: “here’s the model weights, do what you want.” And by that standard, most of the AI industry is firmly in cathedral territory.</p>
<h1 id="what-raymond-got-right-that-still-holds"><strong>What Raymond Got Right (That Still Holds)</strong></h1>
<p>It’s tempting to write the obituary for the bazaar. Don’t.</p>
<p>Raymond’s deepest insight wasn’t about code — it was about <em>coordination</em>. The bazaar demonstrated that loose networks of motivated people could outperform rigid hierarchies. That insight is more relevant than ever.</p>
<p>The projects that will thrive in the agent era won’t be the ones with the most AI-generated PRs. They’ll be the ones that figure out how to coordinate human judgment with machine labor. Someone still has to decide what’s worth building. Someone still has to say “this PR is slop, reject it.” Someone still has to maintain taste.</p>
<p>The bazaar’s immune system isn’t dead — it just needs to evolve. Instead of “many eyes on the code,” we need “many minds on the direction.” Maintainers become curators. Contributors become reviewers. The scarce resource isn’t writing code anymore. It’s knowing which code to keep.</p>
<p>Raymond was right that openness wins. He was right that central planning can’t compete with distributed intelligence. He was right that scratching your own itch produces better software than building to spec.</p>
<p>He just couldn’t have predicted that the itch would be scratched by a machine that doesn’t know what itching feels like.</p>
<p><em>The Cathedral and the Bazaar assumed humans on both sides of the screen. We’re entering an era where that assumption breaks down. The principles survive. The implementation needs an upgrade.</em></p>
<p><strong>What do you think — is the bazaar adapting or dying? Reply and tell me.</strong></p>
]]></content:encoded></item><item><title>Software Engineering Is Dead, or Is It?</title><link>https://lakshminp.com/2026/01/software-engineering-dead/</link><pubDate>Tue, 27 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/software-engineering-dead/</guid><category>essays</category><category>craft</category><description>Everyone said agentic coding would kill software engineering discipline. Turns out it killed the wrong disciplines.
Clean code? Dead. Nobody’s hand-crafting variable names when Claude generates 500 lines in 30 seconds. But TDD, specs-driven development, domain-driven design — the stuff we used to skip because it felt like ceremony? That’s the load-bearing wall now. Tear it out and the whole thing collapses.
TDD: The Cache That Wasn’t I had Claude Code build me a Redis caching module. Proper TTLs. Cache invalidation on writes. Unit tests passing. Beautiful, elegant, chef’s-kiss code.</description><content:encoded><![CDATA[<p>Everyone said agentic coding would kill software engineering discipline. Turns out it killed the <em>wrong</em> disciplines.</p>
<p>Clean code? <a href="https://lakshminp.substack.com/p/clean-code-is-dead-long-live-clean" rel="external nofollow noopener" class="lnp-link">Dead</a>. Nobody’s hand-crafting variable names when Claude generates 500 lines in 30 seconds. But TDD, specs-driven development, domain-driven design — the stuff we used to skip because it felt like ceremony? That’s the load-bearing wall now. Tear it out and the whole thing collapses.</p>
<h2 id="tdd-the-cache-that-wasnt"><strong>TDD: The Cache That Wasn’t</strong></h2>
<p>I had Claude Code build me a Redis caching module. Proper TTLs. Cache invalidation on writes. Unit tests passing. Beautiful, elegant, chef’s-kiss code.</p>
<p>One problem. The actual query functions never called the caching layer.</p>
<p>Hundreds of requests later, I checked Redis. Empty. A pristine, untouched Redis instance, sitting there like a museum exhibit. <a href="https://lakshminp.substack.com/p/claude-code-is-incredible-it-also" rel="external nofollow noopener" class="lnp-link">I’ve written about these failure patterns before</a> — this one hurt the most.</p>
<p>Integration tests would have caught it. But only if I’d written them <em>first</em>. That’s the part everyone skips — writing the verification before the implementation. TDD forces you to define “done” before the agent starts building. Without it, you get beautiful isolated components that nobody wired together.</p>
<p>This isn’t hypothetical. An r/programming thread (894 upvotes) nailed it: “We’re getting correct code, but not right code.” One reviewer found AI-generated Java using the default ForkJoinPool for I/O-bound tasks. Compiles fine. Passes unit tests. Catastrophic under load.</p>
<p>My favorite was the “chief architect” who generated “full coverage” unit tests with Copilot. Duplicate asserts. Unused service constructions. Tests that passed but tested nothing. A green CI pipeline that was essentially a participation trophy.</p>
<p>TDD isn’t ceremony anymore. It’s the spec your agent actually follows.</p>
<h2 id="specs-driven-development-the-authentication-amnesia"><strong>Specs-Driven Development: The Authentication Amnesia</strong></h2>
<p>I spent two weeks pair-programming authentication with Claude Code. We tracked race conditions together. Debated RS256 vs HS256. Built a shared understanding of every edge case.</p>
<p>Then compaction hit.</p>
<p>“Where did we leave off?”</p>
<p>“I don’t have information about previous sessions.”</p>
<p>Two weeks of context. Gone. My <a href="http://todo.md/" rel="external nofollow noopener" class="lnp-link">TODO.md</a> became a graveyard of cryptic notes that made sense to exactly nobody, including me three days later. <a href="https://lakshminp.substack.com/p/why-your-ai-wakes-up-every-morning" rel="external nofollow noopener" class="lnp-link">I wrote the full horror story here</a>.</p>
<p>So I started using a git-backed issue tracker with dependency graphs that persists across agent sessions. Sprints and epics stopped being PM ceremony and became the agent’s memory. The control plane for multi-session work.</p>
<p>The pattern scales beyond my personal disasters. An r/programming post titled “The era of AI slop cleanup has begun” (4,200 upvotes) described a freelancer who keeps getting hired to fix AI-generated codebases. “It mostly works, but does so terribly.” The missing ingredient every single time: no structured planning, no phased delivery. Just vibes and a prompt.</p>
<p>Fred Brooks said it decades ago, and r/ExperiencedDevs rediscovered it (1,400 upvotes): “Once requirements are fully expressed, their information content is fixed. You can change surface syntax, but you can’t compress semantics.”</p>
<p>You can’t skip the thinking. You can only skip writing it down — and then you pay for it later when your agent wakes up with amnesia.</p>
<h2 id="ddd-the-firewall-agents-cant-generate"><strong>DDD: The Firewall Agents Can’t Generate</strong></h2>
<p>Here’s a <a href="https://reddit.com/r/programming/comments/1nxobte/the_phantom_author_in_our_codebases_why/" rel="external nofollow noopener" class="lnp-link">Reddit thread</a> that lives in my head rent-free. Someone described the “Phantom Author” problem — only domain experts catch the subtle flaws agents produce. The code compiles. The tests pass. The logic is plausible. But it’s <em>wrong</em> in ways only someone who understands the domain would notice.</p>
<p>The punchline: “Ironically the only people who should be using AI are people who are already experts.”</p>
<p>Bounded contexts — the core DDD concept — are the firewall. They tell the agent where one domain ends and another begins. Without that modeling, agents connect everything to everything. Your billing module knows about your notification preferences. Your auth layer has opinions about your recommendation engine.</p>
<p>Agents can’t generate domain boundaries because domain boundaries come from understanding the business, not the code. That’s your job. The agent’s job is everything inside the boundary.</p>
<h2 id="the-punchline"><strong>The Punchline</strong></h2>
<p>The disciplines that survived aren’t the ones that made code pretty. They’re the ones that tame complexity.</p>
<p>TDD tells the agent what “done” means. Specs give it memory across sessions. DDD gives it boundaries it can’t infer on its own.</p>
<p>We didn’t need less engineering discipline. We needed <em>different</em> engineering discipline. The ceremony is dead. The structure is mandatory.</p>
]]></content:encoded></item><item><title>The AI Productivity Paradox: Why I'm Working More Than Ever</title><link>https://lakshminp.com/2026/01/ai-productivity-paradox/</link><pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/ai-productivity-paradox/</guid><category>essays</category><category>craft</category><description>I had a conversation with a friend last week that I can’t stop thinking about.
We were comparing notes on hitting usage limits with AI coding tools. Both of us on expensive plans. Both of us running into ceilings more often than we did months ago. Both of us, apparently, turning into “power users” in our respective tiers.
And then he dropped this line: “So AI was supposed to make us work less but now we are working more. That’s the conclusion.”</description><content:encoded><![CDATA[<p>I had a conversation with a friend last week that I can’t stop thinking about.</p>
<p>We were comparing notes on hitting usage limits with AI coding tools. Both of us on expensive plans. Both of us running into ceilings more often than we did months ago. Both of us, apparently, turning into “power users” in our respective tiers.</p>
<p>And then he dropped this line: “So AI was supposed to make us work less but now we are working more. That’s the conclusion.”</p>
<p>I laughed. Then I stopped laughing.</p>
<p>Because he’s right. I get more done in a single day than I used to accomplish in a week. I’m shipping features, writing content, running experiments at a pace that would’ve been unthinkable about a year ago.</p>
<p>And I have never worked this much in my life.</p>
<p>Here’s what nobody warned us about: AI didn’t give us more time. It gave us more capability.</p>
<p>And capability, it turns out, is extremely addictive.</p>
<h2 id="the-collapse-of-activation-energy"><strong>The Collapse of Activation Energy</strong></h2>
<p>Before AI coding assistants, most ideas died a quiet death in my notes app. Not because they were bad ideas. Because the effort-to-value ratio was unfavorable.</p>
<p>“I could build that feature, but it would take a week of focused work. Is it worth a week? Probably not.”</p>
<p>Idea archived. Moving on.</p>
<p>Now that same feature takes a day. Sometimes less. So I build it.</p>
<p>Then I build the next thing. And the next. And suddenly I’m shipping more in a month than I used to ship in a quarter.</p>
<p>The activation energy for starting new work collapsed. And I filled every inch of the newly available space.</p>
<h2 id="ambition-scales-with-output"><strong>Ambition Scales With Output</strong></h2>
<p>Here’s the thing about humans: we don’t scope our ambitions in absolute terms. We scope them relative to what feels achievable.</p>
<p>Before AI, I planned projects based on what I could reasonably ship with my limited time and energy. A feature per week. Maybe two if I was focused.</p>
<p>Now “reasonable” means something entirely different. My mental model of what’s achievable expanded by 5x, and my project scope expanded right along with it.</p>
<p>I’m not doing the same work faster. I’m doing <em>more work</em>.</p>
<p>The goalposts moved. And I moved them myself.</p>
<h2 id="the-death-of-natural-stopping-points"><strong>The Death of Natural Stopping Points</strong></h2>
<p>There used to be friction in development work. Waiting for builds. Context switching costs. The mental load of holding an entire system in your head while debugging.</p>
<p>That friction was annoying. It was also a circuit breaker.</p>
<p>It forced breaks. It created natural pauses where you’d step away, get coffee, maybe realize it was 7pm and you should probably eat dinner.</p>
<p>AI removed the friction. Which sounds great until you realize the friction was also your automatic brake pedal.</p>
<p>Now you can go from idea to implementation to deployment without ever hitting a natural stopping point. The only thing that stops you is your own willpower.</p>
<p>My willpower, for the record, is not great.</p>
<h2 id="the-dopamine-loop-of-shipping"><strong>The Dopamine Loop of Shipping</strong></h2>
<p>Here’s an uncomfortable comparison: AI-assisted coding feels a lot like infinite scroll.</p>
<p>You ship something. It feels good. The tool makes shipping fast and easy. So you ship something else. That also feels good. And there’s always one more thing you could ship.</p>
<p>Same psychological mechanics. Different output.</p>
<p>Except instead of consuming content, you’re producing it. Which feels more virtuous. Which makes it even harder to stop.</p>
<p>“I’m not doomscrolling. I’m being <em>productive</em>.”</p>
<p>Sure you are.</p>
<h2 id="the-why-not-threshold"><strong>The “Why Not” Threshold</strong></h2>
<p>The most insidious change is what happened to my internal cost-benefit calculator.</p>
<p>I used to ask: “Is this worth the effort?”</p>
<p>Now I ask: “Why wouldn’t I just do this?”</p>
<p>That experiment I would’ve skipped because setting it up was tedious? Now I run it. That edge case I would’ve ignored because fixing it properly would take half a day? Now I fix it.</p>
<p>The threshold for “worth my time” dropped to near zero. So everything is worth my time. So I do everything.</p>
<p>This is how you end up working 12-hour days while technically being more “efficient” than ever before.</p>
<h2 id="the-uncomfortable-truth"><strong>The Uncomfortable Truth</strong></h2>
<p>AI tools didn’t give us more free time. They gave us more output capacity. And we’re psychologically incapable of leaving capacity unused. At least I am.</p>
<p>The work expanded to fill the available capability. Parkinson’s Law, but in reverse.</p>
<p>We’re not working less. We’re shipping more while <em>feeling</em> productive. Which is a different thing entirely.</p>
<p>My friend was right to put “off” in scare quotes when wishing me a good weekend. We both knew I wasn’t really taking time off. I was just switching to a different kind of work.</p>
<h2 id="what-now"><strong>What Now?</strong></h2>
<p>I don’t have a tidy solution here. I’m not going to pretend I’ve figured out work-life balance in the age of AI assistants.</p>
<p>But I’ve started noticing when I’m filling capacity just because I can. When I’m starting a new feature not because it matters, but because the activation energy is so low that “why not” won the argument.</p>
<p>Sometimes the answer to “why not” is: because you could just&hellip; not.</p>
<p>Groundbreaking insight, I realize.</p>
<p>The AI isn’t going to set boundaries for you. If anything, hitting usage limits might be the only forced break some of us get. Which is both sad and a little funny.</p>
<p>Maybe the real productivity hack is learning to leave capability on the table.</p>
<p>I’ll let you know how that goes. Right after I ship this one more thing.</p>
<p><em>I write about building and deploying software as a solo developer. If you’re trying to do it all yourself without hiring a team, I’m probably making the same mistakes you are.</em></p>
]]></content:encoded></item><item><title>Congratulations, You've Been Promoted to Code Janitor</title><link>https://lakshminp.com/2026/01/code-janitor-ai-era/</link><pubDate>Fri, 23 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/code-janitor-ai-era/</guid><category>essays</category><category>craft</category><description>It was 2001. I was building a platformer.
Not “building” in the modern sense, where you describe what you want and a language model hallucinates it into existence. I mean building. DJGPP. Allegro. A DOS compiler that ran on Windows 98 and made you feel like a wizard for getting it to work at all.
I spent three weeks figuring out how platform scrolling worked.
Three weeks. Not because I was stupid — though jury’s still out — but because nobody had written a Medium article explaining it. Stack Overflow didn’t exist. The Allegro documentation was a text file that assumed you already knew what a framebuffer was. I had to think.</description><content:encoded><![CDATA[<p>It was 2001. I was building a platformer.</p>
<p>Not “building” in the modern sense, where you describe what you want and a language model hallucinates it into existence. I mean <em>building</em>. DJGPP. Allegro. A DOS compiler that ran on Windows 98 and made you feel like a wizard for getting it to work at all.</p>
<p>I spent three weeks figuring out how platform scrolling worked.</p>
<p>Three weeks. Not because I was stupid — though jury’s still out — but because nobody had written a Medium article explaining it. Stack Overflow didn’t exist. The Allegro documentation was a text file that assumed you already knew what a framebuffer was. I had to <em>think</em>.</p>
<p>And then one night, around 2am, I got it working.</p>
<p>My little sprite — a 16x16 pixel abomination that was supposed to be a knight but looked more like a confused rectangle — walked across the screen. I pressed the arrow keys and the platform scrolled. The background moved. The character stayed centred.</p>
<p>I decided, right then, that I wanted to be a game programmer.</p>
<p>(I didn’t become a game programmer. Life had other plans. But that’s not the point.)</p>
<p>The point is: I remember that moment with perfect clarity. The dopamine hit. The sense of <em>creation</em>. I had figured something out. I had made something move. I understood, down to the register level, why it worked.</p>
<p>I couldn’t tell you the last time I felt that.</p>
<h2 id="the-joy-we-traded"><strong>The Joy We Traded</strong></h2>
<p>There’s a thread on r/ClaudeAI that’s been haunting me. 624 upvotes. Title: “We are not developers anymore, we are reviewers.”</p>
<p>The author nails it:</p>
<blockquote>
<p><em>“Coding used to be a creative act. You enter a ‘flow state,’ solving micro-problems and building something from nothing. Now, the workflow is: Prompt → Generate → Read Code → Fix Code. We have effectively turned the job into an endless Code Review session.”</em></p>
</blockquote>
<p>And then the kicker:</p>
<blockquote>
<p><em>“Let’s be honest, code review has always been the most tedious part of the job.”</em></p>
</blockquote>
<p>Yeah. That landed.</p>
<p>I used to joke that the worst part of being a senior engineer was reviewing other people’s code. All the cognitive load of understanding a system, none of the satisfaction of building it. You’re not creating — you’re auditing. You’re the IRS of software development.</p>
<p>Congratulations. That’s your whole job now.</p>
<h2 id="the-janitor-effect"><strong>The Janitor Effect</strong></h2>
<p>One commenter called it the “reverse centaur.”</p>
<p>The dream was that AI would be the centaur’s horse — we’d ride it, directing its power, multiplying our capabilities. We’d be the brains, it’d be the muscle.</p>
<p>Instead, we’re the cleanup crew.</p>
<p>Claude writes 400 lines of code in 30 seconds. Impressive. Looks right. Probably compiles. But there’s a subtle bug on line 247 where it’s comparing a string to an integer in a way that JavaScript will happily accept and silently mangle. There’s a race condition in the async handler that only manifests under load. There’s a variable named <code>data</code> that shadows another variable named <code>data</code> three scopes up.</p>
<p>You know. Junior developer stuff.</p>
<p>Except this junior developer types at 10,000 words per minute and never gets tired. So now you’re reviewing 10x more code per day, and every review requires you to maintain the mental context of code <em>you didn’t write</em>.</p>
<p>I spent 20 years building mental maps of codebases. Line by line. Function by function. When you write the code yourself, the map builds automatically. You know why that flag exists because you added it at 3am to fix a production incident. You know that module is haunted because you were there when the haunting began.</p>
<p>When Claude writes the code, you get none of that. You just get the artifact. A fully-formed thing that appeared, Athena-like, from the forehead of a language model. And you have to reverse-engineer the intent from the implementation.</p>
<p>This is debugging someone else’s code.</p>
<p>Forever.</p>
<h2 id="the-uncomfortable-truth"><strong>The Uncomfortable Truth</strong></h2>
<p>Here’s what nobody wants to say out loud: the implementation was the fun part.</p>
<p>Not the architecture. Architecture is meetings. Architecture is diagrams that nobody reads and Jira tickets that nobody updates. Architecture is important, yes, but it’s not <em>fun</em>.</p>
<p>The fun was the 2am breakthrough. The fun was that moment when the tests finally pass and you understand <em>why</em>. The fun was the flow state — that hypnotic trance where hours feel like minutes and you emerge, blinking, having built something that didn’t exist before.</p>
<p>LLMs took that part.</p>
<p>They left us the meetings.</p>
<h2 id="the-promoted-to-manager-cope"><strong>The “Promoted to Manager” Cope</strong></h2>
<p>There’s a certain cope that shows up in these discussions. “Well, actually, you’ve been promoted! Now you’re like a tech lead! You’re directing instead of doing!”</p>
<p>Sure. And my 2001 self was “promoted” from game programmer to accountant the moment Excel learned formulas.</p>
<p>Here’s the thing about being promoted: you’re supposed to <em>want</em> it. The tech leads I know who love their jobs? They love mentoring. They love the big-picture thinking. They love watching junior devs grow.</p>
<p>Nobody loves reviewing AI-generated code. The AI doesn’t grow. It doesn’t learn from your feedback. It just generates more code for you to review tomorrow. You’re not mentoring — you’re babysitting. And the baby has unlimited energy and zero object permanence.</p>
<h2 id="what-we-actually-lost"><strong>What We Actually Lost</strong></h2>
<p>Let me be clear: I’m not a Luddite. The productivity gains are real. I ship faster than ever. I build things in hours that would have taken weeks.</p>
<p>But something shifted.</p>
<p>When I built that platformer in 2001, I was a craftsman. Slow, inefficient, probably writing terrible code — but a craftsman. I understood my tools. I understood my materials. I understood, deeply, what I was making.</p>
<p>Now I’m a project manager for a very fast, very unreliable contractor.</p>
<p>The contractor doesn’t care about the code. It has no pride in the work. It optimizes for “looks plausible” rather than “is correct.” It will happily generate the same bug in 15 different files if you don’t catch it in the first one.</p>
<p>And catching it is <em>your</em> job now. Not building. Catching.</p>
<h2 id="the-question-nobody-wants-to-answer"><strong>The Question Nobody Wants to Answer</strong></h2>
<p>The Reddit thread ends with a question:</p>
<blockquote>
<p><em>“Do you miss the actual act of coding, or are you happy to just be the ‘director’ while the AI does the acting?”</em></p>
</blockquote>
<p>I think about my 2001 self. That kid who spent three weeks understanding platform scrolling. Who felt genuine joy when a rectangle moved across a screen.</p>
<p>Would I trade that experience for “just ask Claude to make a platformer”?</p>
<p>I honestly don’t know.</p>
<p>But I know this: that kid would be horrified by how I work today. Not impressed — horrified. Because to him, the coding <em>was</em> the point. The game was just an excuse to code.</p>
<p>And now the code is just an excuse to ship.</p>
<h2 id="the-adaptation"><strong>The Adaptation</strong></h2>
<p>Look, I don’t have a tidy conclusion here. The models aren’t getting worse. The productivity isn’t going away. We’re not going back to DJGPP and Allegro and three-week debugging sessions.</p>
<p>Maybe the joy comes back in a different form. Maybe it’s in the architecture, once we learn to love it. Maybe it’s in building the tools that build the tools. Maybe it’s in the meta-game of prompt engineering and workflow optimization.</p>
<p>Or maybe we just mourn quietly and move on.</p>
<p>I’ve gotten good at code review. I’ve built mental models for reading AI-generated code quickly, spotting the common failure modes, knowing where to look for the bugs. It’s a skill. Not the skill I wanted, but a skill.</p>
<p>And sometimes — rarely, but sometimes — I still drop into the code myself. Ignore Claude. Write it by hand. Feel the flow state kick in, just for a moment.</p>
<p>It’s slower. It’s inefficient. It’s probably a waste of time.</p>
<p>But that little rectangle still needs to walk across the screen sometimes. Even if nobody’s watching.</p>
]]></content:encoded></item><item><title>Why Company AI Bans Will Backfire (The Napster Lesson)</title><link>https://lakshminp.com/2026/01/ai-ban-spotify-moment/</link><pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/ai-ban-spotify-moment/</guid><category>essays</category><category>craft</category><description>In 1999, a college kid named Shawn Fanning released a little program called Napster.
Within 18 months, 80 million people were using it. The record industry lost its collective mind. Metallica sued. Dr. Dre sued. The RIAA launched a legal crusade that would make Prohibition-era feds proud.
In July 2001, Napster was ordered to shut down.
Victory for the record labels, right? Piracy defeated. Order restored.
Except that’s not what happened at all.</description><content:encoded><![CDATA[<p>In 1999, a college kid named Shawn Fanning released a little program called Napster.</p>
<p>Within 18 months, 80 million people were using it. The record industry lost its collective mind. Metallica sued. Dr. Dre sued. The RIAA launched a legal crusade that would make Prohibition-era feds proud.</p>
<p>In July 2001, Napster was ordered to shut down.</p>
<p>Victory for the record labels, right? Piracy defeated. Order restored.</p>
<p>Except that’s not what happened at all.</p>
<p>What happened was Kazaa. And LimeWire. And BitTorrent. And The Pirate Bay. The music industry spent the next decade playing whack-a-mole with increasingly sophisticated piracy networks. They sued college students for thousands of dollars. They installed rootkits on CDs. They lobbied for laws that made sharing a song punishable by more jail time than armed robbery in some states.</p>
<p>None of it worked.</p>
<p>People didn’t stop downloading music. They just got better at hiding it. The tools got more decentralized, more anonymous, more impossible to shut down. Every crackdown spawned three new services. The industry’s own enforcement efforts trained an entire generation to view them as the enemy.</p>
<p>The thing that finally fixed music piracy wasn’t lawsuits or legislation or DRM. It was Spotify. It was giving people a legitimate way to do the thing they were going to do anyway, at a price point that made piracy feel like more effort than it was worth.</p>
<p>The music industry spent a decade fighting human behavior. Then someone finally figured out how to work with it instead.</p>
<p>I keep thinking about this story lately.</p>
<p><strong>The email that started a Reddit war.</strong></p>
<p>A developer posted recently: “My company banned AI tools and I don’t know what to do.”</p>
<p>Security team sent an email. No ChatGPT. No Claude. No Copilot. No automation platforms with LLMs. Data privacy concerns. Their reasoning wasn’t entirely wrong — they work with sensitive client information.</p>
<p>But here’s the part that made 114 people upvote and 392 people comment:</p>
<p>“Some people on my team are definitely using AI anyway on personal devices. Nobody talks about it but you can tell.”</p>
<p>Read that again.</p>
<p>The ban didn’t stop AI usage. It just pushed it underground. Developers are now typing company code into free-tier tools on personal phones with zero audit trail, zero data retention policies, zero corporate oversight.</p>
<p>The policy designed to prevent data leakage created the exact conditions for data leakage to happen.</p>
<p>Sound familiar?</p>
<p><strong>We’ve seen this movie before.</strong></p>
<p>The Napster pattern shows up everywhere once you start looking.</p>
<p>Prohibition didn’t stop drinking. It created speakeasies and bootleggers and gave organized crime its business model for the next century.</p>
<p>Corporate social media bans don’t stop employees from checking Twitter. They just do it on their phones instead of their work computers — which, ironically, means IT has even less visibility into what’s happening.</p>
<p>VPN blocks in authoritarian countries don’t stop people from accessing banned sites. They just create a thriving market for better VPN services.</p>
<p>The pattern is always the same: Ban the thing people want to do. Watch them do it anyway, but worse. Spend enormous resources trying to enforce the unenforceable. Eventually give up or get disrupted by someone who figured out how to make the thing legal and convenient.</p>
<p>The music industry got Spotify. The question is: what’s the Spotify for AI-banned developers?</p>
<p><strong>The escape hatch nobody’s talking about.</strong></p>
<p>Here’s where this gets interesting.</p>
<p>Buried in a comment on that Reddit thread, someone wrote: “Welcome to local llama.”</p>
<p>Most developers scrolled past it. But that two-word comment is actually the whole answer.</p>
<p>You can run Claude Code — the actual Anthropic CLI tool — with local models. Everything stays on your machine. Nothing touches the cloud. Zero API costs. Full compliance. Your security team can’t complain about data leaving the network when the data never leaves your laptop.</p>
<p>This became possible a few months ago when Ollama added native support for the Anthropic Messages API. Two environment variables and you’re running.</p>
<pre><code>export ANTHROPIC_BASE_URL=&quot;http://localhost:11434&quot;
export ANTHROPIC_AUTH_TOKEN=&quot;ollama&quot;
</code></pre>
<p>That’s it. That’s the whole trick.</p>
<p>Your company banned Claude? Cool. Run Claude Code pointed at a local model. The interface is identical. The workflow is identical. The data stays on hardware you control.</p>
<p>This isn’t a hack or a workaround. It’s a legitimate, auditable, IT-approved way to use AI coding tools without sending a single byte to external servers.</p>
<p><strong>The Spotify moment for AI bans.</strong></p>
<p>Think about what Spotify actually solved.</p>
<p>People wanted music. The industry wanted control. Spotify gave people convenient access while giving the industry a revenue stream and usage data. Everyone got something.</p>
<p>Local AI models are the same deal.</p>
<p>Developers want AI assistance. Security teams want data privacy. Local models give developers the tooling while giving security teams complete control over where the data goes.</p>
<p>For organizations, you can even run Ollama on a beefy internal server and point everyone’s Claude Code at it:</p>
<pre><code>export ANTHROPIC_BASE_URL=&quot;http://internal-server.yourcompany.com:11434&quot;
</code></pre>
<p>Now you’ve got a compliant, auditable, centrally-managed AI coding assistant. IT controls the models. IT controls the access. Everything is logged. Nothing leaves the network.</p>
<p>The security team gets their audit trail. Developers stop pretending they’re coding like it’s 2020. Everyone can have honest conversations in standups instead of maintaining an elaborate fiction.</p>
<p><strong>The honest trade-off.</strong></p>
<p>I’d be lying if I said local models were just as good as Claude’s API.</p>
<p>They’re not. Expect about 60-70% of the Claude experience. Local models need more explicit prompting. Complex multi-file refactors require more hand-holding. The magic “it just works” feeling of Claude Sonnet isn’t quite there yet.</p>
<p>One developer put it bluntly: “Claude Code talked to Ollama, and Qwen3-Coder produced some code. It was clumsy, slow, and required detailed prompting to make something work.”</p>
<p>But here’s the thing about that 60-70%: it’s 60-70% more than zero.</p>
<p>If your choice is between “banned from AI entirely” and “AI that’s pretty good but not magical,” that’s not actually a hard choice. You’re not comparing local models to Claude’s API. You’re comparing local models to doing everything manually while your competitors ship twice as fast.</p>
<p>The gap between local and cloud is real but shrinking. Six months ago this setup wasn’t even possible. The models are getting better every few weeks. By the time your company’s “we’ll revisit the AI policy later” actually happens, local models might be good enough that you don’t even want to switch.</p>
<p><strong>The ten-minute setup.</strong></p>
<p>If you want to try this:</p>
<pre><code># Install Ollama
brew install ollama

# Start it and pull a model
ollama serve
ollama pull qwen3-coder:32b

# Add to your ~/.zshrc
export ANTHROPIC_BASE_URL=&quot;http://localhost:11434&quot;
export ANTHROPIC_AUTH_TOKEN=&quot;ollama&quot;
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

# Reload and run
source ~/.zshrc
claude
</code></pre>
<p>One gotcha: Claude Code’s system prompt is about 16,500 tokens. You need models with at least 32K context. Qwen3-Coder 32B and DeepSeek Coder V2 work well. Smaller models will choke before you even ask a question.</p>
<p>If you’re on an M-series Mac with 64GB RAM, you’re in good shape. 32GB is workable. 16GB is going to hurt.</p>
<p><strong>The point of all this.</strong></p>
<p>The Napster story didn’t end with piracy winning. It ended with the industry finally building something that worked with human nature instead of against it.</p>
<p>Your company’s AI ban is the RIAA lawsuit phase. It feels like control. It’s actually just delaying the inevitable while making everything worse in the meantime.</p>
<p>Local models are the Spotify phase. They’re the legitimate path that gives everyone what they actually want.</p>
<p>The technology exists. The setup takes ten minutes. The trade-offs are reasonable. The only question is whether your organization figures this out now, or burns another year pretending the ban is working while developers type code into ChatGPT on their phones.</p>
<p>History suggests they’ll figure it out eventually.</p>
<p>You don’t have to wait.</p>
]]></content:encoded></item><item><title>Your Code Quality Doesn't Matter Anymore (And It Never Did)</title><link>https://lakshminp.com/2026/01/code-quality-doesnt-matter/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/code-quality-doesnt-matter/</guid><category>essays</category><category>ai-coding</category><category>craft</category><description>A founder on Reddit recently shared that his CTO rebuilt what four third-party partners were providing — using Claude, in weeks, at a fraction of the cost.
Another commenter chimed in: their company replaced $300,000/year software with something they built in-house in under four months.
Meanwhile, over on r/SaasDevelopers, a developer is stuck at $200 MRR for eight months. Beautiful code. Great UX. Fifteen features. Asked where his users come from: “Uh, Product Hunt six months ago and some Reddit posts.”</description><content:encoded><![CDATA[<p>A founder on Reddit recently shared that his CTO rebuilt what four third-party partners were providing — using Claude, in weeks, at a fraction of the cost.</p>
<p>Another commenter chimed in: their company replaced $300,000/year software with something they built in-house in under four months.</p>
<p>Meanwhile, over on r/SaasDevelopers, a developer is stuck at $200 MRR for eight months. Beautiful code. Great UX. Fifteen features. Asked where his users come from: “Uh, Product Hunt six months ago and some Reddit posts.”</p>
<p>These two conversations are happening in parallel across the internet, and most developers haven’t connected the dots yet.</p>
<p>Here’s what’s actually happening: AI didn’t just make coding faster. It vaporized the feature moat entirely.</p>
<p><strong>The feature moat was always a lie we told ourselves.</strong></p>
<p>“If I build it better, they will come.” This was comforting. It meant the thing we’re good at — writing code — was the thing that mattered most.</p>
<p>It wasn’t true before AI. It’s aggressively not true now.</p>
<p>Your competitor can rebuild your core features in a weekend. Not because they’re brilliant. Because Claude is sitting right there, and the barrier to “good enough” has collapsed to basically zero. That integration you spent three months perfecting? Someone’s CTO just shipped an 80% version while you were reading this paragraph.</p>
<p>The YC thread frames it well: “AI mostly kills thin feature moats, not real businesses.” If your entire value proposition is “we built this thing and it works,” congratulations — you’ve built something anyone can now replicate before their coffee gets cold.</p>
<p><strong>So what’s actually defensible?</strong></p>
<p>The comments in both threads converge on the same uncomfortable answer: everything except the code.</p>
<p><strong>Distribution.</strong> The SaasDevelopers post makes the case bluntly: a mediocre product with great distribution beats a great product with no distribution. Every time. The OP claims $4.8K MRR with “decent features, nothing groundbreaking” because he publishes three SEO posts weekly and engages in five communities daily. His previous products had better code and failed under $500 MRR.</p>
<p>Whether you believe his specific numbers or not, the pattern is real. Visibility compounds. Code quality doesn’t.</p>
<p><strong>Operational complexity.</strong> The YC founder pivoted to payments specifically because it’s “harder to clone with AI.” Payments involve regulatory mess, edge cases that actually hurt people when you get them wrong, and trust that takes years to build. You can’t vibe-code your way to PCI compliance.</p>
<p><strong>Workflow embedding.</strong> One commenter nailed it: “Can a copycat ship it, but still not get adopted because switching costs and trust are the real barrier?” If yes, you might have something. If your product is a nice UI on top of an API call, you’re a feature waiting to be absorbed.</p>
<p><strong>Data that compounds.</strong> This one’s subtle but important. If your product gets better because you have data your competitors can’t easily replicate — user behavior, domain-specific training data, network effects — that’s a moat AI can’t trivially cross.</p>
<p><strong>The developer’s existential crisis.</strong></p>
<p>Here’s the part nobody wants to say out loud: for most technical founders, the skill that got them here is now table stakes.</p>
<p>You can write clean code. Great. So can Claude. You can architect systems. Wonderful. So can a junior dev with Cursor and four hours.</p>
<p>The skills that matter now are the ones developers historically dismissed as “marketing” or “sales” or “that stuff the business people do.”</p>
<p>Building an audience. Writing content that ranks. Engaging in communities without getting banned for being too promotional. Understanding what people actually want to pay for versus what’s technically impressive.</p>
<p>This is deeply annoying if you became a developer specifically to avoid talking to people.</p>
<p><strong>What to actually do.</strong></p>
<p>Stop adding features to a product nobody’s using. That’s not building — that’s procrastinating with a compiler.</p>
<p>Spend less time in your IDE and more time in the places your customers hang out. Reddit, LinkedIn, niche communities, whatever. Not to drop links. To understand what problems people are actually complaining about and whether your thing solves any of them. (It’s why I’m building <a href="https://threadhq.co/" rel="external nofollow noopener" class="lnp-link">ThreadHQ</a>.)</p>
<p>If your product can be rebuilt in weeks with AI, either pivot to something with real operational complexity, or accept that distribution is your product now and code is just the unlock.</p>
<p>The YC thread suggests payments, compliance-heavy industries, anything where “mistakes actually hurt” and trust is earned over years. The SaasDevelopers thread suggests becoming a distribution machine: 20+ platform launches, daily content, systematic visibility.</p>
<p>Both are right. Pick your poison.</p>
<p><strong>The uncomfortable synthesis.</strong></p>
<p>AI commoditized the build. What’s left is everything around it: who knows about you, who trusts you, and how painful it would be to switch away.</p>
<p>The code was never the product. Now it’s just impossible to pretend otherwise.</p>
]]></content:encoded></item><item><title>Why I'm Building an Agent Orchestrator</title><link>https://lakshminp.com/2026/01/agent-orchestrator/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/agent-orchestrator/</guid><category>essays</category><category>craft</category><description>I have a confession.
I’ve been running multiple Claude Code sessions manually. Like some kind of air traffic controller using Post-it notes.
Terminal 1: auth refactor.
Terminal 2: API pagination.
Terminal 3: that bug I said I’d fix last week.
Tab. Check. Tab. Check. Tab. “Wait, which one was working on the tests?”
Nobody should have to live like that.
The One-Session Bottleneck Here’s the thing about Claude Code: it’s incredible at focused work. Give it a well-defined task, point it at the right files, and it’ll churn through code faster than you can review it.</description><content:encoded><![CDATA[<p>I have a confession.</p>
<p>I’ve been running multiple Claude Code sessions manually. Like some kind of air traffic controller using Post-it notes.</p>
<p>Terminal 1: auth refactor.</p>
<p>Terminal 2: API pagination.</p>
<p>Terminal 3: that bug I said I’d fix last week.</p>
<p>Tab. Check. Tab. Check. Tab. “Wait, which one was working on the tests?”</p>
<p>Nobody should have to live like that.</p>
<h1 id="the-one-session-bottleneck"><strong>The One-Session Bottleneck</strong></h1>
<p>Here’s the thing about Claude Code: it’s incredible at focused work. Give it a well-defined task, point it at the right files, and it’ll churn through code faster than you can review it.</p>
<p>But “focused” is doing a lot of heavy lifting there.</p>
<p>One session means one task. One context. One thread of execution. While Claude is refactoring your auth system, it’s not touching your API. While it’s writing tests, it’s not fixing that bug.</p>
<p>You, meanwhile, are the bottleneck. The dispatcher. The human scheduler running <code>tmux attach -t session-3</code> forty times a day.</p>
<p>I tried to solve this the obvious way: more sessions. Three terminals. Four. At one point, six.</p>
<p>My M2 Mac doesn’t have fans. It just gets warm and sad. The UI started lagging. Keystrokes took a second to register. Activity Monitor looked like a stock chart during a crash.</p>
<h1 id="i-tried-gastown"><strong>I Tried Gastown</strong></h1>
<p>Steve Yegge built <a href="https://github.com/steveyegge/gastown" rel="external nofollow noopener" class="lnp-link">Gastown</a> - a full agent orchestration system. Polecats, refineries, convoys, molecules, mayors, witnesses, deacons. It’s ambitious. It’s thorough.</p>
<p>I wanted to love it.</p>
<p>I really did.</p>
<p>I spent a week trying to wrap my head around the abstraction layers. Rigs containing polecats containing worktrees. Routes pointing to mayors pointing to beads. Molecules with formulas that become protomolecules that become digests.</p>
<p>Then I spawned 6 polecats for 6 well-groomed tasks.</p>
<p>My system hung. Not “slow” hung. “Is this thing even on?” hung.</p>
<p>Turns out each polecat is a full Claude session. Six sessions competing for API calls, memory, and CPU cycles. The parallelism I wanted was theoretical. The system thrashing was very, very real.</p>
<p>The core issue? Gastown is a coordination layer, not an executor. You still manually <code>gt sling</code> each task. There’s no “run these 6 serially while I do other things.” The dispatcher is still you.</p>
<p>I’m not knocking it. The persistence model is solid - sessions can crash and recover context. The beads integration works. The architecture is thoughtful.</p>
<p>But for my brain, the complexity-to-benefit ratio didn’t compute. I needed something simpler.</p>
<h1 id="whats-actually-non-negotiable"><strong>What’s Actually Non-Negotiable</strong></h1>
<p>After a month of manual orchestration and a week of Gastown experimentation, I’ve landed on what actually matters:</p>
<h2 id="1-beads-integration"><strong>1. Beads Integration</strong></h2>
<p>This isn’t optional. <a href="https://github.com/anthropics/beads" rel="external nofollow noopener" class="lnp-link">Beads</a> is how I track work - git-backed issues with dependencies, labels, and full history. Every task is a bead. Every worker needs to know which bead it’s working on.</p>
<p>No beads, no deal.</p>
<h2 id="2-worktree-isolation"><strong>2. Worktree Isolation</strong></h2>
<p>Each worker gets its own git worktree. Not a branch. A worktree.</p>
<p>Why? Because when Worker A is refactoring auth and Worker B is adding pagination, they cannot be stepping on each other’s files. Worktrees give you physical isolation - separate directories, separate working states, zero merge conflicts during work.</p>
<p>When they’re done, you merge. Not before.</p>
<h2 id="3-reliable-prompt-delivery"><strong>3. Reliable Prompt Delivery</strong></h2>
<p>This one took me a while to figure out.</p>
<p>You spawn a session. You send it a prompt. Simple, right?</p>
<p>Except tmux <code>send-keys</code> doesn’t care if Claude is ready. It just blasts text into the pane. If Claude hasn’t fully initialized, your prompt arrives before there’s anything to receive it.</p>
<p>The fix: detect when Claude is actually running (not just a shell), wait for UI initialization, then send with proper debouncing and retry logic.</p>
<p>Sounds obvious in retrospect. Cost me hours of “why isn’t this working?”</p>
<h2 id="4-visual-monitoring-without-babysitting"><strong>4. Visual Monitoring Without Babysitting</strong></h2>
<p>I need to see what’s happening across all workers. But I don’t want to tab through terminals.</p>
<p>A dashboard. Live status. Which worker is active, which is idle, which is stuck. One glance, full picture.</p>
<p>And critically: switching to a worker shouldn’t kill the dashboard. The monitoring should keep running while I’m working.</p>
<figure>
<a href="https://substackcdn.com/image/fetch/$s_!jI6t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png" class="image-link image2 is-viewable-img" target="_blank" data-component-name="Image2ToDOM"></a>
<img src="https://substack-post-media.s3.amazonaws.com/public/images/7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png" class="sizing-normal" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:191377,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://lakshminp.substack.com/i/185158137?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" srcset="https://substackcdn.com/image/fetch/$s_!jI6t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png 424w, https://substackcdn.com/image/fetch/$s_!jI6t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png 848w, https://substackcdn.com/image/fetch/$s_!jI6t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png 1272w, https://substackcdn.com/image/fetch/$s_!jI6t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7fc7f38d-cf20-454b-9b45-e1371ea4c532_1902x1003.png 1456w" sizes="100vw" loading="lazy" width="1456" height="768" />
<img src="data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld2JveD0iMCAwIDIwIDIwIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1mZy1wcmltYXJ5KSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnPjx0aXRsZT48L3RpdGxlPjxwYXRoIGQ9Ik0yLjUzMDAxIDcuODE1OTVDMy40OTE3OSA0LjczOTExIDYuNDMyODEgMi41IDkuOTExNzMgMi41QzEzLjE2ODQgMi41IDE1Ljk1MzcgNC40NjIxNCAxNy4wODUyIDcuMjM2ODRMMTcuNjE3OSA4LjY3NjQ3TTE3LjYxNzkgOC42NzY0N0wxOC41MDAyIDQuMjY0NzFNMTcuNjE3OSA4LjY3NjQ3TDEzLjY0NzMgNi45MTE3Nk0xNy40OTk1IDEyLjE4NDFDMTYuNTM3OCAxNS4yNjA5IDEzLjU5NjcgMTcuNSAxMC4xMTc4IDE3LjVDNi44NjExOCAxNy41IDQuMDc1ODkgMTUuNTM3OSAyLjk0NDMyIDEyLjc2MzJMMi40MTE2NSAxMS4zMjM1TTIuNDExNjUgMTEuMzIzNUwxLjUyOTMgMTUuNzM1M00yLjQxMTY1IDExLjMyMzVMNi4zODIyNCAxMy4wODgyIiAvPjwvZz48L3N2Zz4=" />
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld2JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLW1heGltaXplMiBsdWNpZGUtbWF4aW1pemUtMiI+PHBvbHlsaW5lIHBvaW50cz0iMTUgMyAyMSAzIDIxIDkiPjwvcG9seWxpbmU+PHBvbHlsaW5lIHBvaW50cz0iOSAyMSAzIDIxIDMgMTUiPjwvcG9seWxpbmU+PGxpbmUgeDE9IjIxIiB4Mj0iMTQiIHkxPSIzIiB5Mj0iMTAiPjwvbGluZT48bGluZSB4MT0iMyIgeDI9IjEwIiB5MT0iMjEiIHkyPSIxNCI+PC9saW5lPjwvc3ZnPg==" class="lucide lucide-maximize2 lucide-maximize-2" />
</figure>
<p><em>Early wt screenshot with wt watch on the side.</em></p>
<p><code>wt watch</code> <em>- All workers, one glance. No tab-switching required.</em></p>
<h2 id="5-simplicity"><strong>5. Simplicity</strong></h2>
<p>One binary. Tmux (which I already use). Beads (which I already use). Git worktrees (which are just git).</p>
<p>No mayors. No deacons. No protomolecules. No routing tables pointing to other routing tables.</p>
<p>If I can’t explain the mental model in 30 seconds, it’s too complex.</p>
<h1 id="so-im-building-it"><strong>So I’m Building It</strong></h1>
<p>It’s called <code>wt</code> (worktree). The core workflow:</p>
<p>Grooming is sacred. I run dedicated Claude Code sessions where I don’t write code - I think out loud. “We need pagination on this API. The auth middleware is getting messy, let’s refactor it. Oh, and that bug from last week.”</p>
<p>Claude helps me turn those thoughts into beads. Sets priorities. Adds dependencies. The interaction is conversational, not CLI gymnastics.</p>
<pre><code>me: &quot;let's break down the user dashboard feature&quot;
claude: [creates 4 beads with dependencies, P1 for the data layer, P2 for the rest]
me: &quot;the caching one blocks the others&quot;
claude: [adds dependency links]
</code></pre>
<p>The better the grooming, the more autonomous the workers can be. Then execution is just:</p>
<pre><code>wt ready              # What's unblocked?
wt new proj-123       # Spawn a worker
wt hub                # Watch them work
</code></pre>
<p>But it grew legs. Session lifecycle (<code>wt done</code>, <code>wt abandon</code>, <code>wt signal</code>). History and resumption (<code>wt seance</code> - yes, you can talk to dead sessions). Autonomous batch mode (<code>wt auto</code>). Context handoff (<code>wt handoff</code>, <code>wt prime</code>).</p>
<figure>
<a href="https://substackcdn.com/image/fetch/$s_!DHzi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png" class="image-link image2" target="_blank" data-component-name="Image2ToDOM"></a>
<img src="https://substack-post-media.s3.amazonaws.com/public/images/2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png" class="sizing-normal" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:240,&quot;width&quot;:841,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34251,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://lakshminp.substack.com/i/185158137?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" srcset="https://substackcdn.com/image/fetch/$s_!DHzi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png 424w, https://substackcdn.com/image/fetch/$s_!DHzi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png 848w, https://substackcdn.com/image/fetch/$s_!DHzi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png 1272w, https://substackcdn.com/image/fetch/$s_!DHzi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cfd87f7-a762-4b1f-927c-c4bd5410fdf2_841x240.png 1456w" sizes="100vw" loading="lazy" width="841" height="240" />
</figure>
<p><em>wt project list showing registered projects with their paths and bead counts. wt itself is built using wt, I know, so meta.</em></p>
<p><em>Multiple projects, one tool. Each with its own beads, worktrees, and workers.</em></p>
<p>The commands multiplied, but the mental model stayed simple: projects contain beads, beads spawn workers, workers live in worktrees, hub watches everything.</p>
<p><strong>Next post:</strong> I’ll walk through the architecture - why tmux, why worktrees, and the surprisingly tricky problem of “how do you know when Claude is ready to receive input?”</p>
]]></content:encoded></item><item><title>90% of Programming Skills Just Got Commoditized. The Other 10% Is Worth 1000X More.</title><link>https://lakshminp.com/2026/01/programming-skills-commoditized/</link><pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/programming-skills-commoditized/</guid><category>essays</category><category>ai-coding</category><category>craft</category><description>Andrej Karpathy recently wrote something that’s been rattling around my head:
“I’ve never felt this much behind as a programmer. The profession is being dramatically refactored as the bits contributed by the programmer are increasingly sparse and between. 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 and a failure to claim the boost feels decidedly like skill issue.”</description><content:encoded><![CDATA[<p>Andrej Karpathy recently <a href="https://x.com/karpathy/status/2004607146781278521" rel="external nofollow noopener" class="lnp-link">wrote something</a> that’s been rattling around my head:</p>
<blockquote>
<p>“I’ve never felt this much behind as a programmer. The profession is being dramatically refactored as the bits contributed by the programmer are increasingly sparse and between. 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 and a failure to claim the boost feels decidedly like skill issue.”</p>
</blockquote>
<p>He then listed what this new layer looks like: agents, subagents, prompts, contexts, memory, modes, permissions, tools, plugins, skills, hooks, MCP, LSP, slash commands, workflows, IDE integrations.</p>
<p>His conclusion: “Clearly some powerful alien tool was handed around except it comes with no manual and everyone has to figure out how to hold it and operate it, while the resulting magnitude 9 earthquake is rocking the profession.”</p>
<p>I felt this in my bones.</p>
<h2 id="the-old-stack-vs-the-new-stack"><strong>The Old Stack vs The New Stack</strong></h2>
<p>The old programming stack was hard enough:</p>
<p>Hardware -&gt; OS -&gt; Language -&gt; Frameworks -&gt; Your Code</p>
<p>Years of learning. Layers of abstraction. But at least it was <em>deterministic</em>. At least there were manuals. At least Stack Overflow had answers.</p>
<p>The new stack adds a layer on top:</p>
<p>You -&gt; Prompts/Agents/Context/Memory/Tools/Modes -&gt; Code</p>
<p>This layer is fundamentally different. It’s stochastic. It’s fallible. It’s unintelligible. And it changes every few weeks.</p>
<p>There’s no certification. There’s no textbook. There’s no “Effective AI Orchestration” by Joshua Bloch. Just a bunch of people figuring it out in Discord servers and sharing CLAUDE.md files and best practices like trading cards.</p>
<h2 id="the-divide-is-already-here"><strong>The Divide Is Already Here</strong></h2>
<p>Someone on Reddit <a href="https://reddit.com/r/ClaudeAI/comments/1lquetd/the_claude_code_divide_those_who_know_vs_those/" rel="external nofollow noopener" class="lnp-link">described the pattern</a> they’re seeing on their team:</p>
<blockquote>
<p>“Two developers with similar experience working on similar tasks, but one consistently ships features in hours while the other is still debugging. At first I thought it was just luck or skill differences. Then I realized what was actually happening — it’s their instruction library.”</p>
</blockquote>
<p>They’re watching an underground collection of power users share workflows like secrets:</p>
<ul>
<li>
<p>Commands that automatically debug entire codebases</p>
</li>
<li>
<p>CLAUDE.md files that turn Claude into domain experts</p>
</li>
<li>
<p>Slash commands that turn 45-minute processes into 2-minute ones</p>
</li>
</ul>
<p>Meanwhile, most people are still typing “help me fix this bug” and wondering why their results suck.</p>
<p>As one developer put it: “The differences between someone who opens up CC for the first time and someone with tuned md files is beyond night and day.”</p>
<h2 id="the-skill-issue-is-real-but-not-the-one-you-think"><strong>The Skill Issue Is Real (But Not The One You Think)</strong></h2>
<p>Here’s what hit me about Karpathy’s framing: he called it a “skill issue.”</p>
<p>Not a tools issue. Not an access issue. Not a funding issue.</p>
<p>A <em>skill</em> issue.</p>
<p>The 10X boost exists. The leverage is real. But claiming it requires mastering something that didn’t exist two years ago and has no curriculum.</p>
<p>Someone in that same thread nailed the uncomfortable truth: “90% of traditional programming skills are becoming commoditized while the remaining 10% becomes worth 1000x more. That 10% isn’t coding — it’s knowing how to architect AI workflows.”</p>
<p>The irony is brutal. We spent years mastering syntax, frameworks, design patterns. Now an AI can generate all of that in seconds. What it <em>can’t</em> do is orchestrate itself effectively. That’s your job now.</p>
<h2 id="what-the-new-layer-actually-looks-like"><strong>What The New Layer Actually Looks Like</strong></h2>
<p>Let me make this concrete. Here’s what I’ve had to learn in the past year that wasn’t part of any CS curriculum:</p>
<p><strong>CLAUDE.md Architecture</strong></p>
<p>Your instructions file isn’t documentation. It’s programming. The structure, the phrasing, what you include vs exclude — these decisions compound across every interaction. A well-architected CLAUDE.md is worth more than a well-architected codebase.</p>
<p><strong>Context Management</strong></p>
<p>Every token matters. MCP servers eat context. Long conversations drift. You need to think about what Claude knows, what it’s forgotten, when to compact, when to start fresh. It’s memory management, but for a mind that isn’t yours.</p>
<p><strong>Prompt Design</strong></p>
<p>Not “prompt engineering” in the LinkedIn-influencer sense. Actual design. When do you give examples? When do you constrain? When do you let it explore? How do you phrase things so it doesn’t hallucinate? How do you trigger deeper thinking? These are learnable skills with massive payoff differences.</p>
<p><strong>Tool Orchestration</strong></p>
<p>MCP, skills, hooks, slash commands. Which tool for which job? When does an MCP server make sense vs a bash script vs a skill file? How do you chain them? How do you debug when the chain breaks?</p>
<p><strong>Mode Awareness</strong></p>
<p>Plan mode vs implement mode. When to let Claude explore vs when to constrain. When to use subagents. When to go linear. The <em>meta</em> of working with AI — knowing when to switch approaches — is itself a skill.</p>
<p><strong>Verification Choreography</strong></p>
<p>AI generates fast. Verification is the bottleneck. How do you structure your workflow so you’re not just rubber-stamping garbage? How do you catch the 8 production bombs before they ship? (Yes, <a href="https://lakshminp.substack.com/p/what-claude-cant-do-for-you" rel="external nofollow noopener" class="lnp-link">I wrote about this</a>.)</p>
<h2 id="the-manual-that-doesnt-exist"><strong>The Manual That Doesn’t Exist</strong></h2>
<p>An older developer on Reddit <a href="https://reddit.com/r/ClaudeAI/comments/1lquetd/the_claude_code_divide_those_who_know_vs_those/" rel="external nofollow noopener" class="lnp-link">captured the frustration</a>:</p>
<blockquote>
<p>“I started using AI about 2 years ago. I thought I was doing good, but then I started seeing all this stuff about MCP servers, md files etc and I am kind of lost. I want to learn more and I want to improve my AI skills but it’s difficult for me.”</p>
</blockquote>
<p>This is someone with decades of experience, feeling lost because the new layer has no onramp.</p>
<p>The manual doesn’t exist because the platform keeps shifting. Claude Code ships updates weekly. New features appear. Old patterns stop working. The MCP ecosystem is exploding. Skills just launched. Hooks changed. The ground won’t stop moving.</p>
<p>You can’t study for an earthquake. You can only practice surfing.</p>
<h2 id="how-im-learning-imperfectly"><strong>How I’m Learning (Imperfectly)</strong></h2>
<p>I don’t have this figured out. Nobody does. But here’s what’s working:</p>
<p><strong>Steal shamelessly</strong>. Find people who are clearly more productive and reverse-engineer their setup. Their CLAUDE.md files, their slash commands, their workflows. GitHub repos, Discord servers, Reddit threads. The good stuff is scattered but findable.</p>
<p><strong>Treat your setup as code</strong>. Version control your CLAUDE.md. Iterate on your slash commands. When something works, document why. When something fails, autopsy it. Your instruction library is a codebase now.</p>
<p><strong>Invest in meta-skills</strong>. The specific tools will change. MCP might get replaced. Claude Code might get competition. But the meta-skills — context management, prompt design, verification choreography — those transfer.</p>
<p><strong>Actually use the new features</strong>. Hooks exist. Subagents exist. Skills exist. Most people ignore them because they’re “advanced.” They’re not advanced. They’re just new. The learning curve is the moat.</p>
<p><strong>Teach to learn</strong>. Writing about this forces me to understand it. Explaining my setup to others reveals the gaps. The best way to master the new layer is to articulate it.</p>
<h2 id="the-uncomfortable-conclusion"><strong>The Uncomfortable Conclusion</strong></h2>
<p>Karpathy is right. There’s a 10X boost available. Failing to claim it is a skill issue.</p>
<p>But it’s a <em>new</em> skill. One that didn’t exist before. One that has no manual, no certification, no clear path.</p>
<p>The people figuring it out are building compound advantages. Every custom command, every refined CLAUDE.md pattern, every workflow optimization — it all stacks. The gap between those who master the new layer and those who don’t is widening fast.</p>
<p>The earthquake is still happening. The alien tool is still being figured out. The manual is being written in real-time by the people using it. And the rules are being changed as we speak/code/write.</p>
<p>Roll up your sleeves.</p>
<p><em>This is a companion to my previous essay on <a href="https://lakshminp.substack.com/p/claude-code-is-incredible-it-also" rel="external nofollow noopener" class="lnp-link">what Claude can’t do for you</a>. That one covered the old skills that still matter. This one covers the new skills you need to add.</em></p>
]]></content:encoded></item><item><title>Clean Code Is Dead. Long Live Clean Specs.</title><link>https://lakshminp.com/2026/01/clean-code-dead-clean-specs/</link><pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/01/clean-code-dead-clean-specs/</guid><category>essays</category><category>ai-coding</category><category>craft</category><description>Steve Yegge shipped 225,000 lines of Go code he’s never read.
Let that sink in.
Beads — his coding agent memory system — is used by tens of thousands of developers daily. It’s 100% vibe coded. Yegge has never looked at a single line. Same with his new project, Gastown. Three weeks old, 100% vibe coded, never seen the code, never plans to.
His reaction to anyone uncomfortable with this? “Get out now.”</description><content:encoded><![CDATA[<p>Steve Yegge shipped 225,000 lines of Go code he’s never read.</p>
<p>Let that sink in.</p>
<p><a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a" rel="external nofollow noopener" class="lnp-link">Beads</a> — his coding agent memory system — is used by tens of thousands of developers daily. It’s 100% vibe coded. Yegge has never looked at a single line. Same with his new project, <a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04" rel="external nofollow noopener" class="lnp-link">Gastown</a>. Three weeks old, 100% vibe coded, never seen the code, never plans to.</p>
<p>His reaction to anyone uncomfortable with this? “Get out now.”</p>
<h2 id="the-heresy"><strong>The Heresy</strong></h2>
<p>For two decades, we’ve been taught that code is literature. Uncle Bob’s Clean Code. Martin Fowler’s Refactoring. Elegant variable names. Single responsibility. Code should read like prose.</p>
<p>We optimized for human comprehension because humans had to maintain it.</p>
<p>But what if that’s no longer true?</p>
<p><a href="https://www.simonhoiberg.com/" rel="external nofollow noopener" class="lnp-link">Simon Hoiberg</a> put it bluntly: “Half my code is now written by AI, and the other half is read by AI to fix bugs. Optimizing for human readability is becoming pointless.”</p>
<p>The audience for your code has changed. And it’s not you anymore.</p>
<h2 id="the-other-day-i-wrote-about-comprehension-debt"><strong>The Other Day I Wrote About Comprehension Debt</strong></h2>
<p>I argued that vibe coding creates legacy code from day one. That velocity without comprehension isn’t velocity — it’s procrastination with extra steps.</p>
<p>I still believe that. Mostly.</p>
<p>But here’s the uncomfortable follow-up question: What if comprehension debt only matters when <em>you</em> have to pay it?</p>
<p>If AI writes the code and AI debugs the code and AI refactors the code&hellip; who exactly needs to understand it?</p>
<h2 id="the-new-contract"><strong>The New Contract</strong></h2>
<p>The old contract: Write clean code so humans can read it.</p>
<p>The new contract: Write code that produces correct outcomes, verified by tests that humans can understand.</p>
<p>This is a crucial shift. The code becomes disposable infrastructure. The tests become the spec. The behavior becomes the product.</p>
<p>Steve Yegge doesn’t need to understand 225,000 lines of Go. He needs to understand what Beads should do. The tests verify that it does it. The code is just&hellip; implementation detail. An artifact. A byproduct.</p>
<h2 id="clean-specs--clean-code"><strong>Clean Specs &gt; Clean Code</strong></h2>
<p>Here’s the heretical thought experiment:</p>
<p>What if “clean code” principles should now apply to your specifications instead of your source code?</p>
<p>Think about it:</p>
<ul>
<li>
<p><strong>Readable intent</strong>: Your specs should be crystal clear. “Users can checkout with valid payment. Invalid cards show an error. Empty carts can’t checkout.”</p>
</li>
<li>
<p><strong>Single responsibility</strong>: Each spec describes one behavior. Not implementation — behavior.</p>
</li>
<li>
<p><strong>Self-documenting</strong>: Specs are the documentation that gets executed. They describe what the system should do, and you verify it actually does.</p>
</li>
<li>
<p><strong>Easy to modify</strong>: When requirements change, you update the spec first. AI updates everything else.</p>
</li>
</ul>
<p>The source code can be a tangled mess of AI-generated spaghetti. Who cares? If you can clearly specify what you want and verify you got it, the implementation is just a detail.</p>
<h2 id="the-yegge-paradox"><strong>The Yegge Paradox</strong></h2>
<p>Here’s what’s wild. In the Vibe Coding book Yegge co-authored with Gene Kim, “Steve” is described as reviewing 10,000 lines of code a day, throwing away 10 lines for every line kept.</p>
<p>Wait. He reviews code? I thought he never looks at it?</p>
<p>The answer, I think, is this: He reviews <em>outcomes</em>. He reviews test results. He reviews whether the thing works. He’s not reading code for elegance or comprehension. He’s running it, breaking it, verifying it.</p>
<p>The code review has become a behavior review.</p>
<h2 id="what-this-means-for-you"><strong>What This Means for You</strong></h2>
<p>I’m not saying burn your Clean Code book. (Okay, maybe I am. That thing is 400 pages of what could’ve been a blog post.)</p>
<p>But consider this workflow:</p>
<ol>
<li>
<p><strong>Specify the behavior</strong> — in plain language. “Users can checkout with valid payment. Invalid cards show an error. Empty carts can’t checkout.”</p>
</li>
<li>
<p><strong>Let AI write the tests</strong> — it turns your specs into executable verification</p>
</li>
<li>
<p><strong>Let AI write the implementation</strong> — who cares if it’s ugly</p>
</li>
<li>
<p><strong>Verify the outcomes</strong> — does it do what you specified? Try to break it. Edge cases covered?</p>
</li>
<li>
<p><strong>Ship it</strong> — the code is a means to an end</p>
</li>
</ol>
<p>If something breaks, you don’t debug the code. You describe the broken behavior. AI writes a failing test. AI fixes the implementation. You verify the outcome. You never had to understand the implementation. You just had to understand what you wanted.</p>
<h2 id="the-catch"><strong>The Catch</strong></h2>
<p>There’s always a catch.</p>
<p>This only works if your specifications are actually good. If your specs are vague, incomplete, missing edge cases — you’re in the worst of both worlds. Incomprehensible code that doesn’t even do what you need.</p>
<p>That’s not vibe coding. That’s vibes-all-the-way-down coding. And that’s how you get 18 out of 20 CTOs reporting production disasters.</p>
<p>The discipline has to go somewhere. If you’re not putting it into clean code, you damn well better be putting it into clear specifications and ruthless outcome verification.</p>
<h2 id="the-real-skill-shift"><strong>The Real Skill Shift</strong></h2>
<p>Old skill: Writing elegant, maintainable code that other humans can understand.</p>
<p>New skill: Specifying behavior precisely and verifying outcomes ruthlessly.</p>
<p>The developers who thrive won’t be the ones who write the cleanest code. They’ll be the ones who can articulate exactly what they want. Who can break their own systems. Who can look at a feature and immediately think of ten ways it could fail.</p>
<p>Code literacy is becoming specification literacy. The new “clean code” is clear intent.</p>
<h2 id="the-uncomfortable-conclusion"><strong>The Uncomfortable Conclusion</strong></h2>
<p>We spent twenty years optimizing for human readers who are increasingly being replaced by AI readers.</p>
<p>Maybe Steve Yegge is right. Maybe the code doesn’t matter. Maybe it never really mattered — we just didn’t have anything better.</p>
<p>What matters is: Does it work? Can you prove it? Can you verify it still works after changes?</p>
<p>Clean code was a proxy for those questions. A good heuristic when humans had to debug.</p>
<p>Clean specs answer those questions directly.</p>
<p>The code is dead. Long live the specs.</p>
<p><em>This is a follow-up to my <a href="https://lakshminp.substack.com/p/the-invisible-tax-you-pay-when-you" rel="external nofollow noopener" class="lnp-link">recent essay</a> on comprehension debt. The tension is real: you need to understand the problem deeply enough to specify it clearly, but maybe not the implementation at all. Where that line is&hellip; I’m still figuring out.</em></p>
]]></content:encoded></item><item><title>The Junior Dev Paradox: We’re Speed-Running Past the Tutorial</title><link>https://lakshminp.com/2025/11/junior-dev-ai-paradox/</link><pubDate>Sat, 01 Nov 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/11/junior-dev-ai-paradox/</guid><category>essays</category><category>craft</category><description>So here’s a fun thought experiment: What happens when an entire generation of developers learns to code by never actually learning to code?
I don’t mean that in the gatekeepy “back in my day we walked uphill both ways in assembly language” sense. I mean it literally. Right now, today, someone is getting their first junior dev job having built an impressive portfolio of projects they couldn’t debug if their life depended on it.</description><content:encoded><![CDATA[<p>So here’s a fun thought experiment: What happens when an entire generation of developers learns to code by never actually learning to code?</p>
<p>I don’t mean that in the gatekeepy “back in my day we walked uphill both ways in assembly language” sense. I mean it literally. Right now, today, someone is getting their first junior dev job having built an impressive portfolio of projects they couldn’t debug if their life depended on it.</p>
<p>And honestly? I’m not sure if that’s a problem or just&hellip; different.</p>
<h2 id="the-thing-nobody-wants-to-say-out-loud">The Thing Nobody Wants to Say Out Loud</h2>
<p>We—the developers who learned pre-AI—spent an ungodly amount of time doing things that, in retrospect, might have been pointless. Memorizing syntax. Reading documentation cover to cover because Stack Overflow didn’t have the answer. Spending three hours debugging only to find a missing semicolon. Writing the same boilerplate for the thousandth time because that’s just how you learned patterns.</p>
<p>That grind built something, though. Call it intuition. Call it muscle memory. Call it the ability to look at a stack trace and just <em>know</em> where the problem is because you’ve seen that exact error forty times before. We developed pattern recognition through sheer repetitive exposure, like some kind of coding Stockholm syndrome.</p>
<p>Junior devs today can skip all of that. They can describe what they want and watch Claude or Copilot generate it. They can ship features on day one that would’ve taken us weeks to build as juniors. They can contribute to complex codebases without understanding half of what’s happening under the hood.</p>
<p>Which is either the most amazing democratization of technical skills in history, or we’re a generation of developers who are one AI outage away from complete helplessness.</p>
<p>Probably both.</p>
<h2 id="what-we-might-be-losing">What We Might Be Losing</h2>
<p>Here’s what I wonder about:</p>
<p>Can you develop debugging intuition if AI catches most of your bugs?</p>
<p>Can you build system design sense if you’ve never had to architect something from scratch?</p>
<p>Can you really understand <em>why</em> something works if you’ve only ever described <em>what</em> you want it to do?</p>
<p>The old way of learning had a built-in forcing function. You <em>had</em> to understand data structures because you couldn’t implement anything without them. You <em>had</em> to read error messages carefully because that was your only clue. You <em>had</em> to develop mental models of how systems work because there was no AI to abstract it away.</p>
<p>It was inefficient as hell. It was also weirdly effective.</p>
<p>Now we’ve got junior devs who can ship impressive features but might struggle to explain what a hash table is or why their O(n^2) solution is melting production. They know how to make things work; they just don’t always know <em>why</em> they work or <em>how</em> to fix them when they don’t.</p>
<p>And before someone shows up in the comments with “well actually, they can just ask AI to debug it”—sure, until they can’t. Until the AI doesn’t understand the problem. Until the codebase is too complex or too weird or too legacy. Until, I don’t know, <a href="https://www.lakshminp.com/p/when-claude-code-goes-down-a-meditation" rel="external nofollow noopener" class="lnp-link">Claude Code goes down for five hours and suddenly you’re naked without your safety net</a>.</p>
<h2 id="what-we-might-be-gaining">What We Might Be Gaining</h2>
<p>But here’s the flip side: maybe we’re romanticizing the struggle.</p>
<p>Junior devs today are learning different skills. They’re getting good at prompt engineering, at articulating problems clearly, at evaluating AI-generated solutions. They’re exposed to more patterns, more codebases, more architectural approaches in their first year than we saw in five.</p>
<p>They’re also spending less time on tedious nonsense. Nobody needs to memorize the exact syntax for array methods or spend a week setting up a development environment. That time gets redirected to actually building things, to experimenting, to shipping.</p>
<p>And maybe—<em>maybe</em>—the fundamentals that matter are changing. Maybe understanding how to architect a system is more valuable than knowing how to implement every piece of it. Maybe code review skills and the ability to verify solutions matter more than the ability to generate them from scratch.</p>
<p>Maybe the fact that they can be productive on day one is a feature, not a bug.</p>
<h2 id="the-real-problem-the-copy-paste-generation">The Real Problem: The Copy-Paste Generation</h2>
<p>The actual risk isn’t that junior devs are using AI. It’s that some of them are using it as a crutch instead of a catalyst.</p>
<p>There’s a difference between “I don’t understand this, let me ask AI to explain it” and “I don’t understand this, so I’ll just copy-paste whatever AI gives me and hope it works.” One is learning accelerated by AI. The other is&hellip; well, it’s not learning at all.</p>
<p>We’re going to end up with a split: junior devs who use AI to move faster while still building understanding, and junior devs who are entirely dependent on AI to function. The <strong>first group will be terrifyingly productive</strong>. The second group is going to hit a wall the moment they encounter a problem AI can’t solve.</p>
<p>And here’s the uncomfortable part: it’s getting harder to tell them apart during hiring. Both can build impressive portfolios. Both can ship features. The difference only shows up when things break, when requirements get weird, when they need to dig into a gnarly legacy codebase that AI doesn’t understand.</p>
<h2 id="some-half-baked-solutions">Some Half-Baked Solutions</h2>
<p>So what do we do about this? I don’t have perfect answers, but here are some thoughts:</p>
<p><strong>For junior devs:</strong> Choose the harder path sometimes. Deliberately code without AI for practice. Build a project from scratch where you have to figure everything out manually. Read source code, not just documentation. When AI generates something, understand <em>why</em> it works before moving on. Treat AI as a tutor who’s always available, not a replacement for thinking.</p>
<p><strong>For seniors and mentors:</strong> Stop assuming junior devs have the same foundation you did. Be explicit about the “why” behind decisions. Create space for questions that might sound basic. Do code reviews that focus on understanding, not just functionality. Maybe assign “AI-free” tasks occasionally, not as hazing, but as skill-building.</p>
<p><strong>For companies:</strong> Normalize “I don’t know, let me learn this properly” instead of “ship at all costs.” Allocate time for learning, not just velocity. Celebrate understanding, not just output. Maybe reconsider how you evaluate technical skills in interviews—you’re not just testing if someone can code, you’re testing if they can think.</p>
<p><strong>For education:</strong> Stop pretending AI doesn’t exist. Teach people how to use it effectively, not how to avoid it. But also teach debugging, system design, and foundational concepts. The goal isn’t to reject AI; it’s to use it wisely while building real understanding.</p>
<h2 id="the-uncomfortable-non-conclusion">The Uncomfortable Non-Conclusion</h2>
<p>Here’s the truth: We’re all figuring this out in real-time. Every generation of developers has had this conversation in some form—about IDEs, about Stack Overflow, about frameworks that abstract away complexity. The old guard always worries the new guard doesn’t know “the fundamentals.”</p>
<p>Sometimes they’re right. Sometimes they’re just old. Also, “the fundamentals” is an ever shifting goal post.</p>
<p>I don’t know which this is yet. Ask me in five years when we see how this generation of AI-native developers performs at scale. Ask me when we see if they hit a ceiling or if they just built their skills differently.</p>
<p>What I do know is this: AI-assisted coding isn’t going away. The barrier to building software has collapsed. Junior devs can be productive faster than ever. And somewhere in there, we need to figure out how to preserve the understanding that makes you not just productive, but genuinely good at this job.</p>
<p>Because the best developers aren’t the ones who can generate code the fastest. They’re the ones who can look at a complex system, understand how it works, figure out why it’s broken, and know how to fix it. Whether you learned that through years of painful debugging or through AI-accelerated practice doesn’t really matter.</p>
<p>As long as you actually learned it.</p>
]]></content:encoded></item><item><title>You should probably ditch your IDE</title><link>https://lakshminp.com/2025/10/ditch-your-ide/</link><pubDate>Sun, 26 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/ditch-your-ide/</guid><category>essays</category><category>craft</category><description>I fire up VS Code.
It opens the last workspace I was in — half a dozen tabs, a Dockerfile, maybe a README from some unrelated task.
No clue what I was working on.
Just a vague memory: “Something about the scheduling API… or maybe a bug?”
I sit there for a moment trying to reconstruct context.
Last commit was Friday.
Today’s Tuesday.
My brain’s context window has been wiped clean by Slack pings, meetings, and weekend errands.</description><content:encoded><![CDATA[<p>I fire up VS Code.</p>
<p>It opens the last workspace I was in — half a dozen tabs, a Dockerfile, maybe a README from some unrelated task.</p>
<p>No clue what I was working on.</p>
<p>Just a vague memory: “Something about the scheduling API… or maybe a bug?”</p>
<p>I sit there for a moment trying to reconstruct context.</p>
<p>Last commit was Friday.</p>
<p>Today’s Tuesday.</p>
<p>My brain’s context window has been wiped clean by Slack pings, meetings, and weekend errands.</p>
<p>Command-P.</p>
<p>Type a filename I half remember.</p>
<p>Oh right, that test file.</p>
<p>Wait — did I change this in another branch?</p>
<p>Let me check.</p>
<p>Open terminal.</p>
<p>git branch.</p>
<p>Switch.</p>
<p>Pull.</p>
<p>Oops, the local container setup broke.</p>
<p>Right, I had containerized everything.</p>
<p>So now I need to run docker compose up. But then I remember there’s a VS Code extension that does it automatically. Let me find it, install it, configure it, restart VS Code.</p>
<p>Fifteen minutes gone.</p>
<p>I still haven’t written a single line of code.</p>
<p>This is what IDEs do to us.</p>
<p>They <em>feel</em> fast, but they slow us down in invisible ways.</p>
<p>Every step is a click, a context switch, a small distraction.</p>
<p>Every plugin promises convenience, but adds one more thing to babysit.</p>
<p>IDEs are built for humans — for our limited memory and need for visual cues.</p>
<p>They make us feel productive, but under the hood they’re optimizing for comfort, not speed.</p>
<p>Now we’re trying to stuff AI into them — Copilots, side panels, chat panes —</p>
<p>as if AI needs dark themes and split editors to work.</p>
<p>It doesn’t.</p>
<p>AI just needs a clean interface:</p>
<blockquote>
<p>a project tree, access to code, a goal.</p>
</blockquote>
<p>And that’s why the recent trend is fascinating —</p>
<p>AI tools are slowly <a href="https://x.com/kilocode/status/1981019874563399794" rel="external nofollow noopener" class="lnp-link">releasing</a> <strong><a href="https://cursor.com/cli" rel="external nofollow noopener" class="lnp-link">CLI</a> <a href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/" rel="external nofollow noopener" class="lnp-link">equivalents</a></strong>.</p>
<p>Why? Because CLIs are where real automation lives.</p>
<p>They don’t need to simulate human clicks.</p>
<p>They just <em>do things.</em></p>
<p>Look at <strong>Claude Code</strong>.</p>
<p>It’s keyboard-first, agentic, workflow-oriented.</p>
<p>No panels, no mouse clicks.</p>
<p>Just you, the terminal, and an AI that can actually <em>act</em>.</p>
<p>We’re entering an era where your local environment looks less like VS Code</p>
<p>and more like a command center —</p>
<p>a space where you orchestrate AI agents that code, build, test, and deploy.</p>
<p>You’re still in control, but your role shifts.</p>
<p>From “person typing in an editor”</p>
<p>to “team lead directing a swarm of coding agents.”</p>
<p>Don’t get me wrong — IDEs aren’t evil. They’re amazing tools for focus and flow. But if you care about speed, reproducibility, and automation…</p>
<p>you’ll outgrow them.</p>
<p>Because when AI can understand your repo, navigate branches, run builds, and test code —</p>
<p>why would you confine it inside a tool built for human eyes and hands?</p>
<p>IDEs are optimized for humans, they’ve been around for decades. Suddenly we’re trying to retrofit AI coding agents inside IDEs or forking IDEs to have AI coding assistants as first class citizens, and it is severely limiting.</p>
<p>We’re heading somewhere new.</p>
<p>And the first step might just be typing “claude” instead of opening VS Code.</p>
]]></content:encoded></item><item><title>Be prepared to throw away your code</title><link>https://lakshminp.com/2025/10/throwaway-code/</link><pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/throwaway-code/</guid><category>essays</category><category>craft</category><description>I used to think good code was code that lasted forever.
Elegant abstractions. Perfect separation of concerns. The kind of architecture that would make senior engineers nod approvingly during code reviews I’d never actually have.
So I’d spend hours future-proofing everything.
Designing plugin systems for features that didn’t exist yet. Building configuration layers for options nobody asked for. Creating abstractions so flexible they could handle anything — except the one thing I actually needed to ship.</description><content:encoded><![CDATA[<p>I used to think good code was code that lasted forever.</p>
<p>Elegant abstractions. Perfect separation of concerns. The kind of architecture that would make senior engineers nod approvingly during code reviews I’d never actually have.</p>
<p>So I’d spend hours future-proofing everything.</p>
<p>Designing plugin systems for features that didn’t exist yet. Building configuration layers for options nobody asked for. Creating abstractions so flexible they could handle anything — except the one thing I actually needed to ship.</p>
<p>Then one day, I had to rip out an entire auth system I’d spent two weeks perfecting.</p>
<p>Not because it was broken. Because the product changed direction and suddenly we needed OAuth instead of email/password.</p>
<p>All those beautiful interfaces, those carefully crafted base classes, those thoughtful error hierarchies — deleted in an afternoon.</p>
<p>And you know what hurt most? It wasn’t the wasted time.</p>
<p>It was how <em>hard</em> it was to delete.</p>
<p>The auth logic had tendrils everywhere. Every controller imported it. Every model referenced it. Every test depended on it. Removing it felt like performing surgery on a patient who was still awake.</p>
<p>That’s when I learned the most important lesson about indie dev architecture:</p>
<p><strong>The best code isn’t the code that lasts forever. It’s the code that’s easy to throw away.</strong></p>
<h1 id="the-permanence-trap">The Permanence Trap</h1>
<p>We’re taught to write code like we’re building cathedrals.</p>
<p>Solid foundations. Careful planning. Structure that will stand for generations.</p>
<p>But indie SaaS isn’t a cathedral. It’s a sandcastle at high tide.</p>
<p>The market shifts. Users want something different. You realize your big idea was actually three smaller ideas wearing a trench coat.</p>
<p>If your code assumes permanence, change becomes painful. Every pivot feels like demolition. You’ll avoid necessary changes just because the refactor is too scary.</p>
<h1 id="what-disposable-design-actually-looks-like">What Disposable Design Actually Looks Like</h1>
<p>This doesn’t mean writing sloppy code. It means writing code that’s easy to replace when you inevitably need to.</p>
<p>Here’s a real example from my own codebase.</p>
<p><strong>The permanent version</strong> (what I used to write):</p>
<pre><code>class AuthenticationService:
    def __init__(self, token_provider, session_manager, audit_logger):
        self.token_provider = token_provider
        self.session_manager = session_manager
        self.audit_logger = audit_logger
    
    def authenticate(self, credentials):
        # Complex authentication logic spanning 50 lines
        # with calls to all the injected dependencies
        pass
    
    def refresh_token(self, old_token):
        # More complex logic intertwined with session management
        pass
    
    def validate_session(self, session_id):
        # Even more logic that assumes this exact architecture
        pass
</code></pre>
<p>Now imagine trying to swap this out for OAuth. You’d need to:</p>
<ul>
<li>
<p>Find every place that imports <code>AuthenticationService</code></p>
</li>
<li>
<p>Understand what each method does and how they’re used</p>
</li>
<li>
<p>Figure out which of your abstractions still make sense</p>
</li>
<li>
<p>Keep the whole system working while you rebuild it</p>
</li>
</ul>
<p><strong>The disposable version</strong> (what I write now):</p>
<pre><code>def login_user(email, password):
    “”“Log in with email and password. Returns user_id or None.”“”
    user = db.query(”SELECT * FROM users WHERE email = ?”, email)
    if user and check_password(password, user.password_hash):
        session_id = create_session(user.id)
        return user.id
    return None

def create_session(user_id):
    “”“Create a session for user. Returns session_id.”“”
    session_id = generate_token()
    db.execute(”INSERT INTO sessions (id, user_id) VALUES (?, ?)”, 
               session_id, user_id)
    return session_id
</code></pre>
<p>Look at the difference. No grand abstractions. No injection hierarchies. Just small functions that do one thing.</p>
<p>When I needed to switch to OAuth? I wrote new functions:</p>
<pre><code>def login_with_google(oauth_code):
    “”“Exchange Google OAuth code for user session.”“”
    google_user = fetch_google_user(oauth_code)
    user_id = get_or_create_user(google_user.email)
    return create_session(user_id)
</code></pre>
<p>Notice something? I reused <code>create_session</code> because it was generic enough. But <code>login_user</code>? Deleted. Gone. Didn’t even hesitate.</p>
<p>No refactoring. No careful extraction. Just wrote the new thing and removed the old thing.</p>
<h1 id="the-rules-of-disposable-code">The Rules of Disposable Code</h1>
<p><strong>1. Small, obvious functions over big, clever classes</strong></p>
<p>Classes accumulate dependencies. Functions are isolated. When you need to delete a function, you delete a function. When you need to delete a class, you delete a class <em>and</em> everything tangled up with it.</p>
<p><strong>2. Duplication is cheaper than the wrong abstraction</strong></p>
<p>I used to obsess over DRY (Don’t Repeat Yourself). Now I’m fine with a little repetition if it keeps things independent.</p>
<p>Two similar functions are easier to replace than one abstraction that tries to handle both cases.</p>
<p><strong>3. Keep your interfaces at the boundary, not everywhere</strong></p>
<p>You don’t need an interface for your database layer when you’re the only one touching it. You can always add it later when you have a reason.</p>
<p>Interfaces make sense at system boundaries — the edge where your code meets the world. Everywhere else, they’re just ceremony.</p>
<p><strong>4. Write code that explains itself, not code that needs explanation</strong></p>
<p>When something’s easy to delete, you need to understand it quickly. Clear names, simple flows, minimal indirection.</p>
<p>If you need to draw a diagram to explain how your authentication works, it’s probably too coupled to delete easily.</p>
<p>I know this is easier said than done, but it gets better as you consciously repeat it.</p>
<h1 id="when-permanence-actually-matters">When Permanence Actually Matters</h1>
<p>I’m not saying nothing should last.</p>
<p>Your database schema? That’s hard to change, so think it through.</p>
<p>Your user-facing API? Breaking that hurts customers, so be careful.</p>
<p>But your internal implementation? Your service layer? Your clever abstraction that makes you feel like a real engineer?</p>
<p>That stuff should be built like lego blocks, not concrete.</p>
<h1 id="the-real-discipline">The Real Discipline</h1>
<p>Here’s the paradox: writing disposable code takes more discipline than writing permanent code.</p>
<p>Permanent code lets you over-engineer. You can spend days on an abstraction and call it “planning ahead.”</p>
<p>Disposable code forces you to admit you don’t know the future. You solve today’s problem cleanly, knowing tomorrow might demand something different.</p>
<p>That’s harder. It requires restraint. It means resisting the urge to build the Grand Unified Framework when a simple function would do.</p>
<p>But it’s also freeing.</p>
<p>Because when the product changes — and it will — you won’t be buried under the weight of all your clever decisions.</p>
<p>You’ll just delete the old thing and write the new thing.</p>
<p>And keep shipping.</p>
<p><strong>TL;DR:</strong></p>
<p>Stop building for forever. Build for now, with the assumption that “now” is temporary. The best codebases aren’t the ones that never change — they’re the ones where change doesn’t hurt.</p>
]]></content:encoded></item><item><title>Don’t write a CD pipeline yet</title><link>https://lakshminp.com/2025/10/dont-write-a-cd-pipeline-yet/</link><pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/dont-write-a-cd-pipeline-yet/</guid><category>essays</category><category>craft</category><description>We love to automate things we barely understand. I’ve seen it in every team I’ve worked with — the moment an app runs on someone’s laptop, the next conversation is, “Let’s set up CI/CD.” Jenkins, GitHub Actions, ArgoCD — whatever’s shiny at the moment. It feels like progress. But most of the time, it’s premature optimization disguised as productivity.
If you’ve never deployed your app by hand, you don’t deserve automation yet. Harsh? Maybe. But true.</description><content:encoded><![CDATA[<p>We love to automate things we barely understand. I’ve seen it in every team I’ve worked with — the moment an app runs on someone’s laptop, the next conversation is, “Let’s set up CI/CD.” Jenkins, GitHub Actions, ArgoCD — whatever’s shiny at the moment. It feels like progress. But most of the time, it’s premature optimization disguised as productivity.</p>
<p>If you’ve never deployed your app by hand, you don’t deserve automation yet. Harsh? Maybe. But true.</p>
<p>Because until you’ve felt the friction of deployment — the SSH into your server, the environment variables you forgot to set, the database migration that didn’t run, the static files that didn’t refresh — you have no idea what you’re automating. You’re just encoding mystery and hope into your YAML.</p>
<p>Every good pipeline starts as a manual ritual.</p>
<p>You push code.</p>
<p>You build it.</p>
<p>You ship it.</p>
<p>You see what breaks.</p>
<p>You fix it.</p>
<p>You repeat.</p>
<p>Somewhere along the way, you start noticing patterns. “I always forget this one step.” “This command takes too long.” “This config should be parameterized.” That’s when automation makes sense — when it saves you from <em>known pain</em>, not imagined complexity.</p>
<p>A lot of engineers treat automation as a status symbol. If it’s not fully automated, it’s “not professional.” But automation without understanding is just faster failure. A broken pipeline that hides its steps is worse than no pipeline at all. At least with manual deploys, you see what’s happening.</p>
<p>When you deploy by hand, you learn how your system <em>breathes.</em> You watch logs scroll. You wait for the container to restart. You notice how long migrations take. You catch subtle things — an environment variable typo, a port binding issue, a permissions error — things that a pipeline will fail silently on, leaving you staring at a red ❌ with no clue why.</p>
<p>It’s like driving a stick shift before an automatic — once you’ve done it, you understand the gears. You feel the engine. Later, when the system drives itself, you’ll still know when something’s off.</p>
<p>I’ve seen teams automate their deploys too early and then spend weeks debugging the automation instead of the app. They ship to staging, something breaks, and nobody knows which part of the pipeline did it. It’s a house of cards built on blind trust.</p>
<p>Manual deploys are humbling. They slow you down — in a good way. They expose weak spots. And they give you confidence that you can recover when things go sideways. That’s the foundation you want before you bring in CI/CD.</p>
<p>Once you’ve done it manually a few times, automation becomes obvious. You’ll know which parts to script, which to leave flexible, and which deserve a human eye. That’s when your pipeline becomes a force multiplier instead of a black box.</p>
<p>So before you open GitHub Actions, before you write your first .yaml, do it yourself. SSH into the box. Run the commands(Or run kubectl/helm. Pick your poison). Watch the logs. Feel the pain. Because once you understand it deeply, you’ll automate with empathy — not arrogance. And that’s the kind of automation that lasts.</p>
]]></content:encoded></item><item><title>Did We Overthink Frontend?</title><link>https://lakshminp.com/2025/10/did-we-overthink-frontend/</link><pubDate>Sun, 05 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/did-we-overthink-frontend/</guid><category>essays</category><category>craft</category><description>I miss the 2000s. Not for the fashion. Not for the music. But for the sheer joy of opening a file called index.html, writing a few tags, sprinkling in some JavaScript (read: jQuery), and watching things just work. No build steps. No “npm install” that takes 3 minutes and a small prayer. No mysterious folders named dist or node_modules that collectively occupy more space than my entire operating system.
Back then, CSS was both cruel and kind. Sure, floats were a nightmare. But at least you knew where the pain came from. You weren’t wrestling with layers of abstraction, you were just yelling at Internet Explorer and calling it a day.</description><content:encoded><![CDATA[<p>I miss the 2000s. Not for the fashion. Not for the music. But for the sheer joy of opening a file called <code>index.html</code>, writing a few tags, sprinkling in some JavaScript (read: jQuery), and watching things just work. No build steps. No “npm install” that takes 3 minutes and a small prayer. No mysterious folders named <code>dist</code> or <code>node_modules</code> that collectively occupy more space than my entire operating system.</p>
<p>Back then, CSS was both cruel and kind. Sure, floats were a nightmare. But at least you knew where the pain came from. You weren’t wrestling with layers of abstraction, you were just yelling at Internet Explorer and calling it a day.</p>
<p>I still remember deploying websites via FTP. You edited the file live on the server. It was cowboy coding. It was wrong. It was also&hellip; fast.</p>
<p>Contrast that with today. Want to build a button? Great. First, pick your framework: React, Vue, Svelte, Solid, Astro, Qwik&hellip; feeling dizzy yet? Then set up your linter, your formatter, your pre-commit hook. Configure your TypeScript paths. Resolve some cryptic Webpack error about “Cannot read property ‘undefined’ of null”. Finally, install a UI library, override all the defaults, and write ten lines of Tailwind just to get a pill-shaped button with a drop shadow.</p>
<p>And that button still looks different on Safari.</p>
<p>Modern devs love to tout “Developer Experience.” Which is ironic, because the actual experience feels more like IKEA furniture assembly — except every piece came from a different warehouse, and you’re not sure if you accidentally installed a kitchen cabinet in your login form.</p>
<p>We’ve optimized the hell out of everything. Everything except joy.</p>
<p>It’s not that the 2000s were better. They were <em>simpler</em>. You didn’t need a mental model for hydration or SSR or static generation strategies. You just wrote code, hit refresh, and moved on. Today, you hit refresh and wait for the build to compile while questioning your life choices.</p>
<p>Look, I’m not saying we throw out our modern toolchains and go back to editing HTML in Notepad. I like my hot reloads. I love TypeScript (well, most days). But maybe, just maybe, the past had something to teach us: that frictionless creation matters. That not every project needs a monorepo. That clarity beats cleverness.</p>
<p>So the next time you’re six hours into configuring your <code>tsconfig.json</code>, ask yourself: what would the 2009 me do?</p>
<p>He’d probably already be done with the project.</p>
<p>Maybe it’s time to make frontend fun again.</p>
]]></content:encoded></item><item><title>AI Can Build Anything—Except Product Taste</title><link>https://lakshminp.com/2025/10/ai-product-taste/</link><pubDate>Sat, 04 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/ai-product-taste/</guid><category>essays</category><category>craft</category><description>Everyone says AI makes you “10x more productive.” I’m not sure about that. What it actually made me is… more deliberate.
When execution is basically free, the bottleneck shifts. It’s no longer can I build this? It’s should I build this?
That sounds obvious, but most of us (me included) are terrible at it. We confuse motion for progress. And AI just cranks the treadmill speed up to 11.
Here’s what I mean.</description><content:encoded><![CDATA[<p>Everyone says AI makes you “10x more productive.” I’m not sure about that. What it actually made me is… more deliberate.</p>
<p>When execution is basically free, the bottleneck shifts. It’s no longer <em>can I build this?</em> It’s <em>should I build this?</em></p>
<p>That sounds obvious, but most of us (me included) are terrible at it. We confuse motion for progress. And AI just cranks the treadmill speed up to 11.</p>
<p>Here’s what I mean.</p>
<p>The other weekend, I let myself play around with the idea of “AI-generated developer dashboards.” Normally, something like that would eat a week of evenings. This time, I had three versions running before breakfast: a React prototype, a Python backend spitting out metrics, and a half-decent mock landing page.</p>
<p>Impressive? Maybe. Useful? Not really. By Sunday night I realized I’d basically built three beautifully useless toys. Execution had been trivial. The problem was never execution—it was me chasing shiny objects.</p>
<p>That’s the AI paradox. It lowers the cost of building so much that the real scarcity becomes <em>taste</em>. Judgment. The ability to say no.</p>
<p>Because here’s the dark side: the opportunity cost of distraction just went up. Before, if I burned a week tinkering on something dumb, at least I learned a few low-level tricks. Now I can burn a week and end up with a full microservice, a CI/CD pipeline, and a Terraform config… for an idea that didn’t deserve any of it. Congratulations, I’ve industrialized my dead ends.</p>
<p>I’ve caught myself doing this with infrastructure experiments, too. AI will happily generate Kubernetes manifests, Helm charts, and CI workflows for whatever hair-brained service I throw at it. The code even looks plausible at first glance. Then I deploy it, watch it explode, and realize the whole thing never needed to exist in the first place. It’s the most polished waste of time imaginable.</p>
<p>And this is why restraint has suddenly become a superpower. The real work isn’t generating more; it’s filtering harder. AI will give you 50 rabbit holes before lunch. If you’re not ruthless about which one you go down, you’re just automating your own distraction.</p>
<p>The old mantra was “ship fast and break things.” AI makes that easier than ever. But there’s a hidden multiplier effect: fast execution with bad strategy doesn’t just fail—it fails <em>louder</em>. You don’t just waste time, you waste time at scale. Meanwhile, the teams with clear strategy and discipline can use the exact same tools to compound wins. Same technology, wildly different outcomes.</p>
<p>This is why I think “thinking” has quietly become underrated. Tinkering used to be the path to learning. Now tinkering is dangerous. You can dig a perfect hole in the wrong place faster than ever. Spending more time deciding where to dig—that’s the skill worth leveling up.</p>
<p>Developers don’t usually like to hear that. We want to build. But in an AI-first world, the rarest and most valuable act might be… <em>not</em> building. Closing the tab. Saying no to the prototype. Choosing boredom over the dopamine hit of “look what I got running.”</p>
<p>So no, AI didn’t make me more productive. It made me picky. It forced me to care about what I was building in the first place.</p>
<p>And that’s the paradox: AI made execution trivial, so the premium is now on taste, judgment, and focus.</p>
<p>If you can’t decide what matters, AI will happily help you drown in what doesn’t.</p>
]]></content:encoded></item><item><title>5 Ways to Survive an Inherited Codebase</title><link>https://lakshminp.com/2025/10/dealing-with-bad-code/</link><pubDate>Fri, 03 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/dealing-with-bad-code/</guid><category>essays</category><category>craft</category><description>We spend more time reading code than writing it. And most of that time? We’re reading someone else’s code. Chances are, it’s not an uplifting experience.
Maybe it was a rushed MVP. Maybe it’s a legacy system built by three devs who’ve all since disappeared into the ether. Maybe it’s your code from six months ago, which is somehow worse.
Whatever the case, you’ve inherited it now. Congrats. Here are five things to do when faced with a gnarly codebase that makes you question your career choices.</description><content:encoded><![CDATA[<p>We spend more time reading code than writing it. And most of that time? We’re reading someone else’s code. Chances are, it’s not an uplifting experience.</p>
<p>Maybe it was a rushed MVP. Maybe it’s a legacy system built by three devs who’ve all since disappeared into the ether. Maybe it’s <em>your</em> code from six months ago, which is somehow worse.</p>
<p>Whatever the case, you’ve inherited it now. Congrats. Here are five things to do when faced with a gnarly codebase that makes you question your career choices.</p>
<h2 id="1-read-it-like-an-archaeologist-not-a-critic"><strong>1. Read It Like an Archaeologist, Not a Critic</strong></h2>
<p>You’re not here to judge. You’re here to understand. Pretend you’re brushing dust off a piece of ancient tech, not roasting it on Twitter.</p>
<p>Resist the temptation to label everything “garbage.” Start by asking: what was this trying to do? What constraints might they have had? What shortcuts were probably necessary?</p>
<p>Instead of rewriting it all from scratch (which you won’t), focus on uncovering the original intentions. Sometimes the logic is buried under years of duct tape — but there <em>was</em> logic once.</p>
<blockquote>
<p>Pro move: jot down confusing patterns as questions, not accusations. “Why is this loop reassigning itself?” is better than “wtf is this trash.” It’ll help your future debugging and preserve your sanity.</p>
</blockquote>
<h2 id="2-run-it-break-it-run-it-again"><strong>2. Run It. Break It. Run It Again.</strong></h2>
<p>Before you touch a single line, get it running. Locally. In a container. In staging. On a Raspberry Pi duct-taped to your modem. Whatever it takes.</p>
<p>You need to see the beast in motion. Trigger features, hit edge cases, try invalid input. Watch how it responds. Some things will crash spectacularly. Others will weirdly work.</p>
<p>This is how you learn the terrain. Think of it as a reconnaissance mission, not a rescue operation.</p>
<blockquote>
<p>And yes, this might involve reading a README last updated in 2019 or figuring out why it depends on a deprecated npm package called leftpad-magic.</p>
</blockquote>
<h2 id="3-map-the-landmines"><strong>3. Map the Landmines</strong></h2>
<p>There are always a few “do not touch” areas. Legacy functions nobody understands. Cron jobs that magically keep the business running. Data pipelines held together by tab-delimited CSVs and prayer.</p>
<p>Map these out. Comment them. Annotate them. Make a living doc if you need to. This isn’t overengineering — it’s survival.</p>
<p>Because one day, someone (you?) <em>will</em> try to refactor a critical part at 4:45 PM on a Friday. Your notes might be the thing that prevents a full-blown incident.</p>
<blockquote>
<p>Think of it like marking traps in a dungeon. It’s not glamorous, but future-you will thank present-you.</p>
</blockquote>
<h2 id="4-find-the-one-smart-thing"><strong>4. Find the “One Smart Thing”</strong></h2>
<p>Even the ugliest codebases have one bit of elegant, well-considered design. Maybe it’s a clever bit of caching. Maybe the data model actually anticipates edge cases. Maybe some long-forgotten dev wrote a shell script that works <em>flawlessly</em> every single time.</p>
<p>Find that piece. Admire it. Then steal the pattern.</p>
<p>Because buried in the wreckage of bad code are usually hints of what the author <em>wanted</em> the system to be — before it devolved into spaghetti.</p>
<blockquote>
<p>Recognizing that “one smart thing” also gives you a thread to pull on if you ever do get the green light to refactor properly.</p>
</blockquote>
<h2 id="5-start-small-fix-what-you-touch"><strong>5. Start Small. Fix What You Touch.</strong></h2>
<p>The heroic rewrite is a fantasy. You will not rebuild the system in two weeks with clean architecture and perfect tests. You will get halfway, then get pulled into sprint planning or on-call.</p>
<p>Instead, fix things incrementally. Touching a function? Refactor it. See a poorly named variable? Rename it. Writing a feature? Add tests for the nearby stuff.</p>
<p>This is the Boy Scout Rule: leave the code a little better than you found it.</p>
<blockquote>
<p>Over time, these small changes compound. You build trust with teammates. You make future maintenance suck slightly less. You stop fearing the codebase.</p>
</blockquote>
<h2 id="coming-soon"><strong>Coming Soon…</strong></h2>
<p>I’ll be expanding each of these into standalone posts, diving deeper into how to:</p>
<ul>
<li>
<p>Reverse-engineer legacy logic without losing your mind</p>
</li>
<li>
<p>Use staging environments and logs as your secret weapons</p>
</li>
<li>
<p>Build minimal but helpful internal docs around landmines</p>
</li>
<li>
<p>Spot (and reuse) the clever patterns buried in legacy code</p>
</li>
<li>
<p>Actually make progress on refactoring, even with deadlines</p>
</li>
</ul>
<p>And yes — I’ll include a version for folks who use AI coding assistants (whether it’s Claude, Augment, Copilot, or whatever else). They’re great at speeding things up… and occasionally making legacy code worse in new and exciting ways.</p>
<p><strong>TL;DR:</strong> You will inherit bad code. It <em>will</em> suck. But with a little strategy — and some sarcasm — you can survive it, improve it, and maybe even learn a thing or two from it.</p>
]]></content:encoded></item><item><title>It Was Just a Primary Key. What Could Go Wrong?</title><link>https://lakshminp.com/2025/09/uuid-primary-key-mistake/</link><pubDate>Tue, 30 Sep 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/09/uuid-primary-key-mistake/</guid><category>essays</category><category>craft</category><description>If you ever want to feel smart and slowly ruin your app’s performance, I highly recommend using UUIDs as your primary keys. Works like a charm.
Like many backend developers, I once believed UUIDs were a sign of architectural maturity. They’re globally unique! Secure! Future-proof! How could that possibly backfire?
So I used UUIDs for everything. Users. Orders. Logs. Probably my lunch orders too.
Everything was fine… until one day, our dashboard took 5 seconds to load user stats. Five. Full. Seconds. That’s an eternity when you’re trying to look competent in front of a customer.</description><content:encoded><![CDATA[<p>If you ever want to feel smart <em>and</em> slowly ruin your app’s performance, I highly recommend using UUIDs as your primary keys. Works like a charm.</p>
<p>Like many backend developers, I once believed UUIDs were a sign of architectural maturity. They’re globally unique! Secure! Future-proof! How could that possibly backfire?</p>
<p>So I used UUIDs for everything. Users. Orders. Logs. Probably my lunch orders too.</p>
<p>Everything was fine… until one day, our dashboard took 5 seconds to load user stats. Five. Full. Seconds. That’s an eternity when you’re trying to look competent in front of a customer.</p>
<p>At first, I did what any responsible founder does: I blamed Heroku. Then I blamed Postgres. Then I ran <code>pg_stat_user_indexes</code>.</p>
<p>And there it was. My precious users table had an index so bloated it looked like it had been living off pizza and regret. The B-tree was a mess—fragmented by months of inserting completely random UUIDs. Every new user was wedging itself into a random place in the index like a toddler shoving Legos into a DVD player.</p>
<p>The root cause? UUIDs don’t play nicely with B-tree indexes. They’re not sequential. So instead of nice, ordered inserts, you get chaos—page splits, cache misses, and a slowly dying database.</p>
<p><strong>The fix?</strong></p>
<p>I switched to <code>uuid_generate_v1mc()</code>, which creates roughly time-sortable UUIDs. Performance got better. My ego… stayed bruised.</p>
<p><strong>So here’s the rule of thumb.</strong></p>
<p>UUIDs aren’t bad. They’re just not magic.</p>
<p><strong>Use them when</strong></p>
<ul>
<li>
<p>You need to generate IDs across distributed systems.</p>
</li>
<li>
<p>You don’t want people guessing URLs (/reset-password/:id).</p>
</li>
<li>
<p>You’re migrating or merging datasets and need guaranteed uniqueness.</p>
</li>
</ul>
<p><strong>Avoid them when</strong></p>
<ul>
<li>
<p>You care about write performance and index size.</p>
</li>
<li>
<p>You’re joining on them frequently.</p>
</li>
<li>
<p>You want to be able to debug without going cross-eyed.</p>
</li>
</ul>
<p>Or to put it another way:</p>
<p>If you wouldn’t tattoo a UUID on your arm, maybe don’t use it as your primary key.</p>
]]></content:encoded></item><item><title>Your Python Web App is a Memory Hog. Admit It.</title><link>https://lakshminp.com/2025/09/your-python-web-app-is-a-memory-hog/</link><pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/09/your-python-web-app-is-a-memory-hog/</guid><category>essays</category><category>craft</category><description>We’ve all seen this movie:
You build a “simple” Flask or Django app. You dockerize it. You deploy it on Kubernetes. Then you look at your node metrics and wonder why your app is eating memory like a Chrome tab farm.
And here comes the uncomfortable truth: Go apps don’t do this.
Concurrency: Built-In vs. Bolted-On Python still has the Global Interpreter Lock. AsyncIO, threads, gevent: all clever duct tape.</description><content:encoded><![CDATA[<p>We’ve all seen this movie:</p>
<p>You build a “simple” Flask or Django app. You dockerize it. You deploy it on Kubernetes. Then you look at your node metrics and wonder why your app is eating memory like a Chrome tab farm.</p>
<p>And here comes the uncomfortable truth: <strong>Go apps don’t do this.</strong></p>
<h3 id="concurrency-built-in-vs-bolted-on"><strong>Concurrency: Built-In vs. Bolted-On</strong></h3>
<p>Python still has the <strong>Global Interpreter Lock</strong>. AsyncIO, threads, gevent: all clever duct tape.</p>
<p>Go’s goroutines? Native. Dirt cheap. A Go service can juggle tens of thousands of requests without you even thinking about it. Meanwhile, your Python service is scaling horizontally like it’s on cloud provider commission.</p>
<h3 id="deployment-one-binary-vs-frankenstack"><strong>Deployment: One Binary vs. Frankenstack</strong></h3>
<p>Running Python in production is like dragging a circus into your Pod spec:</p>
<ul>
<li>
<p>Gunicorn or Uvicorn</p>
</li>
<li>
<p>WSGI or ASGI adapters</p>
</li>
<li>
<p>Reverse proxy like Nginx</p>
</li>
<li>
<p>Virtualenvs just to keep packages sane</p>
</li>
</ul>
<p>Each one is another container, another moving part, another thing to patch.</p>
<p>Go? <strong>One binary. One container. One Deployment.</strong> Done.</p>
<h3 id="memory-lean-pods-vs-hungry-pods"><strong>Memory: Lean Pods vs. Hungry Pods</strong></h3>
<p>Here’s what happens under load:</p>
<ul>
<li>
<p>Go Pod: ~30–50 MB steady, serving requests.</p>
</li>
<li>
<p>Python Pod: 200–300 MB <em>per worker</em>.</p>
</li>
</ul>
<p>Kubernetes doesn’t care about your excuses. Requests and limits balloon, the autoscaler spins up more nodes, and your cloud bill burns.</p>
<p>The funny part? Your app isn’t even complex. The runtime is.</p>
<h3 id="magic-vs-predictability"><strong>Magic vs. Predictability</strong></h3>
<p>Python is “dynamic.” Translation: you find out about your bugs at runtime, usually while your Pod is CrashLooping.</p>
<p>Go is boring. Strict. Unforgiving at compile time. But once that binary ships, it just runs. Kubernetes likes boring. Operators like boring. Your SRE team loves boring.</p>
<h3 id="ecosystem-ai-crown-vs-web-pretender"><strong>Ecosystem: AI Crown vs. Web Pretender</strong></h3>
<p>Yes, Python owns the AI/ML world. If you’re training models, you’re not reaching for Go.</p>
<p>But for web apps? The Python ecosystem is bloated and patchwork. Go’s standard library gives you a production-ready HTTP server out of the box. No circus, no adapters, no excuses.</p>
<p>Python is fantastic for notebooks, scripts, and ML. But for web apps in Kubernetes? It’s a bloated liability.</p>
<p>Go apps scale cleaner, run cheaper, and keep clusters saner.</p>
<p>Stop pretending Python web apps are fine in production. They’re not. They’re expensive. They’re messy. And they’re only still around because developers are in denial.</p>
<p>Sometimes the boring choice is the right one. In Kubernetes, boring wins.</p>
]]></content:encoded></item><item><title>The Language That Wasn’t Supposed to Be One</title><link>https://lakshminp.com/2025/09/yaml-accidental-language/</link><pubDate>Tue, 23 Sep 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/09/yaml-accidental-language/</guid><category>essays</category><category>craft</category><description>If you hang around DevOps Twitter or Reddit long enough, you’ll stumble across a familiar fight: “Why does Terraform use HashiCorp Configuration Language (HCL) instead of a real programming language?”
Half the crowd insists HCL is the perfect middle ground. The other half sees it as YAML’s slightly hipper cousin — still clunky, but with more curly braces.
So, how did we end up with HCL at the center of infrastructure-as-code? And what would’ve happened if Terraform had just picked Python or Go instead?</description><content:encoded><![CDATA[<p>If you hang around DevOps Twitter or Reddit long enough, you’ll stumble across a familiar fight: <em>“Why does Terraform use HashiCorp Configuration Language (HCL) instead of a real programming language?”</em></p>
<p>Half the crowd insists HCL is the perfect middle ground. The other half sees it as YAML’s slightly hipper cousin — still clunky, but with more curly braces.</p>
<p>So, how did we end up with HCL at the center of infrastructure-as-code? And what would’ve happened if Terraform had just picked Python or Go instead?</p>
<h1 id="hcls-origin-storyi-think">HCL’s Origin Story(I think)</h1>
<p>Terraform needed something that ticked a very specific set of boxes:</p>
<ul>
<li>
<p><strong>Human-readable syntax</strong>: not just for developers, but for ops folks and managers who only glance at infra configs.</p>
</li>
<li>
<p><strong>Declarative, not imperative</strong>: you declare what you want, Terraform figures out <em>how</em> to get there.</p>
</li>
<li>
<p><strong>Machine-friendly</strong>: structured enough for parsing and state reconciliation, flexible enough for humans to write without crying.</p>
</li>
</ul>
<p>JSON was technically supported from day one. Nobody used it. Too verbose, too painful. HCL struck the balance.</p>
<h1 id="why-not-just-use-a-real-language">Why Not Just Use a “Real” Language?</h1>
<p>Imagine Terraform configs in Python. Sounds nice. Until you realize you’d need to write orchestration logic for every single dependency. Want an S3 bucket before your EC2 instance? Better remember to write that <code>await_bucket()</code> function.</p>
<p>Terraform’s declarative model saves us from that nightmare. By hiding the “how,” it:</p>
<ul>
<li>
<p>Reduces mistakes and redundancy.</p>
</li>
<li>
<p>Keeps configs readable across teams, even for folks who aren’t software engineers.</p>
</li>
<li>
<p>Manages state safely (rollbacks in Python would be a comedy of errors).</p>
</li>
</ul>
<p>General-purpose languages give you infinite flexibility. But in infrastructure, infinite flexibility often means infinite footguns.</p>
<h1 id="the-trade-off">The Trade-off</h1>
<p>Of course, HCL isn’t perfect. Anyone who’s wrestled with <code>for_each</code> and dynamic blocks knows it can feel like you’re fighting the language. And yes, sometimes you wish you could just drop a real loop or function in there.</p>
<p>But that friction is intentional. HCL’s “simplicity ceiling” prevents configs from turning into full-blown software projects. Terraform wants you thinking about infrastructure states, not writing infra-flavored spaghetti code.</p>
<p>The fight over HCL isn’t really about syntax. It’s about philosophy: <strong>do we want infrastructure to look like code, or like configuration?</strong></p>
<p>HashiCorp bet on configuration — and for all the complaints, that bet made Terraform the standard. If it had been a Python library, it probably would’ve ended up as just another provisioning framework.</p>
<p>HCL isn’t beautiful, but it’s pragmatic. It forces us to think declaratively, keeps the focus on infra states, and saves us from writing orchestration logic by hand.</p>
<p>So the next time someone complains “Why not just use Go/Python/TypeScript?”, the answer is simple:<br>
Terraform doesn’t want you to write software. It wants you to write infrastructure.</p>
]]></content:encoded></item></channel></rss>