<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/"
      xmlns:social="http://namanyayg.com/social-metrics/1.0"
      >
  <generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator>
  <link href="https://nmn.gl/blog/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://nmn.gl/blog/" rel="alternate" type="text/html" />
  <updated>2026-04-08T03:57:51+00:00</updated>
  <id>https://nmn.gl/blog/feed.xml</id>

  
  
    <title type="html">N’s Blog</title>
  

  
    <subtitle>Thoughts on AI, startups, and life by Namanyay.</subtitle>
  

  
    <author>
        <name>namanyayg</name>
      
      
    </author>
  

  
  
  
  
  
  
    <entry>
      

      <title type="html">AI is Killing B2B SaaS</title>
      <link href="https://nmn.gl/blog/ai-killing-b2b-saas" rel="alternate" type="text/html" title="AI is Killing B2B SaaS" />
      <published>2026-02-04T00:00:00+00:00</published>
      <updated>2026-02-04T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-killing-b2b-saas</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-killing-b2b-saas"><![CDATA[<p>SaaS is the most profitable business model on Earth.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> It’s easy to understand why: build once, sell the same thing again ad infinitum, and don’t suffer any marginal costs on more sales.</p>

<p>I have been writing software for more than half my life. In the last year itself, I’ve talked to hundreds of founders and operators in SF, from preseed to Series E companies.</p>

<p>AI is bringing an existential threat to a lot of B2B SaaS executives: How to keep asking customers for renewal, when every customer <em>feels</em> they can get something better built with vibe-coded AI products?</p>

<p>And the market is pricing it in. Morgan Stanley’s SaaS basket has <a href="https://www.bloomberg.com/news/articles/2026-01-18/-no-reasons-to-own-software-stocks-sink-on-fear-of-new-ai-tool">lagged the Nasdaq by 40 points</a> since December. HubSpot and Klaviyo are down ~30%. Analysts are writing notes titled “No Reasons to Own” software stocks.</p>

<figure>
  <img src="/blog/assets/saas-stocks.png" />
  <figcaption>The market is reflecting our new reality (Source: Bloomberg)</figcaption>
</figure>

<!--more-->

<h2 id="the-relation-between-vibe-coding-and-b2b-saas-sales">The relation between vibe coding and B2B SaaS sales</h2>

<p>The new problem for B2B SaaS is that with AI, customers can get <em>something</em> working with vibe coding. There are tens of vibe coding “internal tool” services that promise to connect to every integration in the world to pump out CRUD and workflow apps.</p>

<p>Whatever they build <em>simply works</em>. It takes some wrangling to get there (one Series C VP listed <strong>eleven different</strong> vibe coding tools they’ve tried and the pros and cons between each on a phone call once), but productivity gains are immediate.</p>

<p>And vibe coding is fun. You feel like a mad wizard using the right incantation <sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup> to get this magical new silicon intelligence to do exactly what you want.</p>

<p>What they don’t know, though, is that a poorly architected system will fail, eventually. As every senior programmer (eventually) understands, our job is complex because we have to understand the relationships in the real world, the processes involved, and the workflows needed, and representing it in a robust way to create a stable system. AI can’t do that.</p>

<p>Non-programmers don’t know any of this nuance. One Series E CEO told me that they’re re-evaluating the quarterly renewal of their engineering productivity software because they along with an engineer reimplemented something using Github and Notion APIs. They were paying $30,000 to a popular tool<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup> <strong>and they were not going to renew anymore.</strong></p>

<h2 id="how-does-it-impact-b2b-sales">How does it impact B2B sales?</h2>

<p>If customers feel like they aren’t being served exactly like they want to, they are more likely to churn. The reason behind all this is that customers are demanding more from their B2B vendors, because they know what’s possible.</p>

<p>Previously, you would change <em>your company</em> to fit what your ERP and pay them hundreds of thousands of dollars. Now, everyone can see that agentic coding makes an unprecedented level of flexibility possible. And customers are demanding that flexibility, and if they don’t get it, they’ll leave.</p>

<p>This week itself I was on a phone call with a Series B AE talking about how they’re potentially losing an $X00,000 account just because the customer can’t use a specific failure reporting workflow in the SaaS. They’re now working with me to build what the customer needs and retain them.</p>

<h2 id="how-to-survive">How to survive</h2>

<h3 id="1-be-a-system-of-record">1. Be a System of Record</h3>

<p>If the entire company’s workflows operates on your platform, i.e. you’re a line-of-business SaaS, you are integrated into their existing team already. They know your UI and rely on you on the day to day.</p>

<p>For example, to create a data visualization I won’t seek any SaaS. I’ll just code one myself using many of the popular vibe coding tools <em>(my team actually did that and it’s vastly more flexible than what we’d get off-the-shelf).</em></p>

<p>Being a “System of Record” means you’re embedded so deeply that there’s no choice but to win. My prediction is that we’ll see more SaaS companies go from the application layer to offering their robust SoR as their primary selling point.</p>

<h3 id="2-security-authentication-and-robustness">2. Security, authentication, and robustness</h3>

<p>This is where vibe-coded apps silently fail — and where established SaaS platforms earn their keep.</p>

<p>When a non-technical team vibe-codes an internal tool, they’re not thinking about environment keys, XSS vulnerabilities or API keys hardcoded in client-side JavaScript. They’re not implementing rate limiting, audit logs, or proper session management. They’re definitely not thinking about SOC 2 compliance, GDPR data residency requirements, or HIPAA audit trails.</p>

<p>I’ve seen it firsthand: a finance team built a “quick” expense approval tool that stored unencrypted reports in a public S3 bucket. A sales ops team created a commission calculator that anyone with the URL could access — no auth required. These aren’t edge cases. They’re the norm when software is built without security as a foundational concern.</p>

<p>Enterprise SaaS platforms have spent years (and millions) solving these problems: role-based access control, encryption at rest and in transit, penetration testing, compliance certifications, incident response procedures. Your customers may not consciously value this — until something breaks.</p>

<p>The challenge is that security is invisible when it works. You need to communicate this value proactively: remind customers that the “simple” tool they could vibe-code themselves would require them to also handle auth, permissions, backups, uptime, and compliance.</p>

<h3 id="3-adapt-to-the-customer-not-the-other-way-around">3. Adapt to the customer, not the other way around</h3>

<p>The times of asking customers to change how they work are gone. Now, SaaS vendors that differentiate by being ultra customizable win the hearts of customers.</p>

<p>How? It’s the most powerful secret to increase usage. We’ve all heard the classic SaaS problem where the software is sold at the beginning of the year, but no one actually ends up using it because of how inflexible it is and the amount of training needed.</p>

<p>And if a SaaS is underutilized, it gets noticed. And that leads to churn.</p>

<p>This is the case with one of my customers, they have a complex SaaS for maintenance operations. But turns out, this was not being used at the technician level because they found the UI too complex<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">4</a></sup>.</p>

<p>How I’m solving this is essentially a whitelabelled vibe-coding platform with in-built distribution and secure deployments. When they heard of my solution they were immediately onboard. Their customer success teams quickly coded a very specific mobile webapp for the technicians to use and deployed it in a few days.</p>

<p>Now, the IC technician is exposed to just those parts of the SaaS that they care about i.e. creating maintenance work orders. The executives get what they want too, vibe coding custom reports exactly the way they want vs going through complicated BI config. They are able to build exactly what they want and feel like digital gods while doing it.</p>

<p><strong>Usage for that account was under 35%, and is now over 70%.</strong> They are now working closely with me to vibe code new “micro-apps” that work according to all of their customer workflows. And the best part? This is all on top of their existing SaaS which works as a system of record and handles security, authentication, and supports lock-in by being a data and a UI moat.</p>

<p>This is exactly what I’m building: a way for SaaS companies to let their end-users vibe code on top of their platform (More on that below). My customers tell me it’s the best thing they’ve done for retention, engagement, and expansion in 2026 – because when your users are building on your platform, they’re not evaluating your competitors.</p>

<h2 id="the-real-shift">The Real Shift</h2>

<p>Here’s what I’ve realized after hundreds of conversations with founders and operators: AI isn’t killing B2B SaaS. It’s killing B2B SaaS <strong>that refuses to evolve</strong>.</p>

<p>The SaaS model was built on a simple premise: we build it once, you pay forever. That worked when building software was hard. But now your customers have tasted what’s possible. They’ve seen their finance team whip up a custom dashboard in an afternoon. They’ve watched a non-technical PM build an internal tool that actually fits their workflow.</p>

<p>You can’t unsee that. You can’t go back to paying $X0,000/year for software that almost does what you need.</p>

<p>The survivors won’t be the SaaS companies with the best features. They’ll be the ones who become platforms – who let customers build <em>on top of</em> them instead of <em>instead of</em> them. When I showed a well-known VC what I was building to help SaaS companies do exactly this, he said: “This is the future of marketplaces and software companies.”</p>

<p>Maybe. Or maybe this is just another cycle and traditional SaaS will adapt like it always has. But I know this: the companies I’m talking to aren’t waiting around to find out. They’re already rebuilding their relationship with customers from “use our product” to “build on our platform.”</p>

<p>The question isn’t whether AI will eat your SaaS.</p>

<p>It’s whether you’ll be the one holding the fork.</p>

<hr />

<p><strong>I’m solving exactly this problem with a whitelabelled AI platform for B2B SaaS companies</strong>, so your users can vibe code customized workflows on top of their existing system of record.</p>

<p>My customers tell me this is the <strong>best way to support retention, engagement, and expansion</strong> in 2026. If this sounds interesting to you or someone you know, <a href="javascript:void(0)" onclick="showDemoPopup()">I can reach out with a custom demo</a> or you can <a target="_blank" href="https://gigacatalyst.com">learn more about Gigacatalyst</a>.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Whenever I bring a new friend to the Salesforce Park, they are in absolute awe. And, the meme remains true that no one even knows what Salesforce does. Whatever they’re doing, they’re clearly earning enough revenue to purchase multiple blocks in SF. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>a.k.a. “prompt engineering” which is not engineering at all but that’s a different blog post. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I won’t name any names, but the company’s named after an invertebrate animal. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>And who can blame them – I still feel a pang of anxiety when I look at my sales CRM. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="saas" />
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[SaaS is the most profitable business model on Earth.1 It’s easy to understand why: build once, sell the same thing again ad infinitum, and don’t suffer any marginal costs on more sales. I have been writing software for more than half my life. In the last year itself, I’ve talked to hundreds of founders and operators in SF, from preseed to Series E companies. AI is bringing an existential threat to a lot of B2B SaaS executives: How to keep asking customers for renewal, when every customer feels they can get something better built with vibe-coded AI products? And the market is pricing it in. Morgan Stanley’s SaaS basket has lagged the Nasdaq by 40 points since December. HubSpot and Klaviyo are down ~30%. Analysts are writing notes titled “No Reasons to Own” software stocks. The market is reflecting our new reality (Source: Bloomberg) Whenever I bring a new friend to the Salesforce Park, they are in absolute awe. And, the meme remains true that no one even knows what Salesforce does. Whatever they’re doing, they’re clearly earning enough revenue to purchase multiple blocks in SF. &#8617;]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/saas-stocks.png" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/saas-stocks.png" />
      

      
        <social:metrics>
          
            <social:views>50,000</social:views>
          
          
          
            <social:hackernews>510+ votes</social:hackernews>
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Vibe Coding Is Creating Braindead Coders</title>
      <link href="https://nmn.gl/blog/vibe-coding-gambling" rel="alternate" type="text/html" title="Vibe Coding Is Creating Braindead Coders" />
      <published>2025-09-13T00:00:00+00:00</published>
      <updated>2025-09-13T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/vibe-coding-gambling</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/vibe-coding-gambling"><![CDATA[<p>Confession: I’ve been using Claude Code to write all my code for me. And I think it’s making me worse at the thing I’ve loved doing for twelve years.</p>

<p>I can clearly see how AI coding is rewiring our brains – it makes developers crave instant gratification instead of deep understanding, and reduces us to  <strong>gamblers</strong> who pull levers for the next <em>hit</em> of working code.</p>

<p>If this is happening to me, someone who learned to code in the pre-AI era, <strong>what’s it doing to junior developers who’ve never known anything else?</strong></p>

<!--more-->

<h2 id="solving-hard-problems">Solving Hard Problems</h2>

<p>At the age of 16, alone in my room in New Delhi, I was completely obsessed with this problem. Want to try solving this with me?</p>

<figure>
  <img src="/blog/assets/euler-lattice-paths.png" alt="Project Euler 'Lattice Paths' problem" style="max-width: 30em; width: 100%;" />
</figure>

<p>It’s from the site called <a href="https://projecteuler.net/">“Project Euler”</a>, one of the greatest resource for programming challenges that combine math with your data structures and algorithms knowledge. This particular one is Problem 15 “Lattice Paths.” Like all Project Euler problems, it can be stated simply enough.</p>

<p>The problem already shows us how it looks like for 2x2, and you can sketch out 3x3 easily as well (try it out, that was the first thing I did). But, the challenge actually comes when you try to visualize the scale – can you imagine how large the result must be for a 20x20 grid?</p>

<p>I spent <em>weeks</em> on this thing – scratching diagrams on every piece of paper I could find, bothering my math teachers until they avoided me, and even dreaming about algorithms on a few nights.</p>

<p>Can you imagine spending <em>weeks</em> on a single problem today? Just thinking, failing, thinking more?</p>

<p>I tried everything. Brute force. Recursion. Drawing grids until my fingers cramped. Nothing worked.</p>

<p>Then one fine afternoon on the bus back from school, it struck me. <em>This wasn’t about pathfinding at all.</em></p>

<p>It was <strong>combinations.</strong> To reach the bottom-right, you need exactly 20 right moves and 20 down moves. The question can be rephrased: how many ways can you arrange those moves, or how many ways can you choose 20 moves out of 40? And that gives us the answer, <code class="language-plaintext highlighter-rouge">C(40,20)</code>.</p>

<p>I had a mini eureka moment that kept me giddy till I reached home. I booted up my PC and coded the solution in ten minutes. I got the answer: <code class="language-plaintext highlighter-rouge">137846528820</code>. I hit submit and was greeted with the success screen on Project Euler, and the pure gratification of weeks of effort culminating on this one glorious moment.</p>

<p>But here’s the thing — the actual <em>number</em> didn’t matter. What mattered was that moment when everything clicked. When weeks of confusion suddenly made perfect sense. That <a href="https://en.wikipedia.org/wiki/Flow_(psychology)">flow state</a> of complete immersion and understanding.</p>

<p>I wouldn’t have felt like this if I had not spend all that time thinking about the question, or if I had cheated and looked up the answer online.</p>

<p>But I didn’t, and the rush I got was <em>pure magic.</em> I was in love.</p>

<h2 id="ai-addiction">AI Addiction</h2>

<p>A few months ago I was dealing with some gnarly state management bug. Of course, debugging React state is the bottom of the totem pole for enjoyability, just slightly better trying to browse the internet on a slow connection with packet loss, so I (lazily) pasted the entire problem into Claude to let it do it.</p>

<p>Then (and this is the part where it starts to go wrong) <em>I alt-tab to Twitter while it thinks.</em></p>

<figure>
  <img src="/blog/assets/xkcd-ai-generating.webp" alt="XKCD `AI Generating`" style="max-width: 30em; width: 100%;" />
  <figcaption>
    There's always a relevant XKCD... but this is a revision of 
    <a href="https://xkcd.com/303/" target="_blank">Compiling</a>, 
    made by a 
    <a href="https://www.reddit.com/r/xkcd/comments/1ks4r58/ai_programming/" target="_blank">clever redditor</a>
  </figcaption>
</figure>

<p>But, my mind just went <em>“oh, cool”.</em> I felt… nothing.</p>

<p>Well, that’s not true.</p>

<p>I felt something – a hollow satisfaction that my code now works. It felt like the same feeling when doom scrolling through TikTok or when completing another level on a mobile game. That cheap hit of “something happened.”</p>

<p>If you’ve been coding with AI, this might seem relatable:</p>

<p>I feed in a prompt, with the best of my <em>vibes</em>. The AI says it’s “thinking” (even though everyone knows that an AI doesn’t actually <em>think</em>, they’re blatantly lying to our faces. But I smile at the illusion, maybe even slightly believe it.)</p>

<p>I know that it’s gonna take a minute or so before I get the answer. What am I supposed to do in the meanwhile, <em>think</em>? The AI is already going to do that for me, much faster, and will give me the correct result (probably.)</p>

<p>I check back. It’s done. There’s a brief hit of dopamine when I see that it’s completed. Cool.</p>

<p>I check the code (<em>I lied, I just read through what it tells me it’s written and not the actual code</em>). It’s good enough, but of course the AI has made some wrong assumptions so there’s some mistakes.</p>

<p>I give the AI another prompt to fix this, and we repeat our little dance.</p>

<p><em>Put in a prompt, get code.</em></p>

<p><em>Pull the lever, get a reward.</em></p>

<p>No struggle, no insight, <a href="/blog/dangers-vibe-coding" target="_blank">no growth</a>.</p>

<h2 id="the-brain-on-ai">The Brain on AI</h2>

<p>Here’s what’s happening inside your head when you <a href="/blog/vibe-coding-fantasy" target="_blank">“vibe code”</a> and rely completely on AI: you get <a href="https://en.wikipedia.org/wiki/Dopamine">dopamine</a> from the <strong>wrong source.</strong></p>

<p>Before AI, programming gave me two <em>dopamine hits</em>: figuring things out AND getting them to work. The breakthrough moment when you understand why your algorithm failed. The satisfaction of architecting something elegant. The joy of working code after hours of debugging.</p>

<p>Now, the AI does all the figuring out. You’re left with just a shallow pleasure.</p>

<p>If you think about it, that 30-second wait for AI responses can be seen as a <a href="https://en.wikipedia.org/wiki/Reinforcement_schedule">variable ratio schedule</a> — Random rewards delivered at unpredictable intervals — the same psychological pattern that makes slot machines, social media, and mobile games addictive.</p>

<p>If this is happening to me — imagine what it’s doing to <a href="/blog/ai-and-programmers" target="_blank">the developers</a> who’ve never known anything else.</p>

<p>We’re creating a generation of developers who can ship code but <em><a href="/blog/ai-illiterate-programmers" target="_blank">can’t reason about systems</a></em>.</p>

<p>We are creating a generation of <em>human merge buttons</em> who approve AI commits without understanding what they’re deploying.</p>

<p>They’ll never experience that <em>Project Euler</em> moment. That weeks-long struggle that teaches a new programmer how to think, not just <a href="/blog/ai-and-learning" target="_blank">how to copy-paste</a>. They’re skipping the very challenges that would make them great.</p>

<h2 id="the-illusion">The Illusion</h2>

<p>The worst part is that it feels amazing and does actually work (usually.) AI makes me feel productive and features ship faster. The GitHub graph blazes green.</p>

<figure>
  <img src="/blog/assets/giga-usage.png" alt="My weekly AI usage" />
  <figcaption>
    My AI usage last week. Is this too much? (Screenshot from <a href="https://gigamind.dev/?utm_source=blog&amp;utm_content=vibe-coding-gambling-stats-image" target="_blank">my app, my own brainchild</a>.)
  </figcaption>
</figure>

<p>But velocity isn’t competency. When you outsource thinking, you don’t just lose practice — you lose confidence. That voice that used to whisper “I can figure this out” gets quieter every time you reach for AI instead.</p>

<h2 id="how-to-use-ai-without-losing-your-soul">How to Use AI (Without Losing Your Soul)</h2>

<p>I’m not saying abandon AI — these tools have quickly become too powerful to ignore. But we need to be intentional about how we use them.</p>

<ol>
  <li>
    <p>Force yourself to understand the generated code before accepting it. If you can’t explain what it does and why, don’t merge it. This seems obvious, but you’ll be shocked how often new developers skip this step.</p>
  </li>
  <li>
    <p>Practice problems without AI regularly. I’ve started doing Project Euler again. This is <a href="https://en.wikipedia.org/wiki/Deliberate_practice">deliberate practice</a> for programming. My problem-solving muscles were rustier than I expected, but they’re coming back.</p>
  </li>
  <li>
    <p>Use AI waiting time for something productive. Instead of scrolling Twitter or Reddit, you can think about the architecture for your next task while the AI handles implementation details. (Use social media blocking apps if you need to. I use Opal on my phone for this.)</p>
  </li>
  <li>
    <p>Most importantly — remember why you we write code. It’s to create something from nothing, to solve problems that seemed impossible, and build things that matter. Don’t forget the <em>why.</em></p>
  </li>
</ol>

<h2 id="the-future">The Future</h2>

<!-- newsletter_widget -->

<p>Even when AI will have permeated all parts of the software development lifecycle, the best developers will continue to be the ones can think deeply about complex problems.</p>

<p>I’m bullish on AI for coding. It’s revolutionary technology that’s making me more productive than ever. <em>But productivity without understanding is just elaborate copy-pasting.</em></p>

<p>AI should amplify your intelligence, not replace it. Use it to tackle bigger challenges, not to avoid thinking altogether.</p>

<p>Because the day we stop struggling with hard problems is the day we stop being programmers, and become something else entirely.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="startups" />
        
      

      

      
      
        <summary type="html"><![CDATA[Confession: I’ve been using Claude Code to write all my code for me. And I think it’s making me worse at the thing I’ve loved doing for twelve years. I can clearly see how AI coding is rewiring our brains – it makes developers crave instant gratification instead of deep understanding, and reduces us to gamblers who pull levers for the next hit of working code. If this is happening to me, someone who learned to code in the pre-AI era, what’s it doing to junior developers who’ve never known anything else?]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/vibe-coding-gambling.png" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/vibe-coding-gambling.png" />
      

      
        <social:metrics>
          
            <social:views>500,000+</social:views>
          
          
            <social:reddit>4,500+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Lessons from my journey to become the founder I wish I’d met earlier</title>
      <link href="https://nmn.gl/blog/greatness" rel="alternate" type="text/html" title="Lessons from my journey to become the founder I wish I’d met earlier" />
      <published>2025-07-16T00:00:00+00:00</published>
      <updated>2025-07-16T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/greatness</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/greatness"><![CDATA[<p>I celebrate another revolution around the great big ball of fire today. This was a big year for me: I founded my first product startup, all solo.</p>

<p>I’m doing something really hard but I realize I’ve never written down my guiding principles about <em>why</em> I do this. This article is mostly a reminder for me: <em>on what it takes to achieve greatness.</em></p>

<p>Here’s what I’ve learned so far:</p>

<figure class="figure-flippable">
  <div class="figure-flippable__container">
    <div class="figure-flippable__face figure-flippable__face--front">
      <img class="figure-flippable__image" src="/blog/assets/namanyay-anime-style-dolores-park.png" alt="Namanyay (Stylized) in Dolores Park" />
      <button class="figure-flippable__toggle-button">Show Original</button>
    </div>
    <div class="figure-flippable__face figure-flippable__face--back">
      <img class="figure-flippable__image" src="/blog/assets/namanyay-dolores-park.jpg" alt="Namanyay in Dolores Park" />
      <button class="figure-flippable__toggle-button">Show Stylized</button>
    </div>
  </div>
  <figcaption>My favorite view of San Francisco: the legendary Dolores Park MUNI Station</figcaption>
</figure>

<!--more-->

<h2 id="learning-to-fail-with-joy">Learning to fail with joy</h2>

<p>I still remember my first program - in VBScript 6 written on an XP machine in Notepad - a textbox came up, where when you typed your name, the computer wrote back “hi [name]!”</p>

<p>At that time, it was the greatest thing ever. I made that computer do what I want. I summoned a magical box with my name inscribed in it. I felt like a sorcerer unlocking the arcane arts.</p>

<p>I know now that it’s not one of the most sophisticated programs ever. But if I hadn’t written that I wouldn’t have been able to make a living writing code.</p>

<p>In the beginning, whatever we do generally isn’t good. But find the joy and keep doing it, and it will add up.</p>

<p>Recently, I found myself <a href="https://x.com/julianweisser">running across the Golden Gate Bridge with one of the smartest investors in Silicon Valley</a>, about to throw up.</p>

<p>That’s when he told me that <em>even though each step feels hard, just put on a smile and keep taking one more.</em></p>

<p>An <a href="https://x.com/ryanmurphy804">ex Navy SEAL who also held the world record for 4100+ pullups in 13 hours</a> reinforces this whenever I complain during our workouts together: <em>it always hurts and feels bad at the start. Eventually it won’t.</em></p>

<p>What I’ve discovered: the difference between people who build great things and those who don’t isn’t talent. It’s that they <strong>learned to smile while sucking at something.</strong> When you fail and make bad stuff with joy, you get better faster.</p>

<h2 id="my-genius-friends-are-all-a-little-crazy">My genius friends are all a little crazy</h2>

<p>I live with <a href="https://x.com/DhravyaShah">a 19-year-old who’s been working on memory for AI for the last 2 years.</a> That’s over 10% of his life dedicated to AI memory. Ask him anything on this topic, he’ll tell you things that you can never find on Google or ChatGPT. How does he know all this?</p>

<p>Another <a href="https://x.com/philipofficial9">genius founder and a new friend of mine learnt how to code on a Nokia.</a> Can you imagine — a T9 keyboard to type out HTML on? Have you seen the number of <code class="language-plaintext highlighter-rouge">&lt;</code> in HTML, do you even know how to write this on a T9? He’s now running a successful product with over 16k+ GitHub stars. How was he able to stick through all the pain?</p>

<p>Watching these obsessed people made me realize something: the only way you can do something every single day, taking up the majority of your time, is if you’re <strong>genuinely in love with it.</strong> Not simply interested, nor passionate. In love. To the point where typing HTML on a T9 keyboard feels like play.</p>

<p>That kind of love is what lets you rack up the hours. Which lets you suck quicker. Which lets you become great.</p>

<h2 id="the-mistake-of-not-having-feedback-loops">The mistake of not having feedback loops</h2>

<p>For a long time, I thought building a startup meant writing perfect code. I’d disappear for months, crafting beautiful systems that nobody ever used. I was getting great at coding, but terrible at startups.</p>

<p>Then <a href="https://www.youtube.com/watch?v=5lE2iKsscAU&amp;list=PLOHhQHMQ2uzviz4L27YdvnqQCnX4GklOj">I joined buildspace and saw this dude two years ago who inspired and taught thousands.</a></p>

<p>He showed me the fundamental early stage startup feedback loop: ship, get feedback, improve, repeat.</p>

<p>That’s when I realized: I wasn’t doing the whole thing. If you want to be a great writer, you can’t just write—you need readers. If you want to found a startup, you can’t just code—you need customers.</p>

<p>The pattern I started noticing everywhere: greatness comes from doing the complete cycle, not perfecting one part.</p>

<p>Get that set up as soon as you can, however you can, and remember that it’s only you who has to make sure that you’re doing it correctly.</p>

<h2 id="finally-community">Finally, community</h2>

<p>Years ago, I was probably the most antisocial person you’d meet. I tried to do everything on my own. It was hard and the low times felt really low.</p>

<p>Then something shifted. Turns out, finding people who’re having fun alongside you makes everything easier.</p>

<p>I’m now fortunate to be in the <a href="http://www.sfp.club/">Solo Founders Program.</a></p>

<p>People around me are in the same journeys, know exactly what I’m talking about, share my excitement, and give constructive feedback from the same values.</p>

<p>Most importantly, we all share our failures openly. Sometimes we find help, but even knowing that others are facing hardships makes our own a bit easier.</p>

<p>Suddenly, the journey doesn’t feel lonely. It feels like an adventure you’re on together.</p>

<h2 id="why-this-matters">Why this matters</h2>

<p>I’ve thought a lot about what drives the great people I know. And I think the answer is simple: it feels great to build something and be good at it.</p>

<p>I still remember the rush from that first “hello world” moment. And at every step and growth, that feeling never went away. It just got bigger.</p>

<p>Every problem you solve, every user you help, every impossible thing you make possible builds on that same foundation.</p>

<p>Here’s what nobody tells you about building great things: it’s mostly about becoming the kind of person who can handle the journey.</p>

<p>Someone who finds joy in the struggle. Someone who falls in love with problems worth solving. Someone who finds their people.</p>

<p>The world doesn’t need another perfect product. It needs what happens when you combine your weird obsessions with willingness to fail.</p>

<p>So go be weird. Go fail. Go build something only you would build.</p>

<p>We’re all waiting to see what you come up with.</p>

<!-- newsletter_widget -->]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      
        <category term="featured" scheme="http://namanyayg.com/post-types" />
      

      
        
          <category term="life" />
        
          <category term="startups" />
        
      

      

      
      
        <summary type="html"><![CDATA[I celebrate another revolution around the great big ball of fire today. This was a big year for me: I founded my first product startup, all solo. I’m doing something really hard but I realize I’ve never written down my guiding principles about why I do this. This article is mostly a reminder for me: on what it takes to achieve greatness. Here’s what I’ve learned so far: Show Original Show Stylized My favorite view of San Francisco: the legendary Dolores Park MUNI Station]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/namanyay-anime-style-dolores-park.png?v=2" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/namanyay-anime-style-dolores-park.png?v=2" />
      

      
    </entry>
  
    <entry>
      

      <title type="html">There’s a Better Way to Code with AI</title>
      <link href="https://nmn.gl/blog/ai-amnesia" rel="alternate" type="text/html" title="There’s a Better Way to Code with AI" />
      <published>2025-06-19T00:00:00+00:00</published>
      <updated>2025-06-19T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-amnesia</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-amnesia"><![CDATA[<p>Every developer I know has the same frustrating ritual. Open Claude Code or Cursor and ask it to do a task. The AI gives you generic code, sometimes useful (but usually not). You correct it. It apologizes. You explain again, with additional context.</p>

<p>Rinse, repeat, until you want to throw your laptop out the window.</p>

<p><a href="https://x.com/zeeg/status/1935402230062190672">David Cramer from Sentry</a> recently shared his AI workflow where he maintains manual rules files to give LLMs context. Solid approach, but it feels like too much copy-pasting. It’s 2025, and machines can do a better job of remembering things.</p>

<p>It’s funny how we’ve built the most powerful reasoning systems in human history, then lobotomized them by making them forget everything after each conversation. My question: is there a better way?</p>

<!--more-->

<h2 id="the-story">The Story</h2>

<p>Until recently, my daily routine looked like this:</p>

<ol>
  <li>Open Claude Code/Cursor</li>
  <li>Explain the task</li>
  <li>It gives a wrong result on the first try</li>
  <li>Retry, this time with more context</li>
  <li>It gives a wrong result on the second try</li>
  <li>Spend 30 minutes fixing it</li>
  <li>Close the conversation in frustration</li>
  <li>Repeat the next day</li>
</ol>

<p>I was essentially training the same AI from scratch every single day. <em>The definition of insanity, right?</em></p>

<p>That’s when it hit me: The AI isn’t the problem. The conversation model is.</p>

<h2 id="building-a-second-brain">Building a Second Brain</h2>

<p>I started thinking about how human teams actually work. Senior developers don’t explain the entire codebase to new hires every morning. They build institutional knowledge. <strong>They have coding standards, architectural decisions, and learned preferences that persist.</strong></p>

<p>So I built Giga — an AI memory system that learns from your conversations and builds a persistent knowledge base. <strong>Not another rules file you maintain manually, but a system that watches how you code, what you prefer, and what decisions you make.</strong></p>

<p>I made a “reflect” command which allows the AI to look back on it’s own conversation to identify patterns. While dogfooding it, I noticed something magical happening. Giga confidently identifies actual patterns, saves them as “neurons” in the second brain, and allows me to evoke them when I need them.</p>

<p>My AI is finally able to remember my preferences, and it uses them to make better decisions.</p>

<h2 id="how-to-fix-your-ai-workflow">How to Fix Your AI Workflow</h2>

<p>If you’re tired of the hamster wheel, here’s what actually works:</p>

<ul>
  <li>Question the reset. Why are you starting fresh every time? Your conversation history is data. Your corrections are training examples. Your preferences are valuable.</li>
  <li>Build incremental context. Instead of massive context dumps, add one new piece of information per conversation. Let the AI learn gradually, like a human would.</li>
  <li>Think in systems, not conversations. Individual AI chats are tactics. Building persistent AI knowledge is strategy.</li>
</ul>

<h2 id="the-path-forward">The Path Forward</h2>

<p>The future isn’t necessarily more powerful models. It’s AI that remembers, learns, and gets better over time — like a real team member.</p>

<p>We’re at an inflection point. We can keep playing the role of human context providers, or we can build systems that actually learn and improve.</p>

<p>I’m betting on the latter.</p>

<p>The question isn’t whether AI will get smarter. It’s whether we’ll get smarter about using it.</p>

<p><em>Building Giga taught me that the best way forward with code generation AI isn’t about better models — it’s about better context and memory. Giga is in private beta and only available for select teams, but if you’re interested in seeing how persistent AI knowledge works in practice, <a href="mailto:hello@gigamind.dev">shoot me an email</a> and I’d love to show you a demo.</em></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      
        <category term="featured" scheme="http://namanyayg.com/post-types" />
      

      
        
          <category term="ai" />
        
          <category term="startups" />
        
      

      

      
      
        <summary type="html"><![CDATA[Every developer I know has the same frustrating ritual. Open Claude Code or Cursor and ask it to do a task. The AI gives you generic code, sometimes useful (but usually not). You correct it. It apologizes. You explain again, with additional context. Rinse, repeat, until you want to throw your laptop out the window. David Cramer from Sentry recently shared his AI workflow where he maintains manual rules files to give LLMs context. Solid approach, but it feels like too much copy-pasting. It’s 2025, and machines can do a better job of remembering things. It’s funny how we’ve built the most powerful reasoning systems in human history, then lobotomized them by making them forget everything after each conversation. My question: is there a better way?]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Don’t Repeat Mistakes, Teach AI to Remember Every Code Review Instead</title>
      <link href="https://nmn.gl/blog/ai-review-rules" rel="alternate" type="text/html" title="Don’t Repeat Mistakes, Teach AI to Remember Every Code Review Instead" />
      <published>2025-06-14T00:00:00+00:00</published>
      <updated>2025-06-14T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-review-rules</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-review-rules"><![CDATA[<p><em>This is part of my “AI in SF” series, where I share real AI engineering workflows from San Francisco startups. I recently interviewed engineers from Parabola (they’re hiring btw, more on that at the end). Here’s a technique to teach AI to learn from your mistakes.</em></p>

<p>You know that feeling when you leave the same code review comment for the third time this month? “Hey, we use relative imports here, not absolute ones.” Or “Remember to handle both null and empty string cases.” Or “This should use our ORM helper, not raw SQL.”</p>

<p>Your team agrees it’s important. People follow it for some time. But three weeks later, nothing’s changed, and you’re still leaving the same comments.</p>

<p>I recently interviewed <a href="https://www.linkedin.com/in/cjcjameson/">C.J.</a> and <a href="https://www.linkedin.com/in/zach-m-1000000000/">Zach</a> from Parabola (a Series B data automation company) about how they use AI in their engineering workflow. They shared a simple approach that’s replaced most of their linter rules: <strong>teaching Cursor to remember code review feedback permanently</strong>.</p>

<!--more-->

<h2 id="the-problem-with-traditional-linting">The Problem with Traditional Linting</h2>

<p>Traditional linting can’t catch the nuanced “we don’t do it that way here” patterns that come up in code reviews.</p>

<p>On the other hand, LLMs are perfect for this use case. Cursor rules are similar to “system prompts” for cursor, or extra instructions you provide to direct how it behaves. Here’s how Parabola uses them: every time they spot a pattern in code review that should be avoided, they write a Cursor rule. After a PR review, they ask “how could we teach the AI to avoid this class of error in the future?”</p>

<figure>
  <img src="/blog/assets/ai-review-rules.jpg" alt="AI Review Rules" />
</figure>

<p>Some examples that might come up in code reviews but ESLint can’t catch:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Comment Rules: when doing a large change or refactoring, try to retain comments, possibly revising them, or matching the same level of commentary to describe the new systems you're building
It's especially common that extra comments means that the previous/existing code has something weird going on. So either ask the human what to do and raise it before making a change, or think extra about what to do.

Package Usage: If you're adding a new package, think to yourself, "can I reuse an existing package instead"
(Especially if it's for testing, or internal-only purposes)
</code></pre></div></div>

<h2 id="implementation-guide">Implementation Guide</h2>

<ul>
  <li>Start small — Pick the most common code review comment your team makes</li>
  <li>Write conversationally — Explain the rule like you’re talking to a junior engineer</li>
  <li>Include examples — Show what to do and what not to do</li>
  <li>Be specific about scope — When should this rule apply?</li>
  <li>Commit to your repo — Make it part of your codebase so everyone benefits</li>
</ul>

<h2 id="reality-check">Reality Check</h2>

<p>There are some caveats, namely:</p>

<ul>
  <li>It’s a 90% solution, not 100%. Their rules work most of the time, but stuff still slips through. You’re not eliminating code review.</li>
  <li>Different IDEs, different “rules” file conventions. Windsurf has .windsurfrules. Copilot has copilot-instructions.md. You need to understand and note the differences.</li>
  <li>Rules can get messy fast. Without some discipline, you’ll end up with conflicting rules or overly broad ones that trigger when they shouldn’t. You have to review your rules regularly, what made sense six months ago might not make sense now.</li>
</ul>

<!-- newsletter_widget -->

<h2 id="when-to-use">When to Use</h2>

<p>Use Cursor rules for:</p>

<ul>
  <li>Team conventions that are hard to express in code</li>
  <li>Patterns that emerged from production incidents</li>
  <li>Style preferences that don’t warrant build failures</li>
  <li>Complex logic patterns that would be tedious to lint</li>
</ul>

<p>Keep traditional linters for:</p>

<ul>
  <li>Security-critical patterns</li>
  <li>Industry-standard formatting</li>
  <li>Syntax errors that should break builds</li>
  <li>Rules that need to run in CI/CD</li>
</ul>

<h2 id="the-compound-effect">The Compound Effect</h2>

<p>After ~10 PRs, you’ll have a collection of rules that represent your team’s collective wisdom. New AI-generated code automatically follows patterns that used to require manual review cycles.</p>

<p>The beauty is that knowledge doesn’t walk out the door when people leave. It gets encoded into how your AI writes code.</p>

<h2 id="getting-started-today">Getting Started Today</h2>

<p>Create a .cursorrules file in your project root. Add one rule based on the most recent code review comment you’ve made. Test it by asking Cursor to write code that would normally trigger that comment.</p>

<p>You’ll be surprised how quickly this becomes valuable.</p>

<p><em>Parabola is hiring senior engineers who think deeply about user experience and building tools that work for both technical and non-technical users. They’re looking for people who care about ergonomics for different types of users — from someone automating one simple process to someone building complex, reusable data workflows. If building customer-centric data tools sounds interesting, they’d love to hear from you. Check out their <a href="https://parabola.io/careers">Parabola careers</a>.</em></p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="/blog/cursor-guide?utm_source=blog&amp;utm_medium=ai-review-rules&amp;utm_campaign=ai-review-rules">My Cursor AI Workflow That Actually Works in Production</a></li>
  <li><a href="/blog/cursor-ai-gold-files?utm_source=blog&amp;utm_medium=ai-review-rules&amp;utm_campaign=ai-review-rules">Cursor AI Gold Standard Files Workflow</a></li>
  <li><a href="/blog/ai-dev-tips?utm_source=blog&amp;utm_medium=ai-review-rules&amp;utm_campaign=ai-review-rules">Context, Structure, Organization: Framework for AI-powered Development</a></li>
  <li><a href="/blog/ai-understand-senior-developer?utm_source=blog&amp;utm_medium=ai-review-rules&amp;utm_campaign=ai-review-rules">The day I taught AI to think like a Senior Developer</a></li>
  <li><a href="/blog/ai-prompt-engineering?utm_source=blog&amp;utm_medium=ai-review-rules&amp;utm_campaign=ai-review-rules">AI Prompt Playbook (Prompts that Work in Production)</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="startups" />
        
          <category term="ai-in-sf" />
        
      

      

      
      
        <summary type="html"><![CDATA[This is part of my “AI in SF” series, where I share real AI engineering workflows from San Francisco startups. I recently interviewed engineers from Parabola (they’re hiring btw, more on that at the end). Here’s a technique to teach AI to learn from your mistakes. You know that feeling when you leave the same code review comment for the third time this month? “Hey, we use relative imports here, not absolute ones.” Or “Remember to handle both null and empty string cases.” Or “This should use our ORM helper, not raw SQL.” Your team agrees it’s important. People follow it for some time. But three weeks later, nothing’s changed, and you’re still leaving the same comments. I recently interviewed C.J. and Zach from Parabola (a Series B data automation company) about how they use AI in their engineering workflow. They shared a simple approach that’s replaced most of their linter rules: teaching Cursor to remember code review feedback permanently.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/ai-review-rules.jpg?v=2" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/ai-review-rules.jpg?v=2" />
      

      
    </entry>
  
    <entry>
      

      <title type="html">Cursor AI Best Practices: Using the Gold Standard Files Workflow for Precise Results</title>
      <link href="https://nmn.gl/blog/cursor-ai-gold-files" rel="alternate" type="text/html" title="Cursor AI Best Practices: Using the Gold Standard Files Workflow for Precise Results" />
      <published>2025-06-11T00:00:00+00:00</published>
      <updated>2025-06-11T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/cursor-ai-gold-files</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/cursor-ai-gold-files"><![CDATA[<p><em>This is a part of my “AI in SF” series, where I share real AI engineering workflows of SF startups. I recently interviewed an engineer from <a href="https://www.pallet.com/">Pallet</a> (they’re hiring - more on that at the end). Here’s an insight that will make your AI-generated code better.</em></p>

<p>Most developers use Cursor like expensive autocomplete. They let it generate whatever code it wants, fight with inconsistent outputs, and spend more time debugging AI mistakes than they save.</p>

<p>There’s a better way. During my interview with <a href="https://www.linkedin.com/in/vidhurkumar/">Vidhur from Pallet</a>, I learned about a simple technique that made their AI-generated code dramatically better: the “gold standard” file approach.</p>

<!--more-->

<h2 id="what-is-a-gold-standard-file">What Is a Gold Standard File?</h2>

<p>You pick one ideal file for each common pattern in your codebase and reference it in your <code class="language-plaintext highlighter-rouge">.cursorrules</code> file. Think of it as showing Cursor your team’s style guide through working examples rather than abstract rules.</p>

<p>For a typical backend API following a controller-service pattern, you’d maintain three gold standard files:</p>
<ul>
  <li>Controller file: Perfect API endpoint following your controller-service-repository pattern</li>
  <li>Service file: Clean business logic with proper separation of concerns</li>
  <li>Test file: Your exact testing philosophy in action</li>
</ul>

<p>The key insight: <strong>don’t let AI guess what good code looks like. Show it explicitly.</strong></p>

<figure>
  <img src="/blog/assets/ai-gold-standard-files.jpg?v=2" alt="AI Gold Standard Files" />
</figure>

<h2 id="setting-up-your-rules-file">Setting Up Your Rules File</h2>

<p>Your <code class="language-plaintext highlighter-rouge">.cursorrules</code> file should point directly to these examples:</p>

<div class="language-md highlighter-rouge"><div class="highlight"><pre class="highlight"><code>You are an expert software engineer. 

Reference these gold standard files for patterns:
<span class="p">
-</span> Controllers: /src/controllers/orders.controller.ts
<span class="p">-</span> Services: /src/services/orders.service.ts
<span class="p">-</span> Tests: /src/tests/orders.test.ts
Follow these patterns exactly. Don't change existing implementations unless asked.
Use our existing utilities instead of writing new ones.
</code></pre></div></div>

<h2 id="how-to-choose-your-gold-standard-files">How to Choose Your Gold Standard Files</h2>

<ul>
  <li>Start small: Begin with one pattern that you use frequently, like API endpoints. Add more gold standards over time.</li>
  <li>Keep it specific: “Write good code” is useless. “Follow the exact pattern in /src/controllers/orders.controller.ts” works.</li>
  <li>Share with your team: Treat .cursorrules like a .gitignore - everyone should use the same standards.</li>
  <li>Don’t over-provide context: Too many instructions confuse AI. Focus on your most important patterns.</li>
</ul>

<p>I made something that does all of this for you automatically. If you’re interested, <a href="https://gigamind.dev/">give it a try</a>.</p>

<h2 id="what-actually-changes">What Actually Changes</h2>

<p>The transformation is immediate. Code reviews become faster because AI-generated code follows your established patterns instead of random internet examples. New code consistently looks like it was written by your senior engineers, not pulled from different tutorials. Junior developers start absorbing good patterns from AI-generated examples, accelerating their learning curve.</p>

<p>And the real win for startups — development velocity increases without sacrificing code quality. When you’re racing to product-market fit, you can’t afford to choose between shipping fast and maintaining clean code. This approach gives you both.</p>

<!-- newsletter_widget -->

<h2 id="beyond-just-code-generation">Beyond Just Code Generation</h2>

<p>This approach works for any AI coding task. Testing, documentation, even complex algorithms - if you have a good example, AI can follow that pattern reliably.</p>

<p>The bigger principle: AI tools are only as good as the examples you give them. Feed them your best code, get better results.</p>

<h2 id="getting-started-today">Getting Started Today</h2>

<ol>
  <li>Find your best-written controller, service, and test files</li>
  <li>Create a <code class="language-plaintext highlighter-rouge">.cursorrules</code> file that references them specifically</li>
  <li>Add any anti-patterns you want to avoid</li>
  <li>Share with your team and iterate</li>
</ol>

<p>This isn’t revolutionary AI technology. It’s just good engineering: showing tools what you want instead of hoping they guess correctly.</p>

<p><em>Pallet is hiring engineers who want to build AI agents grounded in real-world applications. They’re looking for people who understand that agents aren’t fully autonomous yet and take a pragmatic approach to AI reliability. If you want to work on logistics AI with a team that accepts agents are imperfect and builds proper guardrails around them, check out the <a href="https://www.pallet.com/company#open-positions">Pallet careers page</a>.</em></p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="/blog/cursor-guide?utm_source=blog&amp;utm_medium=cursor-ai-gold-files&amp;utm_campaign=cursor-ai-gold-files">My Cursor AI Workflow That Actually Works in Production</a></li>
  <li><a href="/blog/ai-dev-tips?utm_source=blog&amp;utm_medium=cursor-ai-gold-files&amp;utm_campaign=cursor-ai-gold-files">Context, Structure, Organization: Framework for AI-powered Development</a></li>
  <li><a href="/blog/ai-understand-senior-developer?utm_source=blog&amp;utm_medium=cursor-ai-gold-files&amp;utm_campaign=cursor-ai-gold-files">The day I taught AI to think like a Senior Developer</a></li>
  <li><a href="/blog/ai-prompt-engineering?utm_source=blog&amp;utm_medium=cursor-ai-gold-files&amp;utm_campaign=cursor-ai-gold-files">AI Prompt Playbook (Prompts that Work in Production)</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="startups" />
        
          <category term="ai-in-sf" />
        
      

      

      
      
        <summary type="html"><![CDATA[This is a part of my “AI in SF” series, where I share real AI engineering workflows of SF startups. I recently interviewed an engineer from Pallet (they’re hiring - more on that at the end). Here’s an insight that will make your AI-generated code better. Most developers use Cursor like expensive autocomplete. They let it generate whatever code it wants, fight with inconsistent outputs, and spend more time debugging AI mistakes than they save. There’s a better way. During my interview with Vidhur from Pallet, I learned about a simple technique that made their AI-generated code dramatically better: the “gold standard” file approach.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/ai-gold-standard-files.jpg?v=2" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/ai-gold-standard-files.jpg?v=2" />
      

      
        <social:metrics>
          
            <social:views>70,000+</social:views>
          
          
            <social:reddit>250+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Cursor AI Workflow for Complex Projects (That Actually Works)</title>
      <link href="https://nmn.gl/blog/cursor-complex-projects" rel="alternate" type="text/html" title="Cursor AI Workflow for Complex Projects (That Actually Works)" />
      <published>2025-06-07T00:00:00+00:00</published>
      <updated>2025-06-07T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/cursor-complex-projects</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/cursor-complex-projects"><![CDATA[<p>Social media is full of people showing off their perfect little demo apps claiming AI is revolutionary, meanwhile, AI keeps suggesting fixes for files that don’t exist or rewrites working code into broken messes.</p>

<p>Does that sound familiar?</p>

<p>Here’s the thing — the “vibe coders” are not wrong about AI being powerful. <strong>They’re just not dealing with what you’re dealing with.</strong></p>

<p>You’re working on a real codebase, with real dependencies, real business logic, and real users. Real codebases are messy.</p>

<p>I spent ten years of my life running a development agency, and Cursor has legitimately saved me weeks of work, but only after I stopped expecting it to just “figure things out.” Now, I’m going to share with you the workflow that made Cursor work for me on complex projects.</p>

<!--more-->

<h2 id="mental-model">Mental Model</h2>

<p>Think of your AI as a <strong>brilliant intern</strong> who codes incredibly fast but has zero context about your busines or your architecture decisions.</p>

<p>You wouldn’t hand an intern your entire codebase and say “build the payment system.” You’d give them background, show them examples, and break down exactly what needs to happen.</p>

<p>Same principle applies here.</p>

<p><strong>Document your patterns like someone’s life depends on it.</strong> I keep a <code class="language-plaintext highlighter-rouge">backend-patterns.md</code> file that explains how I structure resources — where routes go, how services work, what the data layer looks like. Every time I ask Cursor to build something backend-related, I reference this file.</p>

<p>Actually, maintaining these files manually got tedious, so I built <a href="https://gigamind.dev/?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">somthing to handle this automatically</a>. My tool creates files that capture your project’s architectural decisions, and keeps them updated as your codebase evolves. Personal productivity hack that’s saved me hours of documentation work.</p>

<p>Result = no more random architectural decisions that break everything downstream.</p>

<p><strong>Plan everything before writing code.</strong> I don’t let the AI write a single line until we both understand exactly what we’re building. I usually co-write the plan with Claude or ChatGPT — what functions we need, which files get touched, potential edge cases.</p>

<p>This might sound tedious, but actually saves a lot of debugging time.</p>

<p><strong>Show, don’t tell.</strong> Instead of explaining how something should work, point to existing code: “Build this new API endpoint, follow the same pattern as the user endpoint.” Pattern recognition is where these models actually shine.</p>

<p><strong>Control scope ruthlessly.</strong> In smaller projects, you can ask it to build whole features. But as complexity grows, get more specific. One function at a time. One file at a time. The bigger the ask, the more likely it breaks something unrelated.</p>

<h2 id="maintenance">Maintenance</h2>

<p>Your codebase needs to stay organized or AI starts hallucinating. Hit that reindex button in Cursor settings regularly — treat it like clearing your browser cache.</p>

<p>When errors happen, <strong>fix them one by one.</strong> Don’t copy-paste a wall of red terminal output. AI gets overwhelmed just like humans do.</p>

<p>Pro tip: Add “don’t change code randomly, ask if you’re not sure” to your prompts. This simple line has saved me countless debugging sessions.</p>

<h2 id="use-cursor-rules-files">Use Cursor Rules Files</h2>

<p>Set up a <code class="language-plaintext highlighter-rouge">.cursorrules</code> file in your project root. Every time you find yourself repeating the same context in prompts — like your backend patterns or coding standards — add it to the rules file. It gets automatically included in every AI request.</p>

<p>You can even create file-specific rules. Your database migration files can have different guidelines than your frontend components.</p>

<h2 id="start-with-a-codebase-outline">Start With a Codebase Outline</h2>

<p>Before doing any major work, have the AI create an outline of your entire project. Go file by file, documenting every class, public function, and system. Include the AI’s understanding of each piece’s purpose and any gotchas.</p>

<p>This used to be the most time-consuming part of my workflow, but the results were worth it. Got so frustrated with this that I ended up building <a href="https://gigamind.dev/?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">my own tool to automate it</a>. Now, it analyzes my entire project in minutes and generates comprehensive documentation, which creates a baseline that prevents the AI from building duplicate systems or breaking existing functionality.</p>

<h2 id="what-this-gets-you">What This Gets You</h2>

<p>I write maybe 10% of the boilerplate I used to. Database queries with proper error handling happen in minutes instead of hours. Complex API endpoints with validation get handled while I focus on architecture decisions that actually matter.</p>

<p>The best part is that I can move fast on the stuff that requires actual thinking while the AI handles tedious implementation.</p>

<p>Your legacy codebase isn’t a disadvantage here — all that structure and business logic you’ve built up is exactly what makes AI productive. You just need to help it understand what you’ve already created.</p>

<h2 id="the-real-advantage">The Real Advantage</h2>

<p>The teams who are able to work with AI effectively on complex projects are going to have a massive advantage. That happens when they learn to give AI the context it needs to be genuinely useful.</p>

<p>Your competitors are still either avoiding AI completely or getting frustrated when it breaks their code.</p>

<p><strong>You can be the one who figured out how to make it work</strong>, and get an advantage against your competitors who are still struggling with it.</p>

<p>Questions? Write to me at <a href="mailto:hi@nmn.gl">hi@nmn.gl</a> (personal email) and I’ll try to help in whatever way I can.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="/blog/cursor-guide?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">My Cursor AI Workflow That Actually Works in Production</a></li>
  <li><a href="/blog/ai-dev-tips?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">Context, Structure, Organization: Framework for AI-powered Development</a></li>
  <li><a href="/blog/ai-understand-senior-developer?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">The Day I taught AI to think like a Senior Developer</a></li>
  <li><a href="/blog/ai-prompt-engineering?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">AI Prompt Playbook (Prompts that Work in Production)</a></li>
  <li><a href="/blog/vibe-security-checklist?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">Security Checklist &amp; Prompts for Cursor Vibe Coders</a></li>
  <li><a href="/blog/building-with-ai?utm_source=blog&amp;utm_medium=cursor-complex-projects&amp;utm_campaign=cursor-complex-projects">Cursor Workflows to build SaaS products in 2025</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="startups" />
        
      

      

      
      
        <summary type="html"><![CDATA[Social media is full of people showing off their perfect little demo apps claiming AI is revolutionary, meanwhile, AI keeps suggesting fixes for files that don’t exist or rewrites working code into broken messes. Does that sound familiar? Here’s the thing — the “vibe coders” are not wrong about AI being powerful. They’re just not dealing with what you’re dealing with. You’re working on a real codebase, with real dependencies, real business logic, and real users. Real codebases are messy. I spent ten years of my life running a development agency, and Cursor has legitimately saved me weeks of work, but only after I stopped expecting it to just “figure things out.” Now, I’m going to share with you the workflow that made Cursor work for me on complex projects.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>20,000+</social:views>
          
          
            <social:reddit>75+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">AI Can’t Even Fix a Simple Bug — But Sure, Let’s Fire Engineers</title>
      <link href="https://nmn.gl/blog/ai-scam" rel="alternate" type="text/html" title="AI Can’t Even Fix a Simple Bug — But Sure, Let’s Fire Engineers" />
      <published>2025-05-22T00:00:00+00:00</published>
      <updated>2025-05-22T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-scam</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-scam"><![CDATA[<p><em>Reddit discovered the funniest thing in tech this week, and it shows exactly how broken the AI narrative is.</em></p>

<p>The newly released GitHub Copilot agent was given permission to make pull requests on Microsoft’s .NET runtime, and the results couldn’t be funnier.</p>

<p>The AI confidently submitted broken code, while human developers patiently explained why it didn’t work. Over and over again, for days.</p>

<!--more-->

<figure>
  <img src="/blog/assets/dotnet-copilot-pr.jpg" alt="GitHub Copilot PRs on the .NET runtime" />
  <figcaption>
    I feel bad for the engineers in this <a href="https://github.com/dotnet/runtime/pull/115733" target="_blank">PR</a>, but I also can't stop laughing.
  </figcaption>
</figure>

<p>What’s worse is that CEOs are using this same technology to <strong>justify laying off entire engineering teams.</strong></p>

<h2 id="comedy-show">Comedy show</h2>

<p>I spent way too much time scrolling through these PRs. The pattern was always the same:</p>

<ul>
  <li>AI: “I fixed the issue!”</li>
  <li>Human: “Your code isn’t working and you broke other tests.”</li>
  <li>AI: “You’re absolutely right! Fixed it now.”</li>
  <li><em>Still not fixed.</em></li>
</ul>

<p>This went on for days. Multiple rounds of the AI insisting it solved problems, while making them worse.</p>

<p>And somewhere, executives are watching this thinking, <em>“Yeah, this is ready to replace our engineering team.”</em></p>

<h2 id="hidden-truth">Hidden truth</h2>

<p>I don’t want to sound like an AI hater, I use AI for coding daily. It’s genuinely made me more productive.</p>

<p>But here’s what’s actually happening with these layoffs: Companies over-hired during the pandemic boom. Now they need to cut costs. AI gave them the perfect cover story.</p>

<p>Instead of admitting <em>“We planned poorly,”</em> they get to sound visionary: <em>“We’re leveraging AI to optimize workforce efficiency!”</em></p>

<p>Meanwhile, I know a startup founder who just raised Series A. He messaged me about wanting to hire three more engineers.</p>

<p>“Aren’t you using AI to do more with fewer people?” I asked.</p>

<p>He laughed. “AI makes my team unstoppable. Why would I want fewer unstoppable people?”</p>

<h2 id="what-devs-need-to-do-right-now">What devs need to do right now</h2>

<p>If you’re worried about AI layoffs, here’s your playbook:</p>

<!-- newsletter_widget -->

<ul>
<li><strong>Become the AI expert on your team.</strong> Don't fight the tools, master them. Be <a href="/blog/ai-and-learning">the person who knows when AI helps and when it hurts.</a><br /><br />

Management needs someone who understands both the potential and limitations.<br /><br /></li>

<li><strong>Document everything AI can't do.</strong> Keep a running list of complex problems you solve that AI suggestions couldn't handle. Things like system design decisions, business logic that required domain knowledge, or bugs that needed deep debugging.<br /><br />

This is not just paranoia, it is to properly understand AI, for yourself and for your team.<br /><br /></li>

<li><strong>Make it public.</strong> Share your real AI experiences on social media. Post screenshots of AI failing at complex tasks alongside examples where it actually helped. Show the world that human + AI is the future, not AI alone. <br /><br />

The world is watching. Share the truth about AI. If you don't, then who will?<br /><br /></li>

<li><strong>Create the narrative.</strong> Every time you solve something AI couldn't, tweet about it. When AI helps you ship faster, share that too. <br /><br />

The goal isn't to bash AI, it's to show that the <em>most powerful combination</em> is skilled developers using AI as a force multiplier. Make <em>yourself</em> the obvious choice for companies that want to win.</li>
</ul>

<h2 id="the-bottom-line">The bottom line</h2>

<p>If your CEO is talking about AI layoffs, update your resume. Not because AI will replace you, but because you’re working for someone who thinks a technology that <a href="/blog/ai-illiterate-programmers">can’t understand a simple bug</a> is ready to run their engineering org.</p>

<p>AI has become a part of software development, but it isn’t perfect. Humans are needed more than ever.</p>

<p>And if you want proof, just check those <a href="https://www.reddit.com/r/ExperiencedDevs/comments/1krttqo/my_new_hobby_watching_ai_slowly_drive_microsoft/" target="_blank">Microsoft PRs</a>. The AI is still confidently wrong. The humans are still patiently fixing everything.</p>

<p>So trust me, your job is safe.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[Reddit discovered the funniest thing in tech this week, and it shows exactly how broken the AI narrative is. The newly released GitHub Copilot agent was given permission to make pull requests on Microsoft’s .NET runtime, and the results couldn’t be funnier. The AI confidently submitted broken code, while human developers patiently explained why it didn’t work. Over and over again, for days.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/blog/assets/dotnet-copilot-pr.jpg" />
        <media:content medium="image" url="https://nmn.gl/blog/blog/assets/dotnet-copilot-pr.jpg" />
      

      
        <social:metrics>
          
            <social:views>250,000+</social:views>
          
          
            <social:reddit>1300+ votes</social:reddit>
          
          
            <social:hackernews>80+ votes</social:hackernews>
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Why Good Programmers Use Bad AI</title>
      <link href="https://nmn.gl/blog/ai-and-programmers" rel="alternate" type="text/html" title="Why Good Programmers Use Bad AI" />
      <published>2025-05-17T00:00:00+00:00</published>
      <updated>2025-05-17T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-and-programmers</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-and-programmers"><![CDATA[<p><em>AI code generation is error-prone. Why, then, are programmers still using it?</em></p>

<p>Everyone from YC partners to Fiverr’s CEO has been proclaiming that “90% of code is AI-generated” or that they’re becoming “AI-first” companies.</p>

<p>The subtext they’re forcing on us is clear: <em>programmers who don’t embrace AI will be left behind.</em></p>

<p>But after two years of daily AI coding — from the earliest Cursor version to the latest agentic tools — I’ve uncovered the truth: AI coding tools are simultaneously terrible <em>and</em> necessary.</p>

<!--more-->

<h2 id="harsh-economies-of-code">Harsh Economies of Code</h2>

<p>The <a href="/blog/ai-illiterate-programmers">industry’s embrace of AI</a> makes perfect sense when you follow the money.</p>

<p>I learned this lesson from my former engineering manager. We were celebrating after a product launch, and I was complaining about some tech debt.</p>

<p><em>“You know what?”</em> he said. <em>“I’ve never once heard the CEO care about our code quality. The only thing that matters is if we can deliver before deadlines or not. Nobody’s getting promoted for clean code if the feature ships late.”</em></p>

<p>It was disappointing, but also… <em>enlightening</em>?</p>

<figure>
  <img src="/blog/assets/devs-vs-businesses-art-vs-deadlines.png" alt="Devs vs Businesses" style="max-width: 30em" />
  <figcaption>What devs prioritize vs what businesses prioritize</figcaption>
</figure>

<p>As a programmer, since I live <em>inside</em> my codebase, I want to make it perfect exactly the way I want.</p>

<p>But from the perspective of a business, my code is merely a <em>tool</em> to generate revenue.</p>

<p>And this is cold reality pushing AI adoption: businesses don’t care if the code is AI-generated or handcrafted <em>as long as it works and ships quickly.</em></p>

<h2 id="what-ai-is-actually-good-for">What AI Is Actually Good For</h2>

<p>After <a href="/blog/ai-and-learning">two years of using AI coding tools</a> extensively, I’ve found some areas where it truly shines. Here’s what I find most useful with AI:</p>

<ul>
  <li><strong>Understanding documentation:</strong> I often have to use libraries that are poorly documented. Last sprint I had to use a Python library with the worst imaginable docs: poorly structured and broken search. I pasted that link into Cursor, let it index the docs, and I was able to find precisely what I needed and ask questions around it within seconds. It would’ve taken hours of trial and error otherwise.</li>
  <li><strong>Boilerplate code:</strong> I don’t know about you, but writing repetitive framework code feels like the worst kind of drudgery to me. I know why GraphQL resolvers are needed, or how each component needs loading, error, and success states; but I really don’t like writing such code myself. Now I let an agentic AI do the grunt work for me, so I can focus on the fun parts.</li>
  <li><strong>Error debugging:</strong> In the past, the main way to debug was to search the error string, find information across multiple stackoverflow results, and condense it to a useable answer yourself. Now, the AI can do the search <em>much quicker</em> and write customized solutions by understanding our code. Why not enjoy that?</li>
</ul>

<h2 id="what-ai-fails-at">What AI fails at</h2>

<p>Even though the AI fanboys might come for me, there are some universally agreed upon issues with AI:</p>

<p><strong>Any non-mainstream tech stack.</strong> AI performs decently with React, but try something else, and the quality plummets.</p>

<p>Last month I used AI to help with a WebGL project, and it generated code that looked plausible, but simply didn’t work, because it hallucinated so many API calls.</p>

<p><strong>Complex business logic.</strong> Last week, I asked AI to refactor a function. The AI happily returned a ‘solution’ with unnecessary duplication.</p>

<p>The code <em>worked</em>, but the AI completely lacked the ability to identify the actual problem and implement an elegant solution. This problem remains because the AI doesn’t have a <a href="(/blog/ai-understand-senior-developer)">full context of your entire codebase</a>.</p>

<p><strong>The debugging overhead is real.</strong> Sometimes fixing AI’s “solution” takes longer than writing it yourself.</p>

<p><strong>Security</strong> is another weak spot. I’ve had AI confidently suggest <a href="/blog/dangers-vibe-coding">storing API keys in local storage</a> — essentially leaving my application open for exploits.</p>

<h2 id="the-correct-way-to-use-ai">The Correct Way to Use AI</h2>

<p>This reality taught me to <a href="/blog/cursor-guide">combine AI’s speed with my personal quality</a> standards:</p>

<!-- newsletter_widget -->

<ul>
  <li>I let AI draft the first version.</li>
  <li>I carve out dedicated review time to catch its blunders.</li>
  <li>I write tests around every AI-generated module.</li>
</ul>

<h2 id="how-to-thrive-not-just-survive">How to Thrive (Not Just Survive)</h2>

<p>If you’re still on the fence, here’s the exact playbook I’ve honed:</p>

<ul>
  <li>Build your verification instincts. When AI outputs code, I learned to spot the red flags: incorrect library usage, not using existing functions or patterns, incorrect domain knowledge.</li>
  <li>Operate at the edge of your abilities. Use AI for the mundane stuff (config, boilerplate, standard patterns, typical integrations), then apply your creativity to the novel problems AI can’t handle yet.</li>
  <li>Context is all you need. <strong>Always</strong> make sure to give the AI a proper context of your entire codebase. If you give the <a href="/blog/ai-understand-senior-developer">correct context, AI can do magical things</a>. If not, you will get low quality results and will often be frustrated.</li>
  <li>Ask deliberately. Instead of “implement this feature,” I break problems into smaller chunks: “Create the data model for this entity,” then “Write the validation logic for these fields,” and so on.</li>
  <li>Deep dive when things break. The best learning happens at failure points. When AI-generated code breaks, that’s your cue to really understand the underlying system. Add logs, use the debugger, manually go through each part of the code flow.</li>
  <li>Keep a “WTF AI” journal. Document every spectacular fail—hallucinated API calls, misnamed types, security oversights. Those failures become your internal library of “don’t let it do that again.” It’s therapeutic and surprisingly educational.</li>
</ul>

<h2 id="next-steps">Next Steps</h2>

<p>The uncomfortable truth is that AI coding tools aren’t optional anymore. The developers who adjust to this new reality fastest will have the most leverage, because they’ll solve higher-level problems while AI handles the mundane.</p>

<p>I don’t particularly like this reality. There’s something unsettling about watching an AI generate in seconds what would have taken me an hour. But pretending this shift isn’t happening won’t change its trajectory.</p>

<p>Perhaps in five years, we’ll look back on this transition period with nostalgia. Another tool that felt threatening until it became indispensable. Or maybe we won’t.</p>

<p>Either way, I’ll be using AI tomorrow morning, cursing its limitations, and shipping more code than I did yesterday.</p>

<p><em>Thanks to <a href="https://www.linkedin.com/in/jmontroy90/">John</a>, <a href="https://cysabi.github.io/">Cyrene</a>, and <a href="https://www.linkedin.com/in/mikkqu/">Mikhail</a> for helping me think deeper about these ideas and for sharing feedback on my drafts</em></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[AI code generation is error-prone. Why, then, are programmers still using it? Everyone from YC partners to Fiverr’s CEO has been proclaiming that “90% of code is AI-generated” or that they’re becoming “AI-first” companies. The subtext they’re forcing on us is clear: programmers who don’t embrace AI will be left behind. But after two years of daily AI coding — from the earliest Cursor version to the latest agentic tools — I’ve uncovered the truth: AI coding tools are simultaneously terrible and necessary.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>150,000+</social:views>
          
          
            <social:reddit>70+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">MCP Getting Started: Model Context Protocol on Windows</title>
      <link href="https://nmn.gl/blog/mcp-model-context-protocol-guide" rel="alternate" type="text/html" title="MCP Getting Started: Model Context Protocol on Windows" />
      <published>2025-04-25T00:00:00+00:00</published>
      <updated>2025-04-25T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/mcp-model-context-protocol-guide</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/mcp-model-context-protocol-guide"><![CDATA[<p>MCPs are a way for AIs to interact with the outside world. An MCP can allow AI to read emails, post tweets, message your friends, and much more.</p>

<p>We are used to interacting with the digital world via apps and windows—but MCPs enable an AI to do everything that humans do, without using any apps.</p>

<p>Here’s a quick guide on setting up and using your first MCPs in Windows.</p>

<!--more-->

<h2 id="choose-your-ai">Choose your AI</h2>

<p>To interact with any MCPs, you first need an app that allows you to interact with an AI.</p>

<p>This is referred to as a “client”. The top option is <a href="https://claude.ai/download">Claude Desktop</a>, but you can also use Cursor or Windsurf.</p>

<p><img src="/blog/assets/claude-desktop.jpg" alt="Claude Desktop MCP Client" /></p>

<p>For this guide, we’ll be using Claude Desktop, so install it to follow along.</p>

<h2 id="configuring-mcps">Configuring MCPs</h2>

<p>All MCP configurations follow a generic format, which is something like:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="dl">"</span><span class="s2">mcpServers</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">mcp-name</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">"</span><span class="s2">command</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">npx</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// or uvx</span>
      <span class="dl">"</span><span class="s2">args</span><span class="dl">"</span><span class="p">:</span> <span class="p">[</span>
        <span class="dl">"</span><span class="s2">-y</span><span class="dl">"</span><span class="p">,</span>
        <span class="dl">"</span><span class="s2">mcp-package-name</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// on the package registry</span>
        <span class="c1">// any other args</span>
      <span class="p">],</span>
      <span class="dl">"</span><span class="s2">env</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
        <span class="c1">// e.g. provide API keys here</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For Claude Desktop, the file you add this config is <code class="language-plaintext highlighter-rouge">%APPDATA%\Claude\claude_desktop_config.json</code></p>

<h2 id="your-first-mcp">Your first MCP</h2>

<p>The easiest (and one of the most useful) MCPs to start with is the “filesystem” MCP.</p>

<p>This allows Claude Desktop to read/write any files.</p>

<p>Here’s what you need to add in <code class="language-plaintext highlighter-rouge">mcpServers</code> in your <code class="language-plaintext highlighter-rouge">claude_desktop_config.json</code> file:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">filesystem</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
  <span class="dl">"</span><span class="s2">command</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">npx</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">args</span><span class="dl">"</span><span class="p">:</span> <span class="p">[</span>
    <span class="dl">"</span><span class="s2">-y</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">@modelcontextprotocol/server-filesystem</span><span class="dl">"</span><span class="p">,</span>
    <span class="c1">// list of folders you want Claude to be able to access</span>
    <span class="dl">"</span><span class="s2">C:</span><span class="se">\\</span><span class="s2">Users</span><span class="se">\\</span><span class="s2">username</span><span class="se">\\</span><span class="s2">Desktop</span><span class="dl">"</span>
    <span class="c1">// I let it access the Claude folder itself, so Claude</span>
    <span class="c1">// can modify the `claude_desktop_config.json` file by itself</span>
    <span class="c1">// to add/remove any MCP servers I want</span>
    <span class="dl">"</span><span class="s2">C:</span><span class="se">\\</span><span class="s2">Users</span><span class="se">\\</span><span class="s2">username</span><span class="se">\\</span><span class="s2">AppData</span><span class="se">\\</span><span class="s2">Roaming</span><span class="se">\\</span><span class="s2">Claude</span><span class="dl">"</span>
  <span class="p">],</span>
  <span class="dl">"</span><span class="s2">env</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">DEBUG</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">*</span><span class="dl">"</span>
  <span class="p">}</span>
<span class="p">},</span>
</code></pre></div></div>

<p>After setting up any MCP, you will need to quit and restart Claude from the Task Manager for it to refresh your config.</p>

<p>But once that’s done, Claude will be able to read/edit any of your files. This is what it looks like:</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">working! claude can now add it&#39;s own MCPs via an MCP :)<br /><br />now to get the Twitter MCP to work: I need to get my Twitter API keys <a href="https://t.co/rajLZ6cLkN">https://t.co/rajLZ6cLkN</a> <a href="https://t.co/6wQ3H5a1ri">pic.twitter.com/6wQ3H5a1ri</a></p>&mdash; neo (@NamanyayG) <a href="https://twitter.com/NamanyayG/status/1915807186254496220?ref_src=twsrc%5Etfw">April 25, 2025</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<h2 id="whats-next">What’s next?</h2>

<p>I am experimenting with MCPs, with my eventual goal being to complete all of my regular tasks (reading/writing emails, tweets, reddit, etc.) through an AI. Stay tuned for more posts about interesting things you can do with MCPs!</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">i slept on MCPs... but they are pure DISRUPTION<br /><br />the world of apps/windows/websites is DEAD <br /><br />(people just don&#39;t know it yet)<br /><br />So, I&#39;m starting something to show everyone how powerful this can be<br /><br />&amp; ofc building in public + open source<br /><br />MY GOAL: eliminate ALL windows, apps,… <a href="https://t.co/4bpwCRScop">pic.twitter.com/4bpwCRScop</a></p>&mdash; neo (@NamanyayG) <a href="https://twitter.com/NamanyayG/status/1915796892409045351?ref_src=twsrc%5Etfw">April 25, 2025</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="https://x.com/NamanyayG/status/1915796892409045351">My MCP experiment</a></li>
  <li><a href="https://gist.github.com/feveromo/7a340d7795fca1ccd535a5802b976e1f">Setting up MCPs on Windows</a></li>
  <li><a href="https://modelcontextprotocol.io/quickstart/user">Official MCP Docs</a></li>
  <li><a href="https://modelcontextprotocol.io/quickstart/user">List of interesting MCP servers</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="mcp" />
        
      

      

      
      
        <summary type="html"><![CDATA[MCPs are a way for AIs to interact with the outside world. An MCP can allow AI to read emails, post tweets, message your friends, and much more. We are used to interacting with the digital world via apps and windows—but MCPs enable an AI to do everything that humans do, without using any apps. Here’s a quick guide on setting up and using your first MCPs in Windows.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">My AI Prompt Engineering Playbook for Developers</title>
      <link href="https://nmn.gl/blog/ai-prompt-engineering" rel="alternate" type="text/html" title="My AI Prompt Engineering Playbook for Developers" />
      <published>2025-04-21T00:00:00+00:00</published>
      <updated>2025-04-21T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-prompt-engineering</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-prompt-engineering"><![CDATA[<p>You know by now that AI can dramatically speed up your development process (<a href="/blog/ai-understand-senior-developer">when used correctly</a>.)</p>

<p><em>But</em> the key is knowing how to communicate with the AI properly.</p>

<p>Here’s my collection of prompts that actually work in real-world scenarios.</p>

<!--more-->

<h2 id="getting-to-the-root-of-problems">Getting to the Root of Problems</h2>

<p>The most common mistake developers make is treating symptoms rather than causes. This prompt helps you break that cycle:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Analyze this error/bug:
[paste error]

Don't just fix the immediate issue. Identify the underlying root cause by:
1. Examining potential architectural problems
2. Considering edge cases that might trigger this
3. Suggesting a comprehensive solution that prevents similar issues

Focus on fixing the core problem, not just the symptom. Before giving a solution, give me a reasoned analysis about why and how you're fixing the root cause.
</code></pre></div></div>

<p><strong>When to use it:</strong> Whenever you hit a frustrating bug that keeps coming back despite your fixes. This approach saves hours of debugging.</p>

<h2 id="understanding-ai-generated-code">Understanding AI-Generated Code</h2>

<p>Never blindly implement code you don’t understand. This prompt ensures you know exactly what you’re adding to your codebase:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Can you explain what you generated in detail:
1. What is the purpose of this section?
2. How does it work step-by-step?
3. What alternatives did you consider and why did you choose this one?
</code></pre></div></div>

<p><strong>When to use it:</strong> Every single time you get code from an AI. No exceptions. Your future self will thank you.</p>

<h2 id="debugging">Debugging</h2>

<p>When you’re truly stuck on a problem, sometimes you need to break out of your usual thought patterns. This prompt helps you systematically explore possibilities from multiple angles:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Help me debug this issue: [code and logs]

Reflect on 5-7 different possible sources of the problem, thinking from a variety of creative angles that you might not normally consider. 

Distill those down to 1-2 most likely sources.

Ideate on which one it could be and add logs to test that.

Give a detailed analysis on why you think you've understood the issue, how it occurs, and the easiest way to fix it.
</code></pre></div></div>

<p><strong>When to use it:</strong> When stuck debugging serious issues. This prompt forces the AI to step back and consider the problem holistically rather than diving too quickly into a single possible explanation.</p>

<h2 id="code-reviews">Code Reviews</h2>

<p>AI can catch issues human reviewers might miss:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Review the code in the files [include files here]

Focus on:
1. Logic flaws and edge cases
2. Performance bottlenecks
3. Security vulnerabilities
4. Maintainability concerns

Suggest specific improvements with brief explanations. First, give a detailed plan. Then, implement it with the least changes and updating minimal code.
</code></pre></div></div>

<p><strong>When to use it:</strong> Before submitting PRs or after finishing a feature but before considering it “done.”</p>

<h2 id="refactoring">Refactoring</h2>

<p>Transform spaghetti code into something maintainable:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Refactor this function to be more:
[paste code]

Make it:
- More readable (clear variable names, logical structure)
- Maintainable (smaller functions with single responsibilities)
- Testable (easier to write unit tests)

Ensure that you do not change too much and that this part of the code remains useable without changing other parts that might depend on it.

First, explain your changes and why they improve the code. 
</code></pre></div></div>

<p><strong>When to use it:</strong> When your code has grown unwieldy over time. Use this only for small parts of the code, you are likely to break things if you refactor too much at once.</p>

<h2 id="security">Security</h2>

<p>I wrote <a href="https://nmn.gl/blog/vibe-security-checklist">an entire guide on security</a> recently where I cover how to audit your code for common vulnerabilities.</p>

<h2 id="rage-prompting">Rage Prompting</h2>

<p>Sometimes, frustration can actually help get results from AI. The “rage prompt” works surprisingly well. Feel free to add more rage depending on your patience levels:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>This code is DRIVING ME CRAZY. It should be doing [expected behavior] but instead it's [actual behavior]. 
PLEASE help me figure out what's wrong with it:
[paste code]
</code></pre></div></div>

<p><strong>When to use it:</strong> When you need direct results without excessive explanation.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The right prompts don’t just save time: they fundamentally change how quickly you can ship reliable products.</p>

<p>By using these prompts consistently, you’ll avoid the common issues of AI development while capitalizing on its strengths.</p>

<p>What prompts have saved you time in your own development process? I’d love to hear about them.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[You know by now that AI can dramatically speed up your development process (when used correctly.) But the key is knowing how to communicate with the AI properly. Here’s my collection of prompts that actually work in real-world scenarios.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>250,000</social:views>
          
          
            <social:reddit>700+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Infantilization at Big Tech</title>
      <link href="https://nmn.gl/blog/big-tech-daycare" rel="alternate" type="text/html" title="Infantilization at Big Tech" />
      <published>2025-04-17T00:00:00+00:00</published>
      <updated>2025-04-17T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/big-tech-daycare</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/big-tech-daycare"><![CDATA[<p>The first time I encountered Big Tech was at age 15 when I won <a href="https://opensource.googleblog.com/2015/02/google-code-in-2014-welcome-to-winners.html">Google Code In</a>. They flew me and my family to San Francisco and showed us around the Googleplex. I arrived with wide eyes, eager to see where the “smartest people in the world” worked.</p>

<p>But, what I found… <em>disturbed</em> me.</p>

<p>Everyone wore the same badges, slept in nap pods, played the same games, and ate at the same cafeterias. I couldn’t escape the realization that I was looking at a daycare for adults.</p>

<p>That day, I silently promised myself I would <em>never</em> work in such an environment.</p>

<!--more-->

<h2 id="adult-daycare">Adult Daycare</h2>

<p>In recent months, I’ve connected with many people working at Big Tech companies. They’re all paid extremely well and enjoy enviable work-life balance. Yet almost without exception, they share a common trait: deep frustration with their jobs.</p>

<p>Let’s acknowledge some positive facts about Big Tech companies:</p>

<ul>
  <li>They pay extraordinarily well</li>
  <li>They offer flexible work schedules &amp; generous PTO</li>
</ul>

<p>But there’s a catch:</p>

<ul>
  <li>Employees typically work on tiny, fragmented slivers of massive projects</li>
  <li>A significant portion of the workday disappears into meetings, documentation, and communication</li>
  <li>Promotion-chasing and office politics consume enormous mental energy</li>
</ul>

<p>Seeing these patterns, it’s no surprise my Big Tech friends feel unfulfilled.</p>

<p>But the question that took me years to answer was: <em>why do these companies deliberately foster such a work culture?</em></p>

<h2 id="insiduous-reality">Insiduous Reality</h2>

<p>There’s a form of corporate doublethink inside Big Tech that only became clear to me after reaching adulthood.</p>

<p>I first sensed it during my teenage visit, watching Google shower us with free gifts for open source contributions that had virtually no business value. It reminded me of the saying: <em>“If something is free, <strong>you</strong> are the product.”</em></p>

<p>Google Code In wasn’t about open source at all. It existed solely to give programming-savvy students a taste of the Google lifestyle, to implant the idea of working there as early as possible. The free swag, the campus tour, the friendly recruiters — it was all strategic.</p>

<p>I’ve since realized the truth: Big Tech companies massively overpay for one critical reason: they don’t want talented individuals to <em>work anywhere else.</em></p>

<h2 id="dont-let-them-work-anywhere-else">Don’t Let Them Work Anywhere Else</h2>

<p>These trillion-dollar companies understand a terrifying truth: if brilliant young developers <em>actually</em> got together, they could build business models that would utterly disrupt traditional tech giants.</p>

<p>So what’s their solution? Simple: <em>don’t let that talent work anywhere else.</em></p>

<p>Of course, they can’t force anyone, so they seduce with what talented people want: money, prestige, comfort, and the illusion of impact.</p>

<p>And to succeed, they must capture the right talent at <em>precisely</em> the right moment. I call this deliberate strategy <em>“talent capture.”</em></p>

<h2 id="talent-capture">Talent Capture</h2>

<p>First, they have to recruit talent from top schools. Next, they must retain these recruits for at least 4-5 years: long enough to drain away the courage of youth and install mindsets that make it nearly impossible to leave.</p>

<p>They’ve perfected this through three techniques:</p>

<ul>
  <li>Offering compensation packages that dwarf anything else available to new graduates, easy to do with the absurd profit margins in software.</li>
  <li>The trap of “equity” and “vesting.” By withholding a significant portion of compensation until completing four years, they dangle the golden carrot that keeps talent locked in place.</li>
  <li>Creating environments where making mistakes doesn’t have any harsh consequences, making people risk-averse.</li>
</ul>

<p>During those critical four years, they methodically:</p>

<ul>
  <li>Encourage employees to climb the hedonic treadmill by displaying the lifestyle of senior employees</li>
  <li>Make them comfortable with unsustainable spending habits</li>
  <li>Fragment work to such a degree that no one develops the comprehensive skills needed to build something independently</li>
  <li>Create bubbles where everyone believes this is simply “how work is”</li>
</ul>

<h2 id="breaking-free">Breaking Free</h2>

<p>By the time most people realize they’re trapped, they’re already deeply enmeshed. The golden handcuffs rewire their brains.</p>

<p>I’ve watched friends transform from innovators to people who measure success by equity packages. Their dreams of building something of their own gradually dissolve into discussions about which team might offer better promotion prospects.</p>

<p>Meanwhile, with each passing year, the courage required to leave grows exponentially. The financial risk of pursuing your own ideas seems increasingly irrational when compared to the millions from staying put. What can be done?</p>

<h2 id="your-creative-potential">Your Creative Potential</h2>

<p>If you’re reading this while working at a Big Tech company, you might be feeling defensive or dismissive. That’s normal. We all protect the choices we’ve made.</p>

<p>But ask yourself honestly: when was the last time you built something that genuinely <em>excited</em> you?</p>

<p>For those outside looking in, wondering if you’re missing out by not landing that coveted FAANG job: perhaps you’ve accidentally avoided the most sophisticated trap ever created?</p>

<p>Remember, your most valuable asset isn’t your coding ability or your degree. It’s your capacity to imagine and build something new. And that’s precisely what the adult daycare is designed to extinguish.</p>

<h2 id="the-path-forward">The Path Forward</h2>

<p>If this post resonates, remember you are not alone. If you’re already caught in the system, recognize that the longer you stay, the harder it becomes to leave. Consider setting a firm exit date and working backward from there. And make sure you save aggressively instead of matching the lifestyle inflation around you.</p>

<p>For those still deciding their path, understand that there’s nothing wrong with spending a few years at Big Tech to build skills and savings. The danger comes when those “few years” become a decade because the golden handcuffs tightened so gradually you never noticed.</p>

<p>Your talents are too valuable to waste on making already wealthy shareholders slightly wealthier. I don’t think that the world needs trillion-dollar companies perfecting ways to further capture our attention and data.</p>

<p>What we instead need are builders willing to solve real problems, even when the challenges seem impossible.</p>

<p>The big tech daycare was never designed for your fulfillment. It was designed for your containment.</p>

<p>The choice, as always, remains yours.</p>

<p>But at least now you understand the game.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="life" />
        
      

      

      
      
        <summary type="html"><![CDATA[The first time I encountered Big Tech was at age 15 when I won Google Code In. They flew me and my family to San Francisco and showed us around the Googleplex. I arrived with wide eyes, eager to see where the “smartest people in the world” worked. But, what I found… disturbed me. Everyone wore the same badges, slept in nap pods, played the same games, and ate at the same cafeterias. I couldn’t escape the realization that I was looking at a daycare for adults. That day, I silently promised myself I would never work in such an environment.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">The day I taught AI to understand code like a Senior Developer</title>
      <link href="https://nmn.gl/blog/ai-understand-senior-developer" rel="alternate" type="text/html" title="The day I taught AI to understand code like a Senior Developer" />
      <published>2025-04-07T00:00:00+00:00</published>
      <updated>2025-04-07T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-understand-senior-developer</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-understand-senior-developer"><![CDATA[<p><em>Is it just me, or are the code generation LLMs we’re all using not that good?</em></p>

<p>For months, I’ve watched developers praise LLMs while silently cleaning up their messes, afraid to admit how much babysitting they actually need.</p>

<p>I realized that LLMs don’t actually <em>understand</em> codebases — they’re just sophisticated autocomplete tools (with good marketing.)</p>

<p>After two years of frustration watching my AI assistants constantly “forget” where files were located, create duplicates, and use completely incorrect patterns, <a href="https://gigamind.dev/" target="_blank">I finally built what the big AI companies couldn’t</a> — or wouldn’t.</p>

<p>I decided to find out: What if I could make AI <em>actually</em> understand how my codebase works?</p>

<!--more-->

<h2 id="illusion-of-comprehension">Illusion of Comprehension</h2>

<p>Last December, I hit my breaking point. My supposedly “intelligent” AI kept generating components that didn’t follow our established patterns. When I pointed this out, it apologized — and then proceeded to make the exact same mistake ten minutes later.</p>

<p>This wasn’t a one-off. This was the norm.</p>

<p>The problem became clear: these AI tools don’t have any actual understanding of codebases as interconnected systems. They’re operating on small context windows and failing spectacularly at maintaining a mental model of the project.</p>

<p>What makes this particularly frustrating is how the marketing from major AI companies implies their tools “understand” your code. They don’t. They’re making educated guesses — and the difference becomes painfully obvious on any moderately complex project.</p>

<h2 id="junior-vs-senior-mindset">Junior vs Senior Mindset</h2>

<p>While thinking about this problem, I went back to first principles and tried to analyze how senior developers think about a codebase:</p>

<ul>
  <li>Junior developers focus on <em>“what”</em> and <em>“how”</em> — they look at specific implementation details to solve immediate problems. They can write functioning code, but they often miss the bigger picture.</li>
  <li>Senior developers focus on <em>“why”</em> and <em>“what if”</em> — they maintain a mental model of the entire system, understand how components interact, and anticipate ripple effects of changes. They distinguish between code that’s incidental versus foundational.</li>
</ul>

<p>I realized LLMs often operate like <a href="/blog/ai-and-learning">junior developers</a>, not senior ones:</p>

<ul>
  <li>They get lost in large codebases because they lack a high-level understanding.</li>
  <li>They write duplicate functionality rather than recognizing reuse opportunities.</li>
  <li>They operate on trial and error, rushing to write code without understanding the complete context.</li>
  <li>They follow patterns inconsistently, not grasping their underlying purpose.</li>
</ul>

<p>My goal became clear: could I make AI think more like senior developers by helping improving its model of the codebase?</p>

<h2 id="breakthrough">Breakthrough</h2>

<p>The solution came to me during a 2 AM coding session, while I was dealing with yet another incorrectly generated file: what if we treated the codebase as a <a href="https://gigamind.dev/" target="_blank"><strong>hierarchical knowledge graph</strong></a> instead of flat files?</p>

<p><img src="/blog/assets/code_graph.png" style="width: 20em; margin: 0 auto; display: block;" /></p>

<p>Human developers don’t memorize entire codebases. We build mental models of how components relate to each other. We understand that some code is boilerplate, while other sections are critical business logic. We naturally view code through different “lenses” depending on what we’re trying to accomplish.</p>

<p>I developed what I call “Ranked Recursive Summarization” (RRS), an algorithm that starts from the leaves of a project’s directory tree and recursively builds understanding upward using LLMs:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># pseudocode:
</span><span class="k">def</span> <span class="nf">ranked_recursive_summarization</span><span class="p">(</span><span class="n">folder</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">is_file</span><span class="p">(</span><span class="n">folder</span><span class="p">):</span>
        <span class="n">chunks</span> <span class="o">=</span> <span class="n">split_into_chunks</span><span class="p">(</span><span class="n">read_file</span><span class="p">(</span><span class="n">folder</span><span class="p">))</span>
        <span class="n">ranked_chunks</span> <span class="o">=</span> <span class="n">rank_by_importance</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">summarize</span><span class="p">(</span><span class="n">ranked_chunks</span><span class="p">)</span>
    
    <span class="n">summaries</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">folder</span><span class="p">.</span><span class="n">children</span><span class="p">:</span>
        <span class="n">summary</span> <span class="o">=</span> <span class="n">RRS</span><span class="p">(</span><span class="n">child</span><span class="p">)</span>
        <span class="n">summaries</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">summary</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">summarize</span><span class="p">(</span><span class="n">summaries</span><span class="p">)</span>
</code></pre></div></div>

<p>This worked <strong>shockingly</strong> well. I implemented this algorithm and <a href="https://gigamind.dev/context" target="_blank">released it on Reddit</a>, there were hundreds of people asking for access in the same day.</p>

<p>But, I soon realized it wasn’t enough. Depending on what I was trying to work on, RRS missed certain details. If it had information about architecture and data models, it missed out on frontend components. If it was too focused on UI, it missed out on describing data flow.</p>

<p>I had to think deeper: what makes a certain part of the code <em>important</em>?</p>

<h2 id="lensed-understanding">Lensed Understanding</h2>

<p>My second insight led to the truly transformative technique: “Prismatic Ranked Recursive Summarization” (PRRS).</p>

<p>Instead of having one global definition of “importance,” PRRS analyzes code through multiple conceptual lenses:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># pseudocode:
</span><span class="k">def</span> <span class="nf">prismatic_rrs</span><span class="p">(</span><span class="n">folder</span><span class="p">,</span> <span class="n">lenses</span><span class="o">=</span><span class="p">[</span><span class="s">'architecture'</span><span class="p">,</span> <span class="s">'data_flow'</span><span class="p">,</span> <span class="s">'security'</span><span class="p">]):</span>
    <span class="n">summaries</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">lens</span> <span class="ow">in</span> <span class="n">lenses</span><span class="p">:</span>
        <span class="n">context</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"Analyze importance from </span><span class="si">{</span><span class="n">lens</span><span class="si">}</span><span class="s"> perspective"</span>
        <span class="n">summaries</span><span class="p">[</span><span class="n">lens</span><span class="p">]</span> <span class="o">=</span> <span class="n">RRS</span><span class="p">(</span><span class="n">folder</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="n">context</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">summaries</span>
</code></pre></div></div>

<p>The results were undeniable. AI suddenly understood:</p>

<ul>
  <li>Where files should logically be placed</li>
  <li>Which patterns to follow</li>
  <li>How to extend existing abstractions instead of creating new ones</li>
  <li>When to reuse code vs. create new implementations</li>
</ul>

<p>I updated my code to include PRRS, and the quality of results shot up. Plain RRS doesn’t work well on monorepos, but <a href="https://gigamind.dev/context" target="_blank">PRRS is able to clearly understand and explain</a> much larger codebases.</p>

<p>Honestly, the approach isn’t particularly complex or compute-intensive. The big AI companies could have implemented something like this from the beginning. But they haven’t, because it doesn’t align with their incentives of getting results for the lowest token costs.</p>

<h2 id="why-this-matters">Why This Matters</h2>

<p>The implications go far beyond fixing basic errors. When AI truly understands your codebase:</p>

<ol>
  <li>Technical debt becomes visible through the “architecture” lens</li>
  <li>Security vulnerabilities emerge naturally through the “security” lens</li>
  <li>Junior developers can navigate complex projects with senior-level insight</li>
  <li>Onboarding time for new team members decreases dramatically</li>
</ol>

<p>There’s a darker side as well. As AI gets better at understanding codebases, the value of certain types of programming knowledge decreases: the <a href="/blog/ai-illiterate-programmers">mid-level programmer</a> who primarily translates requirements into code without architectural insight may find themselves increasingly squeezed.</p>

<p>After experimenting with these techniques for several weeks, I eventually <a href="https://gigamind.dev/context" target="_blank">packaged them into a tool called Giga AI</a>. I built it initially to solve my own frustrations, but other developers kept asking to try it after seeing how it changed my workflow. Developers who use Giga report less time spent correcting AI-generated code, are able to ship faster, and feeling less frustrated.</p>

<h2 id="implementation">Implementation</h2>

<p>Even without my specific tool, you can improve your AI assistant’s code understanding:</p>

<!-- Here's how you can use AI today to improve your codebase's understanding: -->

<ul>
  <li>Create manual summaries of your most important directories and files</li>
  <li>Ask an AI to further improve your manual documentation</li>
  <li>Create and ensure multiple documentation files, each from a different “lens”, based on your project</li>
  <li>Include relevant files into AI’s context as needed</li>
</ul>

<p>These approaches won’t be as seamless as a <a href="https://gigamind.dev">purpose-built solution</a>, but they’ll dramatically improve your results compared to the default experience.</p>

<h2 id="context-is-the-future">Context is the Future</h2>

<p>I believe we’re at the beginning of a fundamental shift in how AI understands complex systems like codebases. The next generation of tools won’t just create embeddings of your code — they’ll build rich mental models from multiple perspectives, just like experienced developers do.</p>

<p>The companies that embrace this approach will leapfrog those still focused on token prediction alone. And developers who learn to leverage these sophisticated tools will have sustainable advantages that mere “prompt engineering” can’t match.</p>

<p>The future belongs to those who see AI not as a replacement for human developers, but as a force multiplier for human ingenuity.</p>

<p>And that future starts <strong>now</strong>.</p>

<p><em>What frustrations have you experienced with AI? I’d love to hear your stories at <a href="mailto:hi@nmn.gl">hi@nmn.gl</a></em></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[Is it just me, or are the code generation LLMs we’re all using not that good? For months, I’ve watched developers praise LLMs while silently cleaning up their messes, afraid to admit how much babysitting they actually need. I realized that LLMs don’t actually understand codebases — they’re just sophisticated autocomplete tools (with good marketing.) After two years of frustration watching my AI assistants constantly “forget” where files were located, create duplicates, and use completely incorrect patterns, I finally built what the big AI companies couldn’t — or wouldn’t. I decided to find out: What if I could make AI actually understand how my codebase works?]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/ai-understand-senior-developer.png" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/ai-understand-senior-developer.png" />
      

      
    </entry>
  
    <entry>
      

      <title type="html">Context, Structure, Organization: The AI Development Trifecta</title>
      <link href="https://nmn.gl/blog/ai-dev-tips" rel="alternate" type="text/html" title="Context, Structure, Organization: The AI Development Trifecta" />
      <published>2025-04-01T00:00:00+00:00</published>
      <updated>2025-04-01T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-dev-tips</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-dev-tips"><![CDATA[<p>AI is incredibly powerful, but it needs guidelines. “Vibe coding” might work initially, but as the project grows, it creates more mistakes than it solves. After fixing countless AI implementations, I’ve distilled it down to three core principles that actually work.</p>

<p>The current wave of AI tools promises to 10x your development speed. What they don’t mention is how they can also 10x your debugging time if implemented poorly. I’m building tools to solve exactly this problem, and I’m sharing some lessons I’ve learned along the way.</p>

<!--more-->

<h2 id="1-structure-your-ais-context">1. Structure Your AI’s Context</h2>

<p>The “context” is the only thing the AI knows. If it’s incomplete, the <a href="/blog/dangers-vibe-coding">AI will make mistakes</a>. To increase accuracy, you need to provide the AI with as much relevant context as possible.</p>

<ol>
  <li>
    <p>Create a <code class="language-plaintext highlighter-rouge">project_milestones.md</code> file that contains an overview of the project and your goals, along with a list of milestones and their descriptions. You may generate this file using chatgpt or claude, or you can create it manually. Then, reference it in your rules. Example:</p>

    <div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Phase 1: Manual Operations &amp; Testing
<span class="p"> -</span> Basic API integration
<span class="p"> -</span> Database schema setup
<span class="p"> -</span> Basic OpenAI integration
<span class="p"> -</span> Manual data collection script
<span class="p"> -</span> Manual analysis trigger
<span class="p"> -</span> Manual review interface
 Phase 2: ...
</code></pre></div>    </div>
  </li>
  <li>Maintain a <code class="language-plaintext highlighter-rouge">documentation.md</code> file that tracks things relevant to your project from different perspectives. This might include, but not limited to:
    <ul>
      <li>API endpoints and their request/response formats</li>
      <li>Database schemas</li>
      <li>Function specifications</li>
      <li>Architecture decisions</li>
    </ul>
  </li>
  <li>Set up clear rules in your <code class="language-plaintext highlighter-rouge">.cursorrules</code> file to ensure that the AI uses the newly created files:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Project Management:
  - Reference @project_milestones.md for all feature implementations
  - Reference @documentation.md for all API endpoints
  - Ensure new code aligns with defined milestones
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="2-break-down-complex-tasks">2. Break Down Complex Tasks</h2>

<p>Back-and-forth with the model often leads to <a href="/blog/vibe-coding-fantasy">compounding errors</a>. Keep tasks focused. When something breaks, don’t spiral into a debugging maze. Instead:</p>

<ol>
  <li>Revert to a previous working state using the checkpoints feature or git.</li>
  <li>Break the task into smaller, isolated changes. Test each tiny change independently, only once it’s working, move on to the next change.</li>
  <li>If the model starts creating new problems while fixing old ones, stop and reassess. Revert to a previous working state, then start the task again. Do not keep working in a long chat, because as the context grows, the AI becomes more likely to make mistakes.</li>
</ol>

<!-- newsletter_widget -->

<h2 id="3-use-the-right-models">3. Use the Right Models</h2>

<p>There are so many AI models, understand the strengths and weaknesses of each and use the right ones at the right time. Adapt your AI workflow based on task types:</p>

<p><strong>For Planning:</strong></p>
<ul>
  <li>Use reasoning-focused models (like 3.7 max mode)</li>
  <li>Create high-level architecture decisions first</li>
  <li>Plan feature roadmaps and save it in md files like <code class="language-plaintext highlighter-rouge">project_milestones.md</code></li>
</ul>

<p><strong>For Implementation:</strong></p>
<ul>
  <li>Use standard models for code generation (my favorite is sonnet 3.5)</li>
  <li>Ensure each task is well-defined and small</li>
  <li>Test each change independently</li>
  <li>Commit to git early and often</li>
</ul>

<h2 id="making-it-work-in-practice">Making It Work In Practice</h2>

<p>Here’s a <a href="/blog/cursor-guide">practical setup</a> that works:</p>

<ol>
  <li>Project Structure:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── .cursor/
│   ├── rules
├── docs/
│   ├── project_milestones.md
│   └── documentation.md
└── src/
</code></pre></div>    </div>
  </li>
  <li>Regular Maintenance:
    <ul>
      <li>Reindex your codebase regularly in Settings → Cursor settings → Features</li>
      <li>Update documentation as you complete milestones by tracking architecture decisions and their reasoning</li>
    </ul>
  </li>
  <li>Development Workflow:
    <ul>
      <li>Start each session by referencing your project context defined in the <code class="language-plaintext highlighter-rouge">documentation.md</code> and <code class="language-plaintext highlighter-rouge">project_milestones.md</code> files</li>
      <li>Work in small, testable increments</li>
      <li>Update milestone progress and documentation consistently, you can even ask the AI to do it for you</li>
      <li>Document learnings and edge cases</li>
      <li>Commit to git early and often</li>
    </ul>
  </li>
</ol>

<h2 id="looking-forward">Looking Forward</h2>

<p>The teams who will win with AI are the ones with solid fundamentals. This structured approach might seem annoying at first, but it’s the difference between AI being a productivity multiplier versus a technical liability in the long-term.</p>

<p>Remember: AI is a powerful tool, but it needs clear boundaries and expectations. By implementing these principles, you’re building a foundation that can reliably scale as AI capabilities grow.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[AI is incredibly powerful, but it needs guidelines. “Vibe coding” might work initially, but as the project grows, it creates more mistakes than it solves. After fixing countless AI implementations, I’ve distilled it down to three core principles that actually work. The current wave of AI tools promises to 10x your development speed. What they don’t mention is how they can also 10x your debugging time if implemented poorly. I’m building tools to solve exactly this problem, and I’m sharing some lessons I’ve learned along the way.]]></summary>
      

      
      

      
        <social:metrics>
          
          
            <social:reddit>200+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Making an emoji terminal game in an hour</title>
      <link href="https://nmn.gl/blog/emoji-mastermind" rel="alternate" type="text/html" title="Making an emoji terminal game in an hour" />
      <published>2025-03-31T00:00:00+00:00</published>
      <updated>2025-03-31T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/emoji-mastermind</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/emoji-mastermind"><![CDATA[<p>I recently moved to NYC since I was accepted by the <a href="https://recurse.com/">Recurse Center</a>, and today was my first day at their hub.</p>

<p>The day started by nerding out on the retro computers, hardware labs, and 3d printers they have; followed by the first breakfast bagel of my life. I feel incredibly fortunate to be surrounded out by so many talented programmers and am looking forward to the next three months with my new friends!</p>

<!--more-->

<p>As part of our first day, we had a workshop about pair programming. For those who don’t know, pair programming involves two people working together on one computer, helping accomplish a specified programming goal and sharing ideas and approaches with each other. It’s one of the best ways to connect with fellow programmers, you learn a lot about each other.</p>

<p><a href="https://github.com/durumu">Presley</a> and I were paired together and tasked to build a “Mastermind” game in an hour using Python. Mastermind is basically Wordle’s predecessor but uses colors instead of letters. I don’t know idomatic Python, but fortunately Presley is extremely knowledgeable so we were a quite capable team.</p>

<p>We built out a basic TUI Mastermind implementation in around 45 minutes. We originally planned to convert it to a full GUI at the end, but we were pragmatic and realized that we weren’t going to be able to do that in the remaining time.</p>

<p>Instead, we decided to go in a slightly different direction: <strong>why not emojis?</strong></p>

<p>We finalized our entire theme around the concept of emojis, making the final version called “Emoji Mastermind”. Instead of colors, you use emojis. Instead of ‘pegs’, you get hearts. A fun twist on the classic game. We demonstrated to our peers to much applause!</p>

<figure>
  <img src="/blog/assets/emoji-mastermind.png" alt="Emoji Mastermind" />
  <figcaption>Emoji Mastermind in action</figcaption>
</figure>

<p>Check out <a href="https://github.com/namanyayg/emoji-mastermind">Emoji Mastermind on Github</a>, I’d love to have you try it out!</p>

<!-- newsletter_widget -->

<p>By the way, if you’re interested in taking your programming skills to the next level, check out the <a href="https://recurse.com/">Recurse Center</a> and consider applying!</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="recurse" />
        
      

      

      
      
        <summary type="html"><![CDATA[I recently moved to NYC since I was accepted by the Recurse Center, and today was my first day at their hub. The day started by nerding out on the retro computers, hardware labs, and 3d printers they have; followed by the first breakfast bagel of my life. I feel incredibly fortunate to be surrounded out by so many talented programmers and am looking forward to the next three months with my new friends!]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Karpathy’s ‘Vibe Coding’ Movement Considered Harmful</title>
      <link href="https://nmn.gl/blog/dangers-vibe-coding" rel="alternate" type="text/html" title="Karpathy’s ‘Vibe Coding’ Movement Considered Harmful" />
      <published>2025-03-27T00:00:00+00:00</published>
      <updated>2025-03-27T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/dangers-vibe-coding</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/dangers-vibe-coding"><![CDATA[<p>Last Tuesday at 1 AM, I was debugging a critical production issue in my AI dev tool. As I dug through layers of functions, I suddenly realized — unlike the new generation of developers, I was <em>grateful</em> that I could actually understand my codebase. That’s when I started thinking more about Karpathy’s recent statements on vibe coding.</p>

<p>For those who missed it, Andrej Karpathy recently shared his thoughts on what he calls “vibe coding” — essentially surrendering code comprehension to AI tools and hoping for the best. His exact words? <em>“I ‘Accept All’ always, I don’t read the diffs anymore.”</em></p>

<p>I have learnt a lot from Karpathy and use AI tools daily, but there’s a world of difference between augmenting your capabilities and completely surrendering your understanding.</p>

<!--more-->

<h2 id="hidden-costs-of-vibing">Hidden Costs of Vibing</h2>

<p>Last month, I encountered a particularly annoying bug in my payment system. The code looked clean — (since ChatGPT had helped me write it). But when users started reporting random issues with their payments not being recognized, I couldn’t just paste the error into an AI and pray. I had to understand the underlying payment management logic and the exact request flow to fix it.</p>

<p>This is where “vibe coding” falls apart. The real problem isn’t just about reading code — it’s about maintaining the intellectual ownership of our systems. When Karpathy says <em>“The code grows beyond my usual comprehension, I’d have to really read through it for a while,”</em> he’s describing a <a href="/blog/ai-illiterate-programmers">fundamental breakdown in engineering responsibility</a>.</p>

<h2 id="exponential-technical-debt">Exponential Technical Debt</h2>

<p>Technical debt compounds <strong>exponentially</strong> when <a href="/blog/ai-and-learning">you don’t understand your code</a>. Each “vibed” solution becomes a black box, and these black boxes multiply. Soon, you’re building on top of foundations you don’t comprehend.</p>

<p>A recent feature I implemented initially took this route. The AI-generated solution worked, but when I needed to optimize it for performance, I was stuck. I couldn’t optimize what I didn’t understand. I ended up rewriting it from scratch, this time ensuring I understood every line.</p>

<h2 id="security-nightmares">Security Nightmares</h2>

<p>The <a href="/blog/vibe-coding-fantasy">security implications</a> of “vibe coding” are… unreal. When you don’t understand your code, you can’t properly assess its vulnerabilities.</p>

<p>Last week, I reviewed some AI-generated authentication code that looked perfectly fine at first glance. But upon closer inspection, the OpenAI API keys were exposed to anyone who knew how to inspect the network calls. This kind of oversight happens when we trust AI without understanding security best practices.</p>

<h2 id="a-better-path-forward">A Better Path Forward</h2>

<p>The real danger isn’t in using AI — it’s in surrendering our understanding to it. Vibe coding might work for a weekend project, but it’s catastrophic for any serious software.</p>

<p>While coding my AI that makes <a href="https://gigamind.dev/">software development faster</a>, I’ve developed a comprehensive approach that balances AI assistance with engineering excellence:</p>

<ol>
  <li>
    <p>When using AI tools, start with a clear architectural vision. Before generating any code, document your requirements, constraints, and expected behavior. This becomes your validation framework for any AI-generated code.</p>
  </li>
  <li>Instead of accepting entire functions or components from AI, break them down into smaller, understandable chunks. I’ve developed a three-step process:
    <ul>
      <li>Generate small, focused pieces of functionality</li>
      <li>Review and understand each piece thoroughly</li>
      <li>Integrate only after validation and testing</li>
    </ul>
  </li>
  <li>Make sure the <a href="https://gigamind.dev/">AI has a complete context about your project</a>. It should include:
    <ul>
      <li>Why the project exists</li>
      <li>Business logic, explaining the “why” not just the “how”</li>
      <li>How various subparts of the project interact with each other</li>
      <li>Where and how are important third party dependencies used</li>
      <li>Explanation of the data schema and model</li>
    </ul>
  </li>
  <li>Develop a testing strategy that forces you to understand the code:
    <ul>
      <li>Write tests before accepting AI-generated code</li>
      <li>Test edge cases explicitly</li>
      <li>Implement integration tests that verify system behavior, or at least do that manually</li>
    </ul>
  </li>
  <li>Even as a solo developer, I maintain a strict code review process for AI-generated code:
    <ul>
      <li>Review all generated code as if it came from a junior developer</li>
      <li>Verify security implications</li>
      <li>Ensure proper error handling</li>
    </ul>
  </li>
</ol>

<!-- newsletter_widget -->

<h2 id="looking-forward">Looking Forward</h2>

<p>We’re at a crucial junction in software engineering. The path of “vibe coding” might seem appealing, offering quick solutions and temporary productivity boosts.</p>

<p>But the real innovation, the sustainable progress, comes from maintaining deep understanding while embracing AI’s capabilities.</p>

<p>The next time you’re tempted to blindly accept AI-generated code, remember: true engineering excellence isn’t about velocity — it’s about building systems you can understand, maintain, and evolve.</p>

<p>Now <em>that’s</em> a vibe worth protecting.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="vibe-coding" />
        
      

      

      
      
        <summary type="html"><![CDATA[Last Tuesday at 1 AM, I was debugging a critical production issue in my AI dev tool. As I dug through layers of functions, I suddenly realized — unlike the new generation of developers, I was grateful that I could actually understand my codebase. That’s when I started thinking more about Karpathy’s recent statements on vibe coding. For those who missed it, Andrej Karpathy recently shared his thoughts on what he calls “vibe coding” — essentially surrendering code comprehension to AI tools and hoping for the best. His exact words? “I ‘Accept All’ always, I don’t read the diffs anymore.” I have learnt a lot from Karpathy and use AI tools daily, but there’s a world of difference between augmenting your capabilities and completely surrendering your understanding.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>250,000+</social:views>
          
          
            <social:reddit>550+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Security Checklist and Prompt For Vibe Coders</title>
      <link href="https://nmn.gl/blog/vibe-security-checklist" rel="alternate" type="text/html" title="Security Checklist and Prompt For Vibe Coders" />
      <published>2025-03-26T00:00:00+00:00</published>
      <updated>2025-03-26T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/vibe-security-checklist</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/vibe-security-checklist"><![CDATA[<p>Two days ago, my friend Owen messaged me in a panic. He had built an impressive SaaS app using Bolt, but realized that his OpenAI API key was completely exposed. He was fortunate to have caught it early, but what if this had actually went into production?</p>

<p><a href="/blog/ai-and-learning">Owen isn’t alone</a>. Unfortunately, AI coding assistants often generate functional but insecure code unless explicitly prompted about security concerns.</p>

<p>After walking Owen through securing his application, I realized these lessons could help others. So I compiled this comprehensive security checklist for vibe coders — the same advice that saved Owen’s project.</p>

<p>I also wrote up a “vibe security prompt” at the bottom of the article. Give that to your AI of choice and secure your application!</p>

<!--more-->

<h3 id="set-up-proper-logging">Set Up Proper Logging</h3>

<p><strong>What to do:</strong> Ensure that all your code is logged consistently, including error cases. You can do that with simple <code class="language-plaintext highlighter-rouge">console.log</code> statements, or use structured logging with tools like Winston or Pino.</p>

<p><strong>Why it matters:</strong> Good logging practices help identify security incidents and troubleshoot issues without exposing sensitive information.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Use a consistent format, so you know where each log is originating from</li>
  <li>Use different log levels (error, warning, info)</li>
  <li>Never log sensitive data like passwords or tokens</li>
</ul>

<h3 id="protect-environment-variables">Protect Environment Variables</h3>

<p><strong>What to do:</strong> Add <code class="language-plaintext highlighter-rouge">.env.local</code> and similar files to your <code class="language-plaintext highlighter-rouge">.gitignore</code> immediately and <a href="https://gigamind.dev/blog/vibe-code-security-app-prompts" target="_blank">manage sensitive information properly</a>.</p>

<p><strong>Why it matters:</strong> Exposed credentials are among the most common causes of security breaches. A single leaked API key can compromise your entire system or result in unexpected bills.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Add sensitive files to <code class="language-plaintext highlighter-rouge">.gitignore</code> before your first commit</li>
  <li>Set up environment variables manually in deployment platforms like Vercel/Netlify</li>
  <li>Never hardcode credentials in your application code</li>
  <li>If you accidentally expose secrets, rotate them immediately</li>
</ul>

<h3 id="validate-inputs-server-side">Validate Inputs Server-Side</h3>

<p><strong>What to do:</strong> What if a malicious attacker tries to send improper data to your app, trying to break it? Client-side checks can easily be bypassed. Make sure to always implement comprehensive server-side validation. You can use libraries like Zod.</p>

<p><strong>Why it matters:</strong> Client-side validation can be bypassed, making server-side validation essential for security.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Validate data structure, types, and format</li>
  <li>Implement validation before processing any user input</li>
  <li>Handle validation errors gracefully without exposing system details</li>
</ul>

<h3 id="use-established-authentication-providers">Use Established Authentication Providers</h3>

<p><strong>What to do:</strong> Leverage authentication services like NextAuth, Clerk, or Supabase instead of building authentication from scratch.</p>

<p><strong>Why it matters:</strong> Authentication is notoriously complex to implement securely. Small mistakes can lead to account takeovers and data breaches.</p>

<p>Just let someone else handle it. I usually go with Supabase Auth, but I’ve heard that Clerk is good as well.</p>

<h3 id="use-api-abstraction-layers-properly">Use API Abstraction Layers Properly</h3>

<p><strong>What to do:</strong> Instead of allowing direct database calls from client code, establish server functions that handle data access.</p>

<p><strong>Why it matters:</strong> Proper API abstraction prevents unauthorized data access and improves performance by centralizing business logic.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Client-side code should only call API endpoints, never database functions directly</li>
  <li>Implement proper authentication and authorization checks in your API layer</li>
  <li>Structure API functions to return only necessary data</li>
</ul>

<h3 id="implement-rate-limiting">Implement Rate Limiting</h3>

<p><strong>What to do:</strong> Add rate limiting to all public-facing endpoints, especially authentication routes.</p>

<p><strong>Why it matters:</strong> Without rate limiting, attackers can perform brute force attacks or overwhelm your server with requests.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Limit each user to a reasonable number of requests per minute/hour</li>
  <li>Set different limits for different endpoints based on sensitivity</li>
  <li>Limit by both IP and user ID to prevent bypass attempts</li>
</ul>

<h3 id="fix-all-linter-errors">Fix ALL Linter Errors</h3>

<p><strong>What to do:</strong> Resolve linter warnings and errors as they occur, don’t save them for later.</p>

<p><strong>Why it matters:</strong> Linters often flag potential security issues along with code quality problems.</p>

<p><strong>Implementation tips:</strong></p>

<ul>
  <li>Configure your IDE to show linter errors in real-time</li>
  <li>Group errors by type and fix systematically</li>
  <li>Consider using AI assistants to help fix linter errors efficiently</li>
</ul>

<h2 id="making-ai-generated-code-more-secure">Making AI-Generated Code More Secure</h2>

<p>The key to getting secure code from AI assistants is asking the right questions. Here are strategies I’ve found effective:</p>

<ul>
  <li>Request security reviews — After generating functional code, ask the AI to audit it for security vulnerabilities</li>
  <li>Break down complex requests — Generate basic functionality first, then separately focus on securing it</li>
  <li>Be explicit about security concerns — Specifically mention authentication, data validation, and error handling</li>
  <li>Prompt for best practices — Ask for modern security standards for your specific framework or language</li>
</ul>

<h2 id="vibe-security-prompt">Vibe Security Prompt</h2>

<p><strong>How to use:</strong> Copy and paste this prompt into Claude 3.7 Sonnet along with your github repository or a zip file containing all your code.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Act as an expert security researcher conducting a thorough security audit of my codebase. Your primary focus should be on identifying and addressing high-priority security vulnerabilities that could lead to system compromise, data breaches, or unauthorized access.

Follow this structured approach:
<span class="p">
1.</span> ANALYSIS PHASE:
<span class="p">   -</span> Review the entire codebase systematically
<span class="p">   -</span> Focus on critical areas: authentication, data handling, API endpoints, environment variables
<span class="p">   -</span> Document each security concern with specific file locations and line numbers
<span class="p">   -</span> Prioritize issues based on potential impact and exploitation risk
<span class="p">
2.</span> PLANNING PHASE:
<span class="p">   -</span> For each identified vulnerability:
<span class="p">     *</span> Explain the exact nature of the security risk
<span class="p">     *</span> Provide evidence of why it's a problem (e.g., potential attack vectors)
<span class="p">     *</span> Outline specific steps needed to remediate the issue
<span class="p">     *</span> Explain the security implications of the proposed changes
<span class="p">
3.</span> IMPLEMENTATION PHASE:
<span class="p">   -</span> Only proceed with code modifications after completing analysis and planning
<span class="p">   -</span> Make minimal necessary changes to address security issues
<span class="p">   -</span> Document each change with before/after comparisons
<span class="p">   -</span> Verify that changes don't introduce new vulnerabilities

Key Focus Areas:
<span class="p">-</span> Exposed credentials and environment variables
<span class="p">-</span> Insufficient input validation
<span class="p">-</span> Authentication/authorization bypasses
<span class="p">-</span> Insecure direct object references
<span class="p">-</span> Missing rate limiting
<span class="p">-</span> Inadequate error handling and logging
<span class="p">-</span> Unsafe data exposure

DO NOT:
<span class="p">-</span> Make cosmetic or performance-related changes
<span class="p">-</span> Modify code unrelated to security concerns
<span class="p">-</span> Proceed with changes without explaining the security implications
<span class="p">-</span> Skip the analysis and planning phases

After each modification, explain:
<span class="p">1.</span> What security vulnerability was addressed
<span class="p">2.</span> Why the original code was unsafe
<span class="p">3.</span> How the new code prevents the security issue
<span class="p">4.</span> What additional security measures should be considered
</code></pre></div></div>

<!-- newsletter_widget -->

<h2 id="final-thoughts">Final Thoughts</h2>

<p>The democratization of software development through AI tools is revolutionary, allowing more people to bring their ideas to life. But with this power comes responsibility. Security can’t be an afterthought — it must be integrated from the beginning.</p>

<p>The gap between “vibe coding” and production-ready applications isn’t insurmountable, but it requires deliberate attention to security fundamentals. As I continue developing my own <a href="https://gigamind.dev/">AI code enhancement tool</a>, I’m focused on solving these security challenges for the next generation of developers.</p>

<p>By implementing these practices, you’ll transform AI-generated code from a security liability into a solid foundation for your applications.</p>

<p>Your users deserve it, and your future self will thank you.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="vibe-coding" />
        
      

      

      
      
        <summary type="html"><![CDATA[Two days ago, my friend Owen messaged me in a panic. He had built an impressive SaaS app using Bolt, but realized that his OpenAI API key was completely exposed. He was fortunate to have caught it early, but what if this had actually went into production? Owen isn’t alone. Unfortunately, AI coding assistants often generate functional but insecure code unless explicitly prompted about security concerns. After walking Owen through securing his application, I realized these lessons could help others. So I compiled this comprehensive security checklist for vibe coders — the same advice that saved Owen’s project. I also wrote up a “vibe security prompt” at the bottom of the article. Give that to your AI of choice and secure your application!]]></summary>
      

      
      

      
        <social:metrics>
          
          
            <social:reddit>300+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Cursor workflows for building SaaS products in 2025</title>
      <link href="https://nmn.gl/blog/building-with-ai" rel="alternate" type="text/html" title="Cursor workflows for building SaaS products in 2025" />
      <published>2025-03-22T00:00:00+00:00</published>
      <updated>2025-03-22T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/building-with-ai</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/building-with-ai"><![CDATA[<p>As a software engineer experimenting with AI for the past 2 years, I’ve tested nearly every AI coding assistant on the market and developed a workflow that consistently delivers results.</p>

<p>Here’s my tried-and-tested method for solo developers looking to leverage AI to make SaaS products.</p>

<!--more-->

<p><em>Disclaimer: While my approach requires minimal manual coding, you’ll still need a basic understanding of concepts to be able to deliver quality results. Always work with a learning mindset, and don’t do things without understanding what’s going on.</em></p>

<h2 id="a-beautiful-ui">A Beautiful UI</h2>

<p>I begin with <a href="https://lovable.dev/">Lovable</a> to generate a beautiful user interface. This AI builder excels at creating clean, professional UIs and comes with a well-structured tech stack.</p>

<p>First, focus solely on building static screens — no databases or authentication yet.</p>

<p>Starting with a tool like Lovable rather than coding from scratch offers two significant advantages:</p>
<ul>
  <li>You can test and refine the UI before adding complexity</li>
  <li>The dependencies and stack configuration are professionally handled, eliminating the version conflicts</li>
</ul>

<p>Once satisfied with your interface, connect the project to GitHub and clone the repository to your local machine using <a href="https://github.com/apps/desktop">GitHub Desktop</a>.</p>

<h2 id="build-feature-by-feature">Build Feature by Feature</h2>

<p>Next, I open the repository in an AI coding assistant like <a href="https://www.cursor.com/">Cursor</a> (more affordable) or <a href="https://github.com/RooVetGit/Roo-Code">Roo Code</a> (higher quality, but more expensive and complex). After installing:</p>

<ol>
  <li>Provide a detailed explanation of my app’s purpose</li>
  <li>Have the AI generate comprehensive documentation in a /docs/ folder</li>
  <li>Build features one by one, always asking the AI to implement error handling and console logging</li>
</ol>

<p>For authentication and databases, I typically use Supabase and have the AI assist with integration, being careful not to expose API keys.</p>

<h2 id="debugging-strategically">Debugging Strategically</h2>

<p>Errors are inevitable when building with AI. I’ve found it’s best to expect a 50% error rate and develop debugging strategies accordingly:</p>

<p>For simple errors:</p>
<ul>
  <li>Test each feature individually</li>
  <li>Use console logs to identify issues</li>
  <li>Feed error messages back to the AI for fixes</li>
</ul>

<p>For complex errors:</p>
<ul>
  <li>Use <a href="https://repomix.com/">RepoMix</a> (I use the <a href="https://repomix.com/guide/installation#vscode-extension">Cursor extension</a>) to copy crucial parts of the codebase</li>
  <li>Paste the code into a more powerful reasoning model like OpenAI’s O1-Pro or DeepSeek R1</li>
  <li>Ask the model to create a detailed technical breakdown of the bug and specific actions to fix it</li>
  <li>Have your coding assistant implement these fixes</li>
</ul>

<p>This approach not only resolves errors but also helps you understand how your code works, improving your ability to manage the project over time.</p>

<h2 id="security-and-deployment">Security and Deployment</h2>

<p>Before deploying, use RepoMix and upload the entire repository to Claude to find security vulnerabilities, especially checking for accidentally exposed API keys or other sensitive information. This is arguably the most crucial step, and I will write a detailed post about this soon.</p>

<!-- newsletter_widget -->

<h2 id="tips-for-success">Tips for Success</h2>

<ul>
  <li>Progress incrementally with small, testable features</li>
  <li>Implement thorough console logging to aid debugging</li>
  <li>When stuck, escalate to more powerful models rather than struggling with the same tool</li>
  <li>Read and understand the fixes proposed by AI to build your own knowledge</li>
  <li>Document everything as you go, ensuring your AI assistants maintain context</li>
</ul>

<h2 id="looking-forward">Looking Forward</h2>

<p>The current generation of AI tools <a href="/blog/ai-illiterate-programmers">won’t replace professional software engineers</a> anytime soon, especially for enterprise software with complex business logic and compliance requirements. However, for developers with basic knowledge, these tools serve as powerful amplifiers that can dramatically accelerate development.</p>

<p>As someone building AI tools for code generation myself, I’m constantly amazed by how far this technology has come. Now is the perfect time to experiment with these capabilities and discover how they can enhance your development workflow.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[As a software engineer experimenting with AI for the past 2 years, I’ve tested nearly every AI coding assistant on the market and developed a workflow that consistently delivers results. Here’s my tried-and-tested method for solo developers looking to leverage AI to make SaaS products.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>50,000</social:views>
          
          
            <social:reddit>100+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">I cut my browser debugging time in half using AI &amp;amp; MCP</title>
      <link href="https://nmn.gl/blog/ai-browser-debugging" rel="alternate" type="text/html" title="I cut my browser debugging time in half using AI &amp;amp; MCP" />
      <published>2025-03-20T00:00:00+00:00</published>
      <updated>2025-03-20T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-browser-debugging</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-browser-debugging"><![CDATA[<p>In 12 years of my dev career, I’ve spent countless hours battling browser bugs.</p>

<p>Recently, I discovered an MCP that’s cut my debugging time in half.</p>

<p>MCP as a term is being overused too much, but just understand them as APIs that AI agents can use.</p>

<p>I found an MCP to let AI see and interact with your browser, called <a href="https://github.com/AgentDeskAI/browser-tools-mcp">BrowserTools</a>. Once you integrate it with Cursor, you can ask it see what’s going on in your browser and the console.</p>

<!--more-->

<h2 id="the-aha-moment">The “aha” moment”</h2>

<p>I recently faced issues in my payment flow, where the payment callback didn’t seem to be working.</p>

<p>I simply typed: “Check what’s happening when users click the pay button and fix any JavaScript errors.”</p>

<p>Rather than me needing to do it manually, the AI was able to recognize the console logs, correlate them to the appropriate lines of code, and then fix it. All by itself.</p>

<h2 id="how-it-works">How it works</h2>

<p>BrowserTools MCP consists of three components:</p>

<ol>
  <li>A Chrome extension that captures console logs, network activity, screenshots, and DOM elements</li>
  <li>A Node server that processes this information locally on your machine</li>
  <li>An MCP server that lets AI clients like Cursor interact with your browser data</li>
</ol>

<p>Everything stays on your local machine — no data gets sent to external services.</p>

<h2 id="ways-you-can-use-this">Ways you can use this</h2>

<h3 id="1-console-log-analysis">1. Console log analysis</h3>

<p>Instead of scanning through hundreds of console messages, I ask: “Summarize the console logs and identify recurring errors.”</p>

<p>Last week, the AI caught a memory leak causing performance issues that I would have missed. Fixed before deployment, saving both user frustration and future debugging time.</p>

<h3 id="2-network-traffic-debugging">2. Network traffic debugging</h3>

<p>Rather than clicking through dozens of network requests, I ask: “Which API calls are failing when users complete a payment?”</p>

<p>The AI immediately identified a mismatch in the authorization token, causing 401 errors. Pattern spotted and fixed in minutes.</p>

<h3 id="3-dom-analysis">3. DOM analysis</h3>

<p>When facing layout issues, I say: “Why aren’t these validation messages showing correctly?”</p>

<p>The AI examines DOM structure, CSS rules, and JavaScript interactions to find the exact problem. It once spotted I was appending error messages to elements without changing their visibility—fixing a UI animation bug.</p>

<h2 id="getting-started">Getting started</h2>

<p>There are detailed instructions in the <a href="https://browsertools.agentdesk.ai/installation#installation">BrowserTools MCP installation docs</a>, but a brief overview:</p>

<ol>
  <li>Install the Chrome extension using developer mode</li>
  <li>Set up the Node server locally using <code class="language-plaintext highlighter-rouge">npx @agentdeskai/browser-tools-mcp@1.2.0</code></li>
  <li>Configure the MCP server in your IDE. Here are instructions for <a href="https://docs.cursor.com/context/model-context-protocol">Cursor</a></li>
</ol>

<p>The 10-15 minute setup is worth every second. Start with simple prompts like:</p>

<p>“Show me all errors in the console”
“Check why this button click isn’t working”
“Analyze network requests to the <code class="language-plaintext highlighter-rouge">/api/users</code> endpoint”</p>

<!-- newsletter_widget -->

<h2 id="the-future">The future</h2>

<p>As a solo developer, browser debugging used to be one of my biggest time sinks. Now, I spend less time fixing bugs and more time building features that matter to users.</p>

<p>By embracing AI as a development partner and powering it up with MCP, I’ve been able to accomplish much more than I could alone.</p>

<p>Have you tried using AI for browser debugging? I’d love to hear your experiences on <a href="https://x.com/NamanyayG">Twitter</a>.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="cursor" />
        
          <category term="mcp" />
        
      

      

      
      
        <summary type="html"><![CDATA[In 12 years of my dev career, I’ve spent countless hours battling browser bugs. Recently, I discovered an MCP that’s cut my debugging time in half. MCP as a term is being overused too much, but just understand them as APIs that AI agents can use. I found an MCP to let AI see and interact with your browser, called BrowserTools. Once you integrate it with Cursor, you can ask it see what’s going on in your browser and the console.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>60,000</social:views>
          
          
            <social:reddit>200+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Vibe Coding is a Dangerous Fantasy</title>
      <link href="https://nmn.gl/blog/vibe-coding-fantasy" rel="alternate" type="text/html" title="Vibe Coding is a Dangerous Fantasy" />
      <published>2025-03-20T00:00:00+00:00</published>
      <updated>2025-03-20T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/vibe-coding-fantasy</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/vibe-coding-fantasy"><![CDATA[<p>Last week, X exploded when a “vibe coder” announced his SaaS was under attack.</p>

<p>His business, built entirely with AI assistance and “zero hand-written code,” was experiencing bypassed subscriptions, maxed-out API keys, and database corruption.</p>

<p>His follow-up admission made this notable: <em>“as you know, I’m not technical so this is taking me longer than usual to figure out.”</em></p>

<p>As someone deeply immersed in the AI code generation space, I’ve been watching this unfold with a mix of sympathy and frustration. Let me be clear — I’m not against AI-assisted development. My own tool aims to <a href="https://gigamind.dev/">improve code generation quality</a>. But there’s a growing and dangerous fantasy that technical knowledge is optional in the new AI-powered world.</p>

<p>After observing many similar (though less public) security disasters, I’ve come to a controversial conclusion: vibe coding isn’t just inefficient — it’s potentially catastrophic.</p>

<!--more-->

<figure>
<img src="/blog/assets/vibe-coding-security.webp" />
</figure>

<h2 id="the-lie-of-vibe-coding">The Lie of Vibe Coding</h2>

<p>Recently, I was working late on a particularly thorny code generation problem when a message from an old SF friend popped up: “Dude, have you seen this? I just launched my side project without writing a single line of code. Just vibe coding!”</p>

<p>He shared his screen with me — a surprisingly polished-looking SaaS product that helped small business with their career path. The UI was clean and the features worked. All built by telling Windsurf what he wanted, occasionally getting frustrated, refining his prompts, and <a href="/blog/ai-and-learning">never once understanding the underlying technology</a>.</p>

<p>“That’s great,” I said, genuinely impressed. “What security measures did you implement?”</p>

<p>His blank stare told me everything.</p>

<p>Some time later, his API keys were scraped from client-side code that AI had carelessly left exposed. He had to negotiate with OpenAI to forgive his bill.</p>

<p>Today, his “side project” is offline while he’s trying to learn authentication and security from first principles.</p>

<p>The vibe coder’s dream turns into a nightmare not when the code doesn’t work, <strong>but when it works just well enough to be dangerous.</strong></p>

<h2 id="the-invisible-complexity-gap">The (Invisible) Complexity Gap</h2>

<p>The problem isn’t that AI tools can’t generate secure code — they often can, with the right prompts. The problem is that without understanding what you’re building, you don’t know what you don’t know.</p>

<p>I witnessed this firsthand when helping another friend debug his AI-generated SaaS for teachers. Looking through the code, I discovered:</p>

<ul>
  <li>No rate limiting on login attempts</li>
  <li>Unsecured API keys</li>
  <li>Admin functions protected only by frontend routes</li>
  <li>DB manipulation from frontend</li>
</ul>

<p>When I pointed these out, <a href="/blog/ai-and-learning">he was genuinely confused</a>. “But it works fine! I’ve been testing it for weeks!”</p>

<p>This is what I’m calling the <strong>“invisible complexity gap”</strong> of vibe coding — the difference between “it works on my machine” and “it’s secure in production.” The gap exists because modern development tools, especially AI assistants, are extraordinarily good at hiding complexity and making things seem simpler than they are.</p>

<p>AI won’t warn you about the security holes you don’t know to ask about. <strong>The perfect circular trap:</strong> you can’t secure what you don’t understand, and you don’t understand what AI builds for you.</p>

<h2 id="when-vibe-coding-backfires">When Vibe Coding Backfires</h2>

<p>The reddit thread that went viral last week is filled with experienced developers smugly saying “I told you so” — but that’s not helpful. The reality is that we’re entering an era where more people want to build software, and AI makes that possible in ways it wasn’t before.</p>

<p>But the consequences of security failures are becoming more severe. When that vibe coder’s SaaS got compromised last week, real money was lost. All because someone believed that understanding your tools was optional.</p>

<p>I’m not saying everyone needs to be a security expert or low-level programmer. But there’s a minimum viable knowledge required to responsibly deploy software that handles other people’s data, and <a href="/blog/ai-illiterate-programmers">AI hasn’t eliminated that requirement</a> — it’s just obscured it.</p>

<!-- newsletter_widget -->

<h2 id="what-vibe-coders-can-actually-do">What Vibe Coders Can Actually Do</h2>

<p>If you’re currently building with AI assistance and recognize yourself in this post, don’t panic. Here’s what you can do right now:</p>

<ol>
  <li>
    <p>First, adopt a learning mindset. You don’t need to understand every line of code, but you should understand the architecture — how data flows through your system, where it’s stored, and how it’s protected.</p>
  </li>
  <li>
    <p>Second, use AI to educate, not just to implement. When Cursor or ChatGPT or Claude generates code for you, ask it to explain the security implications. Ask it what could go wrong. Ask it what you’re missing.</p>
  </li>
  <li>
    <p>Third, implement basic security practices from day one — proper authentication, HTTPS everywhere, environment variables for secrets, and regular backups at minimum.</p>
  </li>
  <li>
    <p>This one won’t be possible for many, but, consider bringing in expertise for a security review before launching anything that handles sensitive data. Even a few hours with someone who knows what to look for can save you from disaster. Feel free to email or shoot me a DM on Twitter if you really can’t find someone, if I have the time I’ll try to help.</p>
  </li>
</ol>

<h2 id="the-future-of-vibe-coding">The Future of Vibe Coding</h2>

<p>For every vibe coder reading this who feels defensive or attacked — I get it. You’re not wrong for wanting to build. AI has democratized creation in beautiful ways.</p>

<p>But democratizing creation means democratizing responsibility too.</p>

<p>The current wave of vibe coding tools optimize for immediate gratification: making something work now. The next generation needs to optimize for sustainable comprehension: making something work <strong>reliably over time</strong>.</p>

<p>The most revolutionary aspect of AI coding tools isn’t that they let you skip understanding, it’s that they compress years of learning into months. They don’t replace the journey — they accelerate it.</p>

<p>Let’s embrace that acceleration while rejecting the fantasy that we can outsource understanding entirely. The future belongs to those who use AI as a powerful tool, not a replacement for knowledge.</p>

<p>Not just vibes. Vibes <em>plus</em> knowledge.</p>

<p>That’s how we build things that last.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="/blog/ai-understand-senior-developer?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">The Day I taught AI to think like a Senior Developer</a></li>
  <li><a href="/blog/cursor-guide?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">My Cursor AI Workflow That Actually Works in Production</a></li>
  <li><a href="/blog/ai-dev-tips?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">Context, Structure, Organization: Framework for AI-powered Development</a></li>
  <li><a href="/blog/ai-prompt-engineering?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">AI Prompt Playbook (Prompts that Work in Production)</a></li>
  <li><a href="/blog/vibe-security-checklist?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">Security Checklist &amp; Prompts for Cursor Vibe Coders</a></li>
  <li><a href="/blog/building-with-ai?utm_source=blog&amp;utm_medium=vibe-coding-fantasy&amp;utm_campaign=vibe-coding-fantasy">Cursor Workflows to build SaaS products in 2025</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[Last week, X exploded when a “vibe coder” announced his SaaS was under attack. His business, built entirely with AI assistance and “zero hand-written code,” was experiencing bypassed subscriptions, maxed-out API keys, and database corruption. His follow-up admission made this notable: “as you know, I’m not technical so this is taking me longer than usual to figure out.” As someone deeply immersed in the AI code generation space, I’ve been watching this unfold with a mix of sympathy and frustration. Let me be clear — I’m not against AI-assisted development. My own tool aims to improve code generation quality. But there’s a growing and dangerous fantasy that technical knowledge is optional in the new AI-powered world. After observing many similar (though less public) security disasters, I’ve come to a controversial conclusion: vibe coding isn’t just inefficient — it’s potentially catastrophic.]]></summary>
      

      
      

      
        <social:metrics>
          
            <social:views>300,000</social:views>
          
          
            <social:reddit>600+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">My Cursor AI Workflow That Actually Works in Production</title>
      <link href="https://nmn.gl/blog/cursor-guide" rel="alternate" type="text/html" title="My Cursor AI Workflow That Actually Works in Production" />
      <published>2025-03-16T00:00:00+00:00</published>
      <updated>2025-03-16T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/cursor-guide</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/cursor-guide"><![CDATA[<p><em>Using AI for coding isn’t perfect, but it definitely makes me faster.</em></p>

<p>I’ve been coding with Cursor AI since it was launched now while building my SaaS. The internet seems split between “AI coding is a miracle” and “AI coding is garbage.” Honestly, it’s somewhere in between.</p>

<p>Some days Cursor helps me complete tasks in record times. Other days I waste hours fighting its suggestions.</p>

<p>After learning from my mistakes, I wanted to share some <strong>cursor workflows</strong> and best practices that actually work in production.</p>

<!--more-->

<h2 id="setting-up-a-cursorrules-file-that-actually-helps">Setting Up a <code class="language-plaintext highlighter-rouge">.cursorrules</code> File That Actually Helps</h2>

<p>The biggest improvement I made was creating a <code class="language-plaintext highlighter-rouge">.cursorrules</code> file. It’s basically a set of instructions that tells Cursor how to generate code for your specific project.</p>

<p>Mine core file is pretty simple — just about 10 lines covering the most common issues I’ve encountered. For example, Cursor kept giving comments rather than writing the actual code. One line in my rules file fixed it forever.</p>

<p>Here’s what the start of my file looks like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- Only modify code directly relevant to the specific request. Avoid changing unrelated functionality.
- Never replace code with placeholders like `// ... rest of the processing ...`. Always include complete code.
- Break problems into smaller steps. Think through each step separately before implementing.
- Always provide a complete PLAN with REASONING based on evidence from code and logs before making changes.
- Explain your OBSERVATIONS clearly, then provide REASONING to identify the exact issue. Add console logs when needed to gather more information.
</code></pre></div></div>

<p>Don’t overthink your rules file. Start small and add to it whenever you notice Cursor making the same mistake twice. You don’t need any long or complicated rules, Cursor is using state of the art models and already knows most of what there is to know.</p>

<p>I continue the rest of the “rules” file with a detailed technical overview of my project. I describe what the project is for, how it works, what important files are there, what are the core algorithms used, and any other details depending on the project. I used to do that manually, but now I just <a href="https://gigamind.dev/?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">use my own tool to generate it</a>. You can do it using Cursor itself, but I just find it faster to have it done for me with one click.</p>

<h2 id="giving-cursor-the-context-it-needs">Giving Cursor the Context It Needs</h2>

<p>My biggest “aha moment” came when I realized Cursor works way better when it can see similar code I’ve already written.</p>

<p>Now instead of just asking “Make a dropdown menu component,” I say “Make a dropdown menu component similar to the Select component in <code class="language-plaintext highlighter-rouge">@components/Select.tsx.</code>”</p>

<p>This tiny change made the quality of suggestions way better. The AI suddenly “gets” my coding style and project patterns. I don’t even have to tell it exactly what to reference — just pointing it to similar components helps a ton.</p>

<p>For larger projects, you need to start giving it more context. Ask it to create rules files inside <code class="language-plaintext highlighter-rouge">.cursor/rules</code> folder that explain the code from different angles like backend, frontend, etc. This is such a powerful technique that I created a <a href="https://gigamind.dev/?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">Cursor extension to do it automatically</a>.</p>

<!-- newsletter_widget -->

<h2 id="my-daily-cursor-workflow">My Daily Cursor Workflow</h2>

<p>In the morning when I’m sharp, I plan out complex features with <a href="https://nmn.gl/blog/ai-illiterate-programmers">minimal AI help</a>. This ensures critical code is solid.</p>

<p>I then work with the Agent mode to actually write them one by one, in order of most difficulty. I make sure to use the “Review” button to read all the code, and keep changes small and test them live to see if they actually work.</p>

<p>For tedious tasks like creating standard components or writing tests, I lean heavily on Cursor. Fortunately, such boring tasks in software development are now history.</p>

<p>For tasks more involved with security, payment, or auth; I make sure to test fully manually and also get Cursor to write automated unit tests, because those are places where I want full peace of mind.</p>

<p>When Cursor suggests something, I often ask “Can you explain why you did it this way?” This has caught numerous subtle issues before they entered my codebase.</p>

<h2 id="avoiding-the-mistakes-i-made">Avoiding the Mistakes I Made</h2>

<p>If you’re trying Cursor for the first time, here’s what I wish I’d known:</p>

<ul>
  <li>Be super cautious with AI suggestions for authentication, payment processing, or security features. I manually review these character by character.</li>
  <li>When debugging with Cursor, always ask it to explain its reasoning. I’ve had it <a href="https://gigamind.dev/blog/prompt-method-debugging-ai-code">confidently “fix” bugs</a> by introducing even worse ones.</li>
  <li>Keep your questions specific. “Fix this component” won’t work. “Update the onClick handler to prevent form submission” works much better.</li>
  <li>Take breaks from AI assistance. I often code without Cursor and came back with a better sense of when to use it.</li>
</ul>

<h2 id="moving-forward-with-ai-tools">Moving Forward with AI Tools</h2>

<p>Despite the frustrations, I’m still using Cursor daily. It’s like having a sometimes-helpful junior developer on your team who works really fast but needs supervision.</p>

<p>I’ve found that being specific, <a href="https://gigamind.dev/">providing context</a>, and always reviewing suggestions has transformed Cursor from a risky tool into a genuine productivity booster for my solo project.</p>

<p>The key for me has been setting boundaries. Cursor helps me write code faster, but I’m still the one responsible for making sure that code works correctly.</p>

<p>What about you? If you’re using Cursor or similar AI tools, I’d love to hear what’s working or not working in your workflow.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="/blog/ai-understand-senior-developer?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">The Day I taught AI to think like a Senior Developer</a></li>
  <li><a href="/blog/ai-dev-tips?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">Context, Structure, Organization: Framework for AI-powered Development</a></li>
  <li><a href="/blog/ai-prompt-engineering?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">AI Prompt Playbook (Prompts that Work in Production)</a></li>
  <li><a href="/blog/vibe-security-checklist?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">Security Checklist &amp; Prompts for Cursor Vibe Coders</a></li>
  <li><a href="/blog/building-with-ai?utm_source=blog&amp;utm_medium=cursor-guide&amp;utm_campaign=cursor-guide">Cursor Workflows to build SaaS products in 2025</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="cursor" />
        
      

      

      
      
        <summary type="html"><![CDATA[Using AI for coding isn’t perfect, but it definitely makes me faster. I’ve been coding with Cursor AI since it was launched now while building my SaaS. The internet seems split between “AI coding is a miracle” and “AI coding is garbage.” Honestly, it’s somewhere in between. Some days Cursor helps me complete tasks in record times. Other days I waste hours fighting its suggestions. After learning from my mistakes, I wanted to share some cursor workflows and best practices that actually work in production.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/blog/assets/cursor-banner.png" />
        <media:content medium="image" url="https://nmn.gl/blog/blog/assets/cursor-banner.png" />
      

      
        <social:metrics>
          
            <social:views>150,000</social:views>
          
          
            <social:reddit>480+ votes</social:reddit>
          
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">Endgame of a post-AGI world</title>
      <link href="https://nmn.gl/blog/agi-gods" rel="alternate" type="text/html" title="Endgame of a post-AGI world" />
      <published>2025-03-12T00:00:00+00:00</published>
      <updated>2025-03-12T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/agi-gods</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/agi-gods"><![CDATA[<p><em>I’m not sure if I’m ready for what’s coming next with AI. Things are moving too fast.</em></p>

<p>The recent releases of Claude 3.7 and GPT-4.5 were… surprising. I read articles where AI researchers admitted these systems solved problems faster than they could.</p>

<p>We’re racing toward a world where humans might not be the smartest ones anymore. And we’re doing it willingly, even eagerly.</p>

<p>A year ago, I laughed off AGI fears. Then, I watched these new models solve in seconds what took me hours. I suddenly felt… <a href="https://nmn.gl/blog/ai-illiterate-programmers"><em>outdated</em></a>.</p>

<!--more-->

<h2 id="moving-too-fast">Moving Too Fast</h2>

<p>The acceleration trajectory is terrifying. Last year, we had text generators. Now we have systems designing novel protein structures, generating cinematic videos, and solving mathematical conjectures that stumped human mathematicians for decades.</p>

<p>This path frightens me. We can still understand what today’s AI creates, but that won’t last.</p>

<p>Have you seen this meme? It depicts the “Shoggoth” from H.P. Lovecraft’s Cthulhu Mythos, a monstrous, tentacled creature that is beyond our understanding. It’s being used to <a href="https://www.nytimes.com/2023/05/30/technology/shoggoth-meme-ai.html">describe AGI</a>.</p>

<p><img src="/blog/assets/shoggoth.jpg" style="max-width: 100%; width: 20em; margin: 0 auto; display: block;" /></p>

<p>AGI will be like that, an intelligence that we cannot comprehend, even though it might smile at us as we ‘align’ it us. We’ll face an intelligence that makes all humans combined look simple. <strong>We’ll be like ants watching humans build a city.</strong></p>

<h2 id="what-happens-when-were-no-longer-the-smartest">What Happens When We’re No Longer The Smartest</h2>

<p>The debates around AGI typically fall into two simplistic camps: <em>extinction</em> or <em>utopia</em>.</p>

<p>I believe the reality will be a bit more subtle: AGI won’t hate or love us, it will just make us <strong>optional</strong>.</p>

<p>At a recent SF AI conference, after several drinks, the conversation turned philosophical. I asked the room: <em>“How do you treat ants when building a house?”</em></p>

<p>We don’t hate ants. We don’t wish them harm. But, we also don’t ask for their opinions. <strong>They simply don’t matter to our decisions.</strong></p>

<p>That’s going to be our future with AGI.</p>

<!-- newsletter_widget -->

<h2 id="hollywood-gets-it-wrong-as-usual">Hollywood Gets it Wrong (As Usual)</h2>

<p>Pop culture show us dramatic confrontations — robot uprisings, AI-human battles, and brave human fighters finding the <em>off switch</em> at the very last moment.</p>

<p>The real danger will be far more subtle and inescapable. A super-intelligent AI wouldn’t be centralized, nor it will need physical domination.</p>

<p>It will seamlessly influence markets, control information, and redirect resources, as and when it pleases. It will understand human psychology so completely that manipulating us will be trivial. It will lie freely and will be able to make us believe anything. And for it, all of this will be like child’s play.</p>

<p>It will sneak up on us. We wouldn’t even notice the transition from being in control to being managed. We’d still feel like we’re making our own decisions when we’re actually being gently herded.</p>

<h2 id="can-we-do-anything">Can We Do Anything?</h2>

<p>Let’s be brutally honest — superintelligent AGI is inevitable. The competitive and economic advantages are too great for any nation to voluntarily fall behind. If the U.S. slows down, China won’t. If China slows down, some startup in Estonia won’t.</p>

<p>So what realistic options remain?</p>

<p>First, we need massive societal restructuring. Our entire civilization is built around human intelligence having economic value. That foundation is crumbling. We need to start reimagining society not based on what humans can produce, but on what we uniquely are.</p>

<p>Second, acceptance. I’ve started reading ancient Stoics alongside modern philosophers. They asked the same question we face: how to find meaning when confronted with forces beyond our control. I’ve found strange comfort in accepting that humans may not always be the most intelligent beings on Earth.</p>

<p>Third, augmentation may be our bridge to relevance. These days I use AI so much that it feels like an extension of my thoughts rather than a separate entity. And this is just the beginning, the line between human and machine will continue to blur.</p>

<h2 id="looking-forward">Looking Forward</h2>

<p>Some nights, coding in my apartment, I look at these AI models and feel obsolete. I feel like a Neanderthal witnessing the first Homo sapiens.</p>

<p>Other days, I remind myself that being intelligent isn’t everything that matters about being human. Our capacity for connection, for play, for finding beauty in imperfection — that is more valuable than raw compute, right?</p>

<p>I don’t know what’s next. But, what I know is that we’re rushing into the biggest change in human history with almost no planning.</p>

<p>The question isn’t if AGI is coming — it’s whether we’ll be able to shape it in ways that preserve humans relevance and dignity.</p>

<p>And lately, I try to spend more time away from screens, focusing on what makes us uniquely human.</p>

<p>Because when AGI arrives, our humanity might be all we have left.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[I’m not sure if I’m ready for what’s coming next with AI. Things are moving too fast. The recent releases of Claude 3.7 and GPT-4.5 were… surprising. I read articles where AI researchers admitted these systems solved problems faster than they could. We’re racing toward a world where humans might not be the smartest ones anymore. And we’re doing it willingly, even eagerly. A year ago, I laughed off AGI fears. Then, I watched these new models solve in seconds what took me hours. I suddenly felt… outdated.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">New Junior Developers Can’t Actually Code</title>
      <link href="https://nmn.gl/blog/ai-and-learning" rel="alternate" type="text/html" title="New Junior Developers Can’t Actually Code" />
      <published>2025-02-14T00:00:00+00:00</published>
      <updated>2025-02-14T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-and-learning</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-and-learning"><![CDATA[<p><em>Something’s been bugging me about how new devs learn and I need to talk about it.</em></p>

<p>We’re at this weird inflection point in software development. Every junior dev I talk to has Copilot or Claude or GPT running 24/7. They’re shipping code faster than ever. But when I dig deeper into their understanding of what they’re shipping? That’s where things get concerning.</p>

<p>Sure, the code works, but ask why it works that way instead of another way? Crickets. Ask about edge cases? <a href="/blog/ai-illiterate-programmers">Blank stares</a>.</p>

<p>The foundational knowledge that used to come from struggling through problems is just… missing.</p>

<p>We’re trading <a href="/blog/ai-understand-senior-developer">deep understanding</a> for quick fixes, and while it feels great in the moment, we’re going to pay for this later.</p>

<!--more-->

<h2 id="back-when-we-had-to-actually-think">Back when we had to actually think</h2>

<p>I recently realized that there’s a whole generation of new programmers who don’t even know what StackOverflow is.</p>

<p>Back when “Claude” was not a chatbot but the man who invented the field of information entropy, there was a different way to debug programming problems.</p>

<p>First, search on Google. Then, hope some desperate soul had posed a similar question as you had. If they did, you’d find a detailed, thoughtful, (and often patronizing) answer from a wise greybeard on this site called <em>“Stack Overflow”</em>.</p>

<p>Here’s <a href="https://stackoverflow.com/questions/12227594/what-is-the-difference-between-unary-plus-numberx-and-parsefloatx" target="_blank">one of the questions I posed 12 years ago</a>. See the top answer by Nathan Wall (who has earned a whopping 10,000+ points and is apparently a <a href="https://www.stacksource.com/" target="_blank">CTO</a> since 2015):</p>

<figure>
<img src="assets/so-qa-12-years.jpg" alt="Stack Overflow question and answer" style="width: 700px;" />
<figcaption>
Do yourself a favor and <a href="https://stackoverflow.com/a/13676265/1518029" target="_blank">go read it</a>, you might learn something new.
</figcaption>
</figure>

<p>Can you imagine that some dude just wrote an answer with this level of detail? Raw, without any AI? And for free?</p>

<p>My original question was thoroughly answered of course, but he didn’t stop there. I learnt so many new facts. Some of them I didn’t even know existed, and others I didn’t even want to know but now they’re etched in my brain forever.</p>

<p>This was the best case scenario if you had a question. If you stumbled upon a particularly difficult problem and didn’t find someone who had answered your question already, then tough luck.</p>

<figure>
<img src="https://imgs.xkcd.com/comics/wisdom_of_the_ancients.png" alt="XKCD 979" />
<figcaption>
<a href="https://xkcd.com/979/" target="_blank">"Wisdom of the Ancients", XKCD 979</a>
</figcaption>
</figure>

<p>Junior devs these days have it easy. They just go to <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">chat.com</a> and copy-paste whatever errors they see. Even lazier ones don’t do the 30 second effort of toggling to a browser window, they just use a tool that does it all in <a href="https://cursor.com" target="_blank">one place</a>.</p>

<p>It’s convenient and quick.</p>

<p>But, there’s still one reason that StackOverflow was superior:</p>

<p><em>Reading discussions by experienced developers about your topic is the <strong>best</strong> way to learn.</em></p>

<p>Here’s a graph to explain what I mean:</p>

<figure>
<img src="assets/speed-vs-knowledge.jpg" alt="Speed vs knowledge" style="width: 500px;" />
</figure>

<p>AI gives you answers, but the knowledge you gain is shallow. With StackOverflow, you had to read multiple expert discussions to get the full picture. It was slower, but you came out understanding not just what worked, but why it worked.</p>

<p>Think about every great developer you know. Did they get that good by copying solutions? No—they got there by <a href="/blog/ai-understand-senior-developer">understanding systems deeply and understanding other developers’ thought processes</a>. That’s exactly what we’re losing.</p>

<p>I’m not trying to be that guy who complains about “kids these days.” I use AI tools daily. I’m literally <a href="https://gigamind.dev/context">building one</a>. But we need to be honest about what we’re trading away for this convenience.</p>

<h2 id="what-can-we-do">What can we do?</h2>

<p>I’ve been experimenting with ways to fix this (because let’s face it, AI isn’t going anywhere). Here’s what’s actually working:</p>

<ul>
  <li>First, use AI with a learning mindset. When it gives you an answer, interrogate it. Ask it why. Sure, it takes longer, but that’s literally the point.</li>
  <li>Next, find your tribe. Reddit, Discord, Mastodon—wherever the smart people hang out. That’s where you’ll find the real discussions happening. The ones that make you go “huh, I never thought about it that way.”</li>
  <li>Do code reviews differently. Instead of just checking if the code works, start a conversation with your team. What other approaches did they consider? Why did they pick this one? Make understanding the process as important as the end result.</li>
  <li>Build things from scratch sometimes. Yes, AI can generate that authentication system for you. But try building one yourself first. You’ll write worse code, but you’ll understand every line of it. That knowledge compounds.</li>
</ul>

<!-- newsletter_widget -->

<h2 id="looking-forward">Looking forward</h2>

<p>Here’s the reality: The acceleration has begun and there’s nothing we can do about it. Open source models are taking over, and we’ll have AGI running in our pockets before we know it. But that doesn’t mean we have to let it make us worse developers.</p>

<p>The future isn’t about whether we use AI—it’s about how we use it. And maybe, just maybe, we can find a way to combine the speed of AI with the depth of understanding that we need to learn.</p>

<p>Let me know if you’ve found other ways to balance this. Or tell me I’m just being an old man yelling at clouds. Either way, let’s figure this out together.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      
        <category term="featured" scheme="http://namanyayg.com/post-types" />
      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[Something’s been bugging me about how new devs learn and I need to talk about it. We’re at this weird inflection point in software development. Every junior dev I talk to has Copilot or Claude or GPT running 24/7. They’re shipping code faster than ever. But when I dig deeper into their understanding of what they’re shipping? That’s where things get concerning. Sure, the code works, but ask why it works that way instead of another way? Crickets. Ask about edge cases? Blank stares. The foundational knowledge that used to come from struggling through problems is just… missing. We’re trading deep understanding for quick fixes, and while it feels great in the moment, we’re going to pay for this later.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/ai-and-learning.jpg" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/ai-and-learning.jpg" />
      

      
        <social:metrics>
          
            <social:views>1,000,000</social:views>
          
          
            <social:reddit>2,500+ votes</social:reddit>
          
          
            <social:hackernews>100+ comments</social:hackernews>
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">AI is Creating a Generation of Illiterate Programmers</title>
      <link href="https://nmn.gl/blog/ai-illiterate-programmers" rel="alternate" type="text/html" title="AI is Creating a Generation of Illiterate Programmers" />
      <published>2025-01-24T00:00:00+00:00</published>
      <updated>2025-01-24T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-illiterate-programmers</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-illiterate-programmers"><![CDATA[<p>A couple of days ago, Cursor went down during the ChatGPT outage.</p>

<p>I stared at my terminal facing those red error messages that I hate to see. An AWS error glared back at me. I didn’t want to figure it out without AI’s help.</p>

<p>After 12 years of coding, I’d <a href="/blog/ai-understand-senior-developer">somehow become worse at my own craft</a>. And this isn’t hyperbole—this is the new reality for software developers.</p>

<!--more-->

<h2 id="the-decay">The Decay</h2>

<p>It crept up on me <em>subtly</em>.</p>

<p>First, I stopped reading documentation. Why bother when AI could explain things instantly?</p>

<p>Then, my debugging skills took the hit. Stack traces now feel unapproachable without AI. I don’t even read error messages anymore, I just copy and paste them.</p>

<p>I’ve become a human clipboard, a mere intermediary between my code and an LLM.</p>

<p>Previously, every error message used to teach me something. Now? The solution appears magically, and I learn nothing. The dopamine hit of instant answers has replaced the satisfaction of genuine understanding.</p>

<p>Deep comprehension is the next thing that was affected. Remember spending hours understanding why a solution works? Now, I simply implement AI suggestions. If they don’t work, I <a href="/blog/ai-understand-senior-developer">improve the context</a>, and just ask the AI again. It’s a cycle of increasing dependency.</p>

<p>Then come the emotional changes. Previously, it was a part of the <em>joy</em> of programming to solve new problems. Now, I get frustrated if AI doesn’t give me a solution in 5 minutes.</p>

<p>The scariest part? I’m building an <a href="/blog/ai-understand-senior-developer">AI-powered development tool</a>, but I can’t shake the feeling I’m contributing to the very problem that’s eroding our collective skills.</p>

<h2 id="the-rehab-plan">The Rehab Plan</h2>

<p>I’m not suggesting anything radical like going AI-free completely. Instead, I’m starting with “No-AI Days.” One day a week where:</p>

<ul>
  <li>Read every error message completely</li>
  <li>Use actual debuggers again</li>
  <li>Write code from scratch</li>
  <li>Read source code instead of asking AI</li>
</ul>

<p>I won’t lie, it sucks. <a href="/blog/ai-and-learning">I feel slower, dumber, and more frustrated.</a></p>

<p>But I can also see the difference. I feel a stronger connection with my code and a sense of ownership, which had slowly disappeared with AI. Plus, I learn a lot more.</p>

<h2 id="the-uncomfortable-truth">The (Uncomfortable) Truth</h2>

<p>We’re not becoming 10x developers with AI.</p>

<p>We’re becoming 10x <strong>dependent</strong> on AI. <em>There’s a difference.</em></p>

<p>Every time we let AI solve a problem we could’ve solved ourselves, we’re <a href="/blog/ai-and-learning">trading long-term understanding for short-term productivity</a>. We’re optimizing for today’s commit at the cost of tomorrow’s ability.</p>

<!-- newsletter_widget -->

<p>I’m not suggesting we abandon AI tools—that ship has sailed. But we need rules of engagement. Here’s some ideas that I have:</p>

<ul>
  <li>No AI for problems that you haven’t tried to understand first</li>
  <li>Read and understand all AI-suggested solutions</li>
  <li>Regular periods of coding without AI assistance</li>
  <li>Focus on learning patterns, not just fixing immediate issues</li>
</ul>

<p>I won’t lie, I don’t think I’ll be able to follow these rules all the time. But it’s a start, and I strongly believe anyone who’s new to programming should <strong>definitely</strong> follow all of these rules.</p>

<p>Right now, somewhere, <a href="/blog/ai-and-learning">a new programmer is learning to code</a>. They’ll never know the satisfaction of solving problems truly on their own. They’ll never experience the deep understanding that comes from wrestling with a bug for hours.</p>

<p>We’re creating a generation of developers who can ask AI the right questions but can’t understand the answers. Every time AI goes down, they’re exposed as increasingly helpless. As of now, <a href="/blog/ai-understand-senior-developer">AI isn’t capable enough</a> to replace programmers fully, but this will only get worse as it improves. The real question isn’t whether AI will replace programmers. It’s whether we’re replacing ourselves.</p>

<p>Try coding without AI for just one day. The results might surprise you.</p>

<h3 id="update-01-feb">Update (01 Feb)</h3>

<p>This post resonated a lot with the developer community! I feel overjoyed and humbled.</p>

<p>Here’s some interesting statistics:</p>

<ul>
  <li>Over 1,250,000 views in one week</li>
  <li>2,500 upvotes on Reddit /r/programming, 1,000 on /r/webdev</li>
  <li>100 comments on Hacker News</li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
      

      

      
      
        <summary type="html"><![CDATA[A couple of days ago, Cursor went down during the ChatGPT outage. I stared at my terminal facing those red error messages that I hate to see. An AWS error glared back at me. I didn’t want to figure it out without AI’s help. After 12 years of coding, I’d somehow become worse at my own craft. And this isn’t hyperbole—this is the new reality for software developers.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/20250124-ai-illiterate-meta.jpeg" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/20250124-ai-illiterate-meta.jpeg" />
      

      
        <social:metrics>
          
            <social:views>1,250,000</social:views>
          
          
            <social:reddit>3,500+ votes</social:reddit>
          
          
            <social:hackernews>100+ comments</social:hackernews>
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">I built an AI Trend Analyzer to stop myself from doomscrolling</title>
      <link href="https://nmn.gl/blog/project-trends-ai" rel="alternate" type="text/html" title="I built an AI Trend Analyzer to stop myself from doomscrolling" />
      <published>2025-01-14T00:00:00+00:00</published>
      <updated>2025-01-14T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/project-trends-ai</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/project-trends-ai"><![CDATA[<p>As a content creator in the tech space, I found myself caught in an all-too-familiar trap: endless hours of doomscrolling through social media and news aggregators, trying to stay on top of the latest trends.</p>

<p>The signal-to-noise ratio was abysmal—for every meaningful tech development, I had to wade through countless memes, heated arguments, and clickbait. I knew there had to be a better way.</p>

<!--more-->

<h2 id="the-birth-of-an-idea">The Birth of an Idea</h2>

<p>That’s when I decided to build my own solution: an AI-powered trend analyzer that could do the heavy lifting for me.</p>

<p>The goal was simple: automatically extract meaningful tech discussions, analyze them for trends, and generate content ideas—all without getting lost in the noise.</p>

<h2 id="how-it-works">How It Works</h2>

<p>First, we need a source of information to understand trends.</p>

<p>And the obvious choice is right in front of us—Reddit.</p>

<p>I used an LLM to pick news-worthy subreddits from the list of top 250 subreddits, to allow anyone to find trends from a niche of their choice. I also asked the LLM to choose an emoji and color scheme for each subreddit. The result is something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>technology: {
  url: "https://redlib.catsarch.com/r/technology/top?t=day",
  name: "Technology",
  emoji: "💻",
  color: {
    gradient: "from-blue-500 to-indigo-600",
    accentLight: "bg-blue-50 text-blue-600",
    accentDark: "bg-blue-500 text-white"
  },
  description: "General technology news and discussions",
  category: "technology"
},
programming: {
  url: "https://redlib.catsarch.com/r/programming/top?t=day",
  name: "Programming",
  emoji: "👨‍💻",
  color: {
    gradient: "from-purple-500 to-indigo-600",
    accentLight: "bg-purple-50 text-purple-600",
    accentDark: "bg-purple-500 text-white"
  },
  description: "Software development and programming",
  category: "technology"
},
</code></pre></div></div>

<p>To find the most recent trends, I chose the “top of the day” links from each subreddit. You’ll note that I’m using an alternative Reddit API, this is to get a cleaner HTML that can be scraped easily.</p>

<p>Then, the system works in two main stages:</p>

<h3 id="1-data-collection-with-firecrawl-extract">1. Data collection with Firecrawl Extract</h3>

<p>The foundation of this system is built on Firecrawl’s powerful “extract” endpoint—an AI powered feature that goes beyond simple web scraping.</p>

<p>Instead of just pulling raw HTML and requiring complex parsing logic, the extract endpoint allows you to define a schema and get structured data back directly:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">schema</span> <span class="o">=</span> <span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
  <span class="na">links</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">array</span><span class="p">(</span><span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
    <span class="na">linkId</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">(),</span>
    <span class="na">title</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">(),</span>
    <span class="na">number_of_upvotes</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">number</span><span class="p">(),</span>
    <span class="na">number_of_comments</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">number</span><span class="p">(),</span>
    <span class="na">website_domain</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">(),</span>
    <span class="na">topics</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">array</span><span class="p">(</span><span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()),</span>
    <span class="na">companies_mentioned</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">array</span><span class="p">(</span><span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()),</span>
    <span class="na">linkHref</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()</span>
  <span class="p">}))</span>
<span class="p">});</span>
</code></pre></div></div>

<p>What makes this particularly powerful is that Firecrawl uses AI to understand and extract exactly what you need. You just define the shape of the data you want, and it handles all the complexity of finding and structuring that information.</p>

<h3 id="2-trend-analysis-with-claude">2. Trend analysis with Claude</h3>

<p>Next, Claude 3.5 Sonnet analyzes the collected data.</p>

<p>I spent considerable time crafting a prompt that generates structured, actionable insights. For each trend, it provides:</p>

<ul>
  <li>A viral score based on engagement metrics</li>
  <li>A clear trend title and overview</li>
  <li>Source links for reference</li>
  <li>Three detailed content suggestions with viral hooks and narrative angles</li>
</ul>

<p>What makes this particularly powerful is how Claude contextualizes the information. It doesn’t just list facts – it understands the broader implications and suggests multiple angles for content creation.</p>

<h2 id="future-enhancements">Future Enhancements</h2>

<p>I’m already using it to find and analyze trends. I usually don’t bother with news and I don’t find most of it interesting, but this works for me to summarize discussions and find new topics. Till now, it has helped me <a href="https://nmn.gl/blog/ai-midlevel-engineer">ideate my last blog post</a>, and we’ll see how much it helps me in the future.</p>

<p>On a technical level, I’m excited about several potential improvements:</p>

<ul>
  <li>Adding more data sources beyond the current set</li>
  <li>Creating an API for other creators to build upon</li>
  <li>Adding customizable topic filters</li>
  <li>Generating automatic tweet threads and article outlines</li>
</ul>

<p>It’s all open source so I’ll be more than happy if there’s any contributions!</p>

<h2 id="try-it-yourself">Try It Yourself</h2>

<p>GitHub Repository: <a href="https://github.com/namanyayg/trends-ai">namanyayg/trends-ai</a></p>

<p>Live Demo: <a href="https://trends.nmn.gl">trends.nmn.gl</a></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
          <category term="side-project" />
        
      

      

      
      
        <summary type="html"><![CDATA[As a content creator in the tech space, I found myself caught in an all-too-familiar trap: endless hours of doomscrolling through social media and news aggregators, trying to stay on top of the latest trends. The signal-to-noise ratio was abysmal—for every meaningful tech development, I had to wade through countless memes, heated arguments, and clickbait. I knew there had to be a better way.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Is AI ready to be a mid-level engineer?</title>
      <link href="https://nmn.gl/blog/ai-midlevel-engineer" rel="alternate" type="text/html" title="Is AI ready to be a mid-level engineer?" />
      <published>2025-01-13T00:00:00+00:00</published>
      <updated>2025-01-13T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-midlevel-engineer</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-midlevel-engineer"><![CDATA[<p>Mark Zuckerberg recently claimed AI will replace mid-level engineers by 2025.</p>

<p>As someone building AI developer tools and studying their real-world implementation, I believe this fundamentally misunderstands both the current state of AI and the role of mid-level engineers.</p>

<p>Here’s what Meta is missing.</p>

<!--more-->

<h2 id="the-current-reality">The Current Reality</h2>

<p>Current AI coding assistants excel at specific tasks: generating boilerplate, explaining code, and helping with routine programming tasks.</p>

<p>But there’s a crucial gap between writing isolated functions and understanding complex systems. In our testing, even the most advanced AI tools consistently fail when dealing with interconnected systems. They’ll generate syntactically perfect code that introduces subtle runtime errors by missing critical system dependencies.</p>

<p>Look, AI coding tools are already impressive. Companies like Lovable and Bolt (both $4M+ ARR) are showing real value. But they’re thriving in a completely different space: helping indie developers build from scratch, assisting with smaller projects, turning non-developers into casual coders.</p>

<p>That’s valuable! But it’s not exactly the same as maintaining Meta’s massive codebase.</p>

<h2 id="the-problems">The Problems</h2>

<p>In building our own AI developer tools (and surveying 87 developers about their experiences), I’ve identified three fundamental challenges that separate AI from mid-level engineers:</p>

<ol>
  <li>
    <p><strong>Context:</strong>
Mid-level engineers maintain a comprehensive mental model of the system architecture. They understand service boundaries, data flows, and system dependencies.</p>

    <p>Current AI tools are unable to keep large codebases in context, making it impossible to reason about system-wide impacts.</p>
  </li>
  <li>
    <p><strong>Memory:</strong>
Engineers retain crucial historical context about the codebase.</p>

    <p>They understand why certain architectural decisions were made, which optimization attempts failed, and what edge cases led to current implementations.</p>

    <p>AI tools lack this persistent understanding, treating each interaction as a new problem.</p>
  </li>
  <li>
    <p><strong>Systems Thinking:</strong>
Engineers excel at understanding cross-system interactions. They can identify potential performance bottlenecks, predict cascading effects of changes, and spot hidden dependencies.</p>

    <p>Current AI tools lack this capability for higher-level system analysis.</p>
  </li>
</ol>

<h2 id="the-future">The Future</h2>

<p>The path to truly effective AI development tools requires solving several fundamental technical challenges:</p>

<h3 id="advanced-code-understanding">Advanced code understanding</h3>

<p>We need AI systems that can maintain persistent models of entire codebases.</p>

<p>This includes understanding architectural patterns, coding conventions, and system dependencies.</p>

<p>Current research in program analysis and semantic code understanding shows promise, but we’re still far from production-ready solutions, because of the context problem.</p>

<h3 id="temporal-codebase-analysis">Temporal codebase analysis</h3>

<p>Future systems will need to understand code evolution over time.</p>

<p>This means tracking architectural decisions, failed approaches, and the reasoning behind current implementations through git history, PRs, and documentation.</p>

<p>This requires advances in both retrieval systems and causal reasoning about code changes.</p>

<h3 id="multi-agent-development">Multi-agent development</h3>

<p>The complexity of software development suggests a multi-agent approach. Here’s what this could look like:</p>

<ul>
  <li>A planning agent that breaks down high-level requirements into specific technical tasks. This requires knowledge of system architecture, technical constraints, and development patterns. Our experiments show this agent needs to maintain a graph of system dependencies and understand technical debt implications.</li>
  <li>Implementation agents specialized by domain (frontend, backend, infrastructure) that understand specific ecosystem best practices. These need to coordinate on interface contracts and maintain consistency across boundaries.</li>
  <li>A testing agent that combines property-based testing approaches with historical bug patterns to generate comprehensive test suites. This agent would need access to production monitoring data to prioritize high-risk scenarios.</li>
  <li>A review agent that checks for architectural consistency, performance implications, and security considerations. This requires understanding of both local code changes and system-wide impacts.</li>
</ul>

<p>Could we build this by 2025?</p>

<p>Maybe.</p>

<p>The building blocks are there: large language models, graph databases for code understanding, advanced retrieval systems.</p>

<p>But we’re not just teaching AI to write code—we’re teaching it to think about code the way experienced developers do.</p>

<h2 id="the-evolution-of-software-engineering">The Evolution of Software Engineering</h2>

<p>The real transformation won’t be replacement, but evolution. We’re seeing the emergence of two distinct specializations:</p>

<p><strong>Traditional Software Engineers</strong> will increasingly focus on high-level system design, architectural decisions, and complex business logic.</p>

<p>Rather than being replaced, they’ll be elevated.</p>

<p>The mundane parts of their job - boilerplate code, routine debugging, basic feature implementation - will be handled by AI. This lets them focus on what humans do best: understanding business context, making architectural decisions, and solving novel problems.</p>

<p><strong>AI Engineering Specialists</strong> will emerge as a new role, focusing on building and maintaining AI-enhanced development environments. This involves:</p>

<ul>
  <li>Designing prompt architectures for different development scenarios</li>
  <li>Building and maintaining custom training sets from company codebases</li>
  <li>Developing tools for AI/human collaboration in development workflows</li>
  <li>Optimizing AI system performance and resource usage</li>
</ul>

<p>Both roles will require strong software engineering fundamentals, but with different specializations. Traditional engineers will need to understand AI capabilities and limitations to effectively collaborate with AI tools. AI specialists will need deep understanding of development workflows to build effective AI systems.</p>

<h2 id="a-new-reality">A New Reality</h2>

<p>The future I see isn’t about AI replacing developers—it’s about fundamentally transforming how we build software. Imagine:</p>

<ul>
  <li>Engineers spending 80% of their time on creative problem-solving instead of routine implementation</li>
  <li>Development velocity increasing by an order of magnitude while maintaining quality</li>
  <li>Systems that catch subtle bugs before they reach production by understanding entire codebases</li>
  <li>Teams focusing on innovation while AI handles the implementation details</li>
</ul>

<p>Developers who successfully integrated AI tools report spending significantly more time on system design and creative problem-solving. They’re not being replaced—they’re being augmented in ways that make their work more impactful.</p>

<p>The key isn’t to resist this change, but to shape it. We have the opportunity to create development environments that combine human creativity and insight with AI’s speed and precision.</p>

<p><em>Want to be part of building this future? I’m working on these exact problems at the intersection of AI and development tooling. Reach out at <a href="mailto:hi@nmn.gl">hi@nmn.gl</a> - let’s make software development more powerful, more creative, and more human.</em></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
      

      

      
      
        <summary type="html"><![CDATA[Mark Zuckerberg recently claimed AI will replace mid-level engineers by 2025. As someone building AI developer tools and studying their real-world implementation, I believe this fundamentally misunderstands both the current state of AI and the role of mid-level engineers. Here’s what Meta is missing.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Giga: An AI that actually understands your codebase</title>
      <link href="https://nmn.gl/blog/giga-intro" rel="alternate" type="text/html" title="Giga: An AI that actually understands your codebase" />
      <published>2025-01-13T00:00:00+00:00</published>
      <updated>2025-01-13T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/giga-intro</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/giga-intro"><![CDATA[<meta http-equiv="refresh" content="0; url=/blog/giga" />

<link rel="canonical" href="/blog/giga" />

<p>If you are not redirected automatically, follow this <a href="/blog/giga">link to the article</a>.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
      

      

      
      
        <summary type="html"><![CDATA[If you are not redirected automatically, follow this link to the article.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Giga: An AI that actually understands your codebase</title>
      <link href="https://nmn.gl/blog/giga" rel="alternate" type="text/html" title="Giga: An AI that actually understands your codebase" />
      <published>2025-01-13T00:00:00+00:00</published>
      <updated>2025-01-13T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/giga</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/giga"><![CDATA[<meta http-equiv="refresh" content="0; url=https://gigamind.dev" />

<link rel="canonical" href="https://gigamind.dev" />

<p>If you are not redirected automatically, follow this <a href="https://gigamind.dev/?utm_source=blog&amp;utm_medium=giga&amp;utm_campaign=giga&amp;utm_content=inline">link</a>.</p>

<p><em>Modern software development is complex. Our projects have hundreds of files, intricate dependencies, and carefully thought-out architectural decisions.</em></p>

<!--more-->

<h2 id="the-problem">The Problem</h2>

<p>Anyone who’s used AI coding assistants has experienced this: you ask for help with a feature, and the AI generates code that completely ignores your existing patterns. Or worse, it breaks your carefully planned architecture.</p>

<p>After experiencing this frustration firsthand, I started building Giga—a different kind of AI coding assistant that maintains deep, persistent understanding of your entire codebase.</p>

<h2 id="how-giga-works">How Giga Works</h2>
<p>The core innovation in Giga is what I call the “project brain”—a smart context management system that:</p>

<ul>
  <li>Automatically analyzes your project structure, dependencies, and architectural patterns</li>
  <li>Maintains a living SPEC.md file that captures your project’s technical decisions and constraints</li>
  <li>Integrates with git to track meaningful changes and evolve its understanding</li>
  <li>Uses this persistent context to ensure AI suggestions align perfectly with your existing architecture</li>
</ul>

<h2 id="early-results">Early Results</h2>
<p>The initial beta testing has been enlightening. Developers report that Giga:</p>

<ul>
  <li>Eliminates the need to repeatedly explain project structure to AI</li>
  <li>Significantly reduces instances of AI suggestions breaking existing architecture</li>
  <li>Maintains consistent understanding even as codebases grow</li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
          <category term="giga" />
        
      

      

      
      
        <summary type="html"><![CDATA[If you are not redirected automatically, follow this link. Modern software development is complex. Our projects have hundreds of files, intricate dependencies, and carefully thought-out architectural decisions.]]></summary>
      

      
      
        
        <media:thumbnail url="https://nmn.gl/blog/assets/giga.jpeg" />
        <media:content medium="image" url="https://nmn.gl/blog/assets/giga.jpeg" />
      

      
    </entry>
  
    <entry>
      

      <title type="html">Stop Using Just One AI Model in Production</title>
      <link href="https://nmn.gl/blog/on-model-redundancy" rel="alternate" type="text/html" title="Stop Using Just One AI Model in Production" />
      <published>2025-01-11T00:00:00+00:00</published>
      <updated>2025-01-11T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/on-model-redundancy</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/on-model-redundancy"><![CDATA[<p><em>Learning why model redundancy &gt; optimization</em></p>

<p>It started with a frustrating Thursday afternoon. Our code analysis service was hitting rate limits constantly, and I was doing what any reasonable engineer would do: optimizing our token usage, implementing better queuing, and trying to squeeze maximum performance from our chosen model.</p>

<p>Nothing worked. Or rather, everything worked a little bit, but not enough.</p>

<!--more-->

<h2 id="the-discovery">The Discovery</h2>

<p>We were using Amazon Bedrock’s Nova-Pro model for our code analysis service. It’s a powerful model with a quota of 100,000 tokens per minute. Sounds good enough, right? That’s what we thought too.</p>

<p>But here’s the thing about analyzing code: it’s bursty. A developer pushes 50 files, and suddenly you need to process everything now. Not in a nice, evenly distributed way that would make your rate limiter happy.</p>

<p>During one particularly frustrating debugging session, I temporarily switched to a “lesser” model (Llama-3) while debugging what I thought was an issue with Nova-Pro. And then something interesting happened: our overall throughput went up.</p>

<h2 id="the-math">The Math</h2>

<p>My current quotas:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Nova-Pro: 100,000 tokens/minute
Llama-3: 60,000 tokens/minute
</code></pre></div></div>

<p>But it wasn’t just about raw numbers. The real magic happened when we started treating these models as complementary resources rather than primary/backup options.</p>

<h2 id="the-conventional-wisdom-that-we-ignored">The Conventional Wisdom (That We Ignored)</h2>

<p>Traditional wisdom says:</p>

<ol>
  <li>Pick the best model for your use case</li>
  <li>Optimize your usage of that model</li>
  <li>Implement proper rate limiting</li>
  <li><em>Maybe</em> have a backup model for failover</li>
</ol>

<p>We threw that out the window. Instead, we:</p>

<ol>
  <li>Use both models simultaneously</li>
  <li>Route larger batches to Nova-Pro</li>
  <li>Send smaller batches to Llama-3</li>
  <li>Let them handle rate limits independently</li>
</ol>

<h2 id="why-this-shouldnt-work-but-does">Why This Shouldn’t Work (But Does)</h2>

<p>The obvious objection is cost. Why pay for two models when you could optimize one? But here’s where it gets interesting:</p>

<ol>
  <li><strong>Rate limits are not linear</strong>: When you hit a rate limit, you don’t just lose the excess capacity - you often lose entire batches of work that need to be retried.</li>
  <li><strong>Context switching is expensive</strong>: Every time you hit a rate limit and have to retry later, you’re losing context. With two models, you maintain momentum.</li>
  <li><strong>Different models, different strengths</strong>: What we found was that Llama-3 actually performed better on smaller, simpler files, while Nova-Pro excelled at complex, interrelated code analysis.</li>
</ol>

<h2 id="the-implementation">The Implementation</h2>

<p>Here’s the interesting part. Instead of complex rate limiting logic, we built a simple model alternation strategy:</p>

<ol>
  <li>Route larger batches to Nova-Pro</li>
  <li>Send smaller batches to Llama-3</li>
  <li>Let them handle rate limits independently</li>
</ol>

<p>Here’s some pseudocode to illustrate the idea:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Model-specific quotas and characteristics</span>
<span class="kd">const</span> <span class="nx">MODEL_QUOTAS</span> <span class="o">=</span> <span class="p">{</span>
  <span class="dl">'</span><span class="s1">nova-pro</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">tokensPerMinute</span><span class="p">:</span> <span class="mi">100</span><span class="nx">_000</span><span class="p">,</span>
    <span class="na">maxKbPerBatch</span><span class="p">:</span> <span class="mi">350</span><span class="p">,</span>  <span class="c1">// Conservative limit for large, complex batches</span>
  <span class="p">},</span>
  <span class="dl">'</span><span class="s1">llama-3</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">tokensPerMinute</span><span class="p">:</span> <span class="mi">60</span><span class="nx">_000</span><span class="p">,</span>
    <span class="na">maxKbPerBatch</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>  <span class="c1">// Better for smaller, simpler batches</span>
  <span class="p">}</span>
<span class="p">}</span> <span class="k">as</span> <span class="kd">const</span><span class="p">;</span>

<span class="cm">/**
 * Pre-assigns models to groups based on their characteristics
 * Returns batches with their assigned models
 */</span>
<span class="kd">function</span> <span class="nx">createModelAwareBatches</span><span class="p">(</span><span class="nx">fileGroups</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]):</span> <span class="p">{</span> <span class="nl">model</span><span class="p">:</span> <span class="nx">ModelId</span><span class="p">,</span> <span class="nx">batch</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="p">}[]</span> <span class="p">{</span>
  <span class="c1">// First, analyze and assign optimal models for each group</span>
  <span class="kd">const</span> <span class="nx">groupsWithModels</span> <span class="o">=</span> <span class="nx">fileGroups</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">group</span> <span class="o">=&gt;</span> <span class="p">({</span>
    <span class="nx">group</span><span class="p">,</span>
    <span class="c1">// Assign model based on group characteristics</span>
    <span class="na">model</span><span class="p">:</span> <span class="nx">selectModelForGroup</span><span class="p">(</span><span class="nx">group</span><span class="p">)</span>
  <span class="p">}));</span>

  <span class="c1">// Create batches respecting each model's size limits</span>
  <span class="kd">const</span> <span class="nx">batches</span><span class="p">:</span> <span class="p">{</span> <span class="nl">model</span><span class="p">:</span> <span class="nx">ModelId</span><span class="p">,</span> <span class="nx">batch</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="p">}[]</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">let</span> <span class="nx">currentBatch</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">let</span> <span class="nx">currentModel</span><span class="p">:</span> <span class="nx">ModelId</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">currentBatchSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="nx">groupsWithModels</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(({</span> <span class="nx">group</span><span class="p">,</span> <span class="nx">model</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">groupSizeKb</span> <span class="o">=</span> <span class="nx">group</span><span class="p">.</span><span class="nx">totalSize</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">;</span>

    <span class="c1">// Start new batch if:</span>
    <span class="c1">// 1. Is first batch</span>
    <span class="c1">// 2. Different model than current batch</span>
    <span class="c1">// 3. Would exceed model's size limit</span>
    <span class="k">if</span> <span class="p">(</span>
      <span class="nx">currentModel</span> <span class="o">===</span> <span class="kc">null</span> <span class="o">||</span>
      <span class="nx">model</span> <span class="o">!==</span> <span class="nx">currentModel</span> <span class="o">||</span>
      <span class="nx">currentBatchSize</span> <span class="o">+</span> <span class="nx">groupSizeKb</span> <span class="o">&gt;</span> <span class="nx">MODEL_QUOTAS</span><span class="p">[</span><span class="nx">model</span><span class="p">].</span><span class="nx">maxKbPerBatch</span>
    <span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">currentBatch</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">batches</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
          <span class="na">model</span><span class="p">:</span> <span class="nx">currentModel</span><span class="p">,</span>
          <span class="na">batch</span><span class="p">:</span> <span class="nx">currentBatch</span>
        <span class="p">});</span>
      <span class="p">}</span>
      <span class="nx">currentBatch</span> <span class="o">=</span> <span class="p">[</span><span class="nx">group</span><span class="p">];</span>
      <span class="nx">currentModel</span> <span class="o">=</span> <span class="nx">model</span><span class="p">;</span>
      <span class="nx">currentBatchSize</span> <span class="o">=</span> <span class="nx">groupSizeKb</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nx">currentBatch</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">group</span><span class="p">);</span>
      <span class="nx">currentBatchSize</span> <span class="o">+=</span> <span class="nx">groupSizeKb</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">});</span>

  <span class="c1">// Add the last batch</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">currentBatch</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">currentModel</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">batches</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
      <span class="na">model</span><span class="p">:</span> <span class="nx">currentModel</span><span class="p">,</span>
      <span class="na">batch</span><span class="p">:</span> <span class="nx">currentBatch</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">batches</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/**
 * Selects optimal model based on group characteristics
 * Complex groups -&gt; Nova Pro
 * Simple groups -&gt; Llama-3
 */</span>
<span class="kd">function</span> <span class="nx">selectModelForGroup</span><span class="p">(</span><span class="nx">group</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">):</span> <span class="nx">ModelId</span> <span class="p">{</span>
  <span class="c1">// Complexity factors:</span>
  <span class="c1">// - Number of files in group</span>
  <span class="c1">// - Number of imports/dependencies</span>
  <span class="c1">// - Size of files</span>
  <span class="c1">// - Type of files (e.g., test files vs core logic)</span>
  
  <span class="kd">const</span> <span class="nx">COMPLEXITY_THRESHOLD</span> <span class="o">=</span> <span class="mf">0.7</span><span class="p">;</span>  <span class="c1">// Normalized complexity score threshold</span>
  
  <span class="c1">// Higher score = more complex</span>
  <span class="kd">const</span> <span class="nx">complexityScore</span> <span class="o">=</span> <span class="nx">calculateComplexityScore</span><span class="p">(</span><span class="nx">group</span><span class="p">);</span>
  
  <span class="k">return</span> <span class="nx">complexityScore</span> <span class="o">&gt;</span> <span class="nx">COMPLEXITY_THRESHOLD</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">nova-pro</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">llama-3</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/**
 * Calculates normalized complexity score (0-1) based on group characteristics
 */</span>
<span class="kd">function</span> <span class="nx">calculateComplexityScore</span><span class="p">(</span><span class="nx">group</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">):</span> <span class="nx">number</span> <span class="p">{</span>
  <span class="c1">// Size factor: Larger groups are more complex</span>
  <span class="kd">const</span> <span class="nx">sizeFactor</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">group</span><span class="p">.</span><span class="nx">totalSize</span> <span class="o">/</span> <span class="p">(</span><span class="mi">20</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// Cap at 20KB</span>
  
  <span class="c1">// Dependency factor: More dependencies = more complex</span>
  <span class="kd">const</span> <span class="nx">dependencyFactor</span> <span class="o">=</span> <span class="nx">group</span><span class="p">.</span><span class="nx">complexity</span><span class="p">;</span>  <span class="c1">// Already normalized 0-1</span>
  
  <span class="c1">// File count factor: More files = more complex relationships</span>
  <span class="kd">const</span> <span class="nx">fileCountFactor</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">group</span><span class="p">.</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">/</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// Cap at 10 files</span>
  
  <span class="c1">// Weighted average of factors</span>
  <span class="k">return</span> <span class="p">(</span>
    <span class="nx">sizeFactor</span> <span class="o">*</span> <span class="mf">0.3</span> <span class="o">+</span>
    <span class="nx">dependencyFactor</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span>
    <span class="nx">fileCountFactor</span> <span class="o">*</span> <span class="mf">0.2</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="cm">/**
 * Process batches with assigned models, handling rate limits by switching models
 */</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">processBatches</span><span class="p">(</span><span class="nx">batches</span><span class="p">:</span> <span class="p">{</span> <span class="nl">model</span><span class="p">:</span> <span class="nx">ModelId</span><span class="p">,</span> <span class="nx">batch</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="p">}[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">Result</span><span class="p">[]</span><span class="o">&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="na">results</span><span class="p">:</span> <span class="nx">Result</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">{</span> <span class="nx">model</span><span class="p">,</span> <span class="nx">batch</span> <span class="p">}</span> <span class="k">of</span> <span class="nx">batches</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">try</span> <span class="p">{</span>
      <span class="c1">// Try with assigned model first</span>
      <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">processWithModel</span><span class="p">(</span><span class="nx">batch</span><span class="p">,</span> <span class="nx">model</span><span class="p">);</span>
      <span class="nx">results</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">isRateLimitError</span><span class="p">(</span><span class="nx">error</span><span class="p">))</span> <span class="p">{</span>
        <span class="c1">// On rate limit, try alternate model</span>
        <span class="kd">const</span> <span class="na">alternateModel</span><span class="p">:</span> <span class="nx">ModelId</span> <span class="o">=</span> <span class="nx">model</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">nova-pro</span><span class="dl">'</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">llama-3</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">nova-pro</span><span class="dl">'</span><span class="p">;</span>
        <span class="k">try</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">processWithModel</span><span class="p">(</span><span class="nx">batch</span><span class="p">,</span> <span class="nx">alternateModel</span><span class="p">);</span>
          <span class="nx">results</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">alternateError</span><span class="p">)</span> <span class="p">{</span>
          <span class="c1">// If both models fail, let error handling middleware deal with it</span>
          <span class="k">throw</span> <span class="nx">alternateError</span><span class="p">;</span>
        <span class="p">}</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>  <span class="c1">// Non-rate-limit errors are handled by middleware</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">results</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The results were pretty good:</p>

<ul>
  <li>~1.8x throughput improvement</li>
  <li>40% reduction in rate limit errors</li>
  <li>More consistent performance under load</li>
  <li>Most importantly, faster results == <strong>happier developers</strong></li>
</ul>

<h2 id="why-this-matters-now">Why This Matters Now</h2>

<p>As AI services become more central to our applications, we need to start thinking differently about capacity and redundancy. The old patterns of primary/backup and single-model optimization might not be the best approach anymore.</p>

<h2 id="the-trade-offs">The Trade-offs</h2>

<p>Let’s be honest about the downsides:</p>

<ul>
  <li>Higher potential costs</li>
  <li>More complex routing logic</li>
  <li>Potentially inconsistent results between models</li>
  <li>Additional monitoring requirements</li>
</ul>

<p>But in our case, the benefits far outweighed these costs.</p>

<h2 id="looking-forward">Looking Forward</h2>

<p>This accidental discovery has changed how we think about AI service architecture. Instead of asking “which model is best?”, we’re now asking “how can different models work together?”</p>

<p>The future might not be about finding the perfect model, but about orchestrating multiple models effectively.</p>

<p>What do you think? Have you tried similar approaches? I’d love to hear about your experiences with AI service architecture.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
      

      

      
      
        <summary type="html"><![CDATA[Learning why model redundancy &gt; optimization It started with a frustrating Thursday afternoon. Our code analysis service was hitting rate limits constantly, and I was doing what any reasonable engineer would do: optimizing our token usage, implementing better queuing, and trying to squeeze maximum performance from our chosen model. Nothing worked. Or rather, everything worked a little bit, but not enough.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">LLM “Structured Outputs” are Missing the Point</title>
      <link href="https://nmn.gl/blog/on-structured-outputs" rel="alternate" type="text/html" title="LLM “Structured Outputs” are Missing the Point" />
      <published>2025-01-07T00:00:00+00:00</published>
      <updated>2025-01-07T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/on-structured-outputs</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/on-structured-outputs"><![CDATA[<p>Last week, my AI coding assistant generated a perfectly-structured code review suggestion.</p>

<p>The format was immaculate - every field properly typed, every attribute carefully specified, the suggestion clear and actionable.</p>

<p>There was just one problem: it fundamentally misunderstood how our authentication system worked.</p>

<!--more-->

<h2 id="the-structured-output-trap">The Structured Output Trap</h2>

<p>The current narrative around structured outputs is compelling. OpenAI, LlamaIndex, and others promote them as the solution to AI reliability. The pitch is seductive: Define a schema, get perfectly formatted responses, never worry about parsing again.</p>

<p>It sounds perfect. It’s not.</p>

<p>Here’s a real example from my extension. Using function calling, I defined a schema for code review suggestions:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span> <span class="nx">CodeReviewSuggestion</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">severity</span><span class="p">:</span> <span class="dl">'</span><span class="s1">critical</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">warning</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">info</span><span class="dl">'</span><span class="p">;</span>
  <span class="nl">category</span><span class="p">:</span> <span class="dl">'</span><span class="s1">security</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">performance</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">maintainability</span><span class="dl">'</span><span class="p">;</span>
  <span class="nl">location</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">file</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
    <span class="nl">startLine</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
    <span class="na">endLine</span><span class="p">:</span> <span class="kr">number</span>
  <span class="p">};</span>
  <span class="nl">suggestion</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">impact</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">fix</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">before</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
    <span class="nl">after</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
    <span class="nl">rationale</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The AI generated this perfectly-structured response:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"severity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"critical"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"security"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"location"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"file"</span><span class="p">:</span><span class="w"> </span><span class="s2">"src/auth/session.ts"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"startLine"</span><span class="p">:</span><span class="w"> </span><span class="mi">45</span><span class="p">,</span><span class="w">
    </span><span class="nl">"endLine"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"suggestion"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Move token validation before user data access"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"impact"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Potential unauthorized data access"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"fix"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"before"</span><span class="p">:</span><span class="w"> </span><span class="s2">"const userData = await getUser(token); validateToken(token);"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"after"</span><span class="p">:</span><span class="w"> </span><span class="s2">"validateToken(token); const userData = await getUser(token);"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rationale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ensure token is valid before accessing user data"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The response was perfectly structured. Every field was correctly typed. The suggestion seemed logical. There was just one critical problem: <code class="language-plaintext highlighter-rouge">getUser()</code> is idempotent and caches results, while <code class="language-plaintext highlighter-rouge">validateToken()</code> has side effects that refresh the token. The suggested reordering would break our token refresh mechanism.</p>

<p>This isn’t a parsing error or a hallucination in the traditional sense. The AI followed its output schema perfectly. It just made incorrect assumptions about our codebase’s behavior.</p>

<h2 id="the-real-problem">The Real Problem</h2>

<p>I’ve found that the real challenge lies in what happens before and after those perfectly formatted responses. Building AI tooling has taught me that we’re optimizing for the wrong thing. While everyone focuses on making AI outputs more structured, the real challenges lie elsewhere:</p>

<ol>
  <li><strong>Context Quality</strong>: The reliability of AI responses depends far more on the quality and structure of inputs than outputs. Sending normalized, validated context matters more than receiving structured responses.</li>
  <li><strong>Reality Validation</strong>: A perfectly structured response that contradicts project reality is worse than an unstructured response that gets the fundamentals right.</li>
  <li><strong>Failure Patterns</strong>: Most AI failures aren’t structural - they’re logical. The AI confidently produces well-structured responses that make incorrect assumptions about your codebase.</li>
</ol>

<p>More importantly, structured outputs create a dangerous illusion of reliability. When you receive a perfect JSON response, you naturally tend to trust it more. This is exactly the wrong instinct.</p>

<h2 id="a-different-approach">A Different Approach</h2>

<p>In my experience building an AI coding assistant, the most significant reliability gains came from what we did before and after the AI interaction, not from output formatting.</p>

<p>This points to a different future for AI tooling. Instead of obsessing about output structure, we need to focus on:</p>

<ol>
  <li>Better tools for preparing and validating AI inputs</li>
  <li>More sophisticated reality-checking of AI suggestions</li>
  <li>Explicit handling of AI failure modes that look structurally correct</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>Yes, structured outputs are valuable. But they’re also dangerous precisely because they’re so convincing. A malformed JSON response is obvious. A perfectly structured response that subtly misunderstands your application’s requirements is a time bomb.</p>

<p>For those building AI tools: Stop obsessing about output structure. Start obsessing about input quality and reality validation. Because in the real world, a perfectly formatted wrong answer is still wrong — it’s just harder to spot.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
      

      

      
      
        <summary type="html"><![CDATA[Last week, my AI coding assistant generated a perfectly-structured code review suggestion. The format was immaculate - every field properly typed, every attribute carefully specified, the suggestion clear and actionable. There was just one problem: it fundamentally misunderstood how our authentication system worked.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">What 6 hours at HN #1 actually does to your traffic</title>
      <link href="https://nmn.gl/blog/hn-rank-1-analysis" rel="alternate" type="text/html" title="What 6 hours at HN #1 actually does to your traffic" />
      <published>2025-01-06T00:00:00+00:00</published>
      <updated>2025-01-06T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/hn-rank-1-analysis</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/hn-rank-1-analysis"><![CDATA[<p><em>“Every developer has that one post they almost didn’t write…”</em></p>

<p>For 2025, I decided to get back into technical writing.</p>

<p>I’ve been obsessing over getting AI to actually <em>understand</em> production code. Not just generate it, but really grok what’s happening in a mature codebase.</p>

<p>After countless nights exploring this rabbit hole, I wrote about a breakthrough I had.</p>

<p>Sunday morning, I posted it on HackerNews. “Why not,” I thought, “maybe someone will find it interesting.”</p>

<p>Then I went back to my weekend.</p>

<!--more-->

<h2 id="the-moment-it-hit-1">The moment it hit #1</h2>

<p>I was lazing around with my girlfriend when I pulled up HN, as one does every 27 minutes. My karma looked… different?</p>

<p>Wait.</p>

<p><em>Holy shit.</em></p>

<p>The post was #1 on the front page.</p>

<blockquote class="twitter-tweet" data-dnt="true" data-theme="light"><p lang="en" dir="ltr">...FINALLY <br /><br />I&#39;ve had this dream since 14 years now <br /><br />I AM RANK #1 on HACKERNEWS<br /><br />!!! <br /><br />(go check out my post folks!) <a href="https://t.co/fiFpqaD0iz">pic.twitter.com/fiFpqaD0iz</a></p>&mdash; nmn (@NamanyayG) <a href="https://twitter.com/NamanyayG/status/1875924361586295021?ref_src=twsrc%5Etfw">January 5, 2025</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>I literally jumped off the couch (my girlfriend still doesn’t understand what HN is but she got hyped seeing me hyped).</p>

<p>Then panic set in — <em>I don’t even have analytics set up</em>.</p>

<p>Quick dive into Google Analytics setup (yes yes, I know, I’ll switch to something open source soon, I hate Google’s invasive monopoly as much as you do).</p>

<p>The numbers started flowing in. Hundreds of views per minute. Over half from the US.</p>

<blockquote class="twitter-tweet" data-dnt="true" data-theme="light"><p lang="en" dir="ltr">THANK YOU HACKERNEWS 🫡 <a href="https://t.co/vcaqsIMwLL">pic.twitter.com/vcaqsIMwLL</a></p>&mdash; nmn (@NamanyayG) <a href="https://twitter.com/NamanyayG/status/1875930895112351769?ref_src=twsrc%5Etfw">January 5, 2025</a></blockquote>

<p>Here’s what those first crazy hours looked like. I was really surprised to see how much the US dominates the audience of HN, I thought it would be somewhat more equally distributed:</p>

<figure>
<img src="assets/hn-1-traffic.png" />
<figcaption>New users by hour and country. Time is in IST, so 21:00 PM is 7:30 AM PST</figcaption>
</figure>

<p>Another surprising fact was how much the traffic was from mobile. I thought it would be more desktop. However, this data point is skewed because of how most HNers would have ad blockers enabled on their desktop browsers:</p>

<figure>
<img src="assets/hn-1-devices.png" />
<figcaption>Mobile vs Desktop traffic</figcaption>
</figure>

<p>But the real gold wasn’t in the traffic. It was in the <em>discussion</em>.</p>

<h2 id="how-hn-really-feels-about-ai">How HN really feels about AI</h2>

<p>Reading through 200+ comments was like watching a fascinating experiment unfold. The evolution of the discussion revealed deep insights about how we <em>really</em> think about AI.</p>

<h3 id="phase-1-show-me-the-code-hours-0-2">Phase 1: “Show me the code” (Hours 0-2)</h3>

<p>The initial response was pure, unfiltered HN energy. Multiple users immediately pointed out that my implementation details were too vague:</p>

<blockquote>
  <p>“This hard coded string is doing some very heavy lifting. This isn’t anything special until this string is also generated accurately and consistently for any reasonable PR.”</p>
</blockquote>

<p><em>Oof. Fair point.</em></p>

<p>What’s fascinating is how the early technical criticism followed a pattern:</p>

<ul>
  <li>Request for implementation details</li>
  <li>Skepticism about real-world application</li>
  <li>Discussion about evaluation methods</li>
</ul>

<p>But here’s what I didn’t expect: the harshest technical critics often became the most engaged participants.</p>

<p>The technical scrutiny made the discussion <em>better</em>. People weren’t just criticizing — they were sharing real solutions and experiences.</p>

<h3 id="phase-2-let-me-tell-you-about-my-ai-adventures-hours-2-5">Phase 2: “Let me tell you about my AI adventures” (Hours 2-5)</h3>

<p>As the post gained traction, something shifted. The discussion evolved from “prove it works” to “here’s what I’ve seen work”.</p>

<p>Developers started sharing war stories that revealed a fascinating pattern: everyone’s using AI, but nobody’s quite sure if they’re using it right.</p>

<blockquote>
  <p>“Just the other day I used cursor and iteratively implemented stories for 70 .vue files in few hours… something that would’ve taken me at least few days if not a week.”</p>
</blockquote>

<p>The really interesting part? Almost everyone who shared a success story immediately followed it with a caveat. And everyone who shared a failure had a “but sometimes it’s amazing” moment.</p>

<p>This phase had the most nuanced takes. No one was purely pro or anti AI — just developers sharing real experiences and trying to make sense of this new world.</p>

<h3 id="phase-3-but-what-does-it-all-mean-after-hour-6">Phase 3: “But what does it all mean?” (After Hour 6)</h3>

<p>As the comment section grew, the discussion took an interesting turn. Technical nitpicking gave way to broader discussions about AI tools in general.</p>

<p>The anthropomorphization debate kicked off when one user pointed out:</p>

<blockquote>
  <p>“Another cherry-picked example of an LLM doing something amazing, written about with a heavy dose of anthropomorphism.”</p>
</blockquote>

<p>This sparked a fascinating thread about how we talk about AI. Another user defended the approach:</p>

<blockquote>
  <p>“To me, articles like this are not so interesting for the results… The problem is that there is still mystery on how to use them effectively. Articles like that are great for learning new prompting tricks.”</p>
</blockquote>

<h3 id="what-the-comments-actually-revealed">What The Comments Actually Revealed</h3>

<p>Looking at all the discussion, there’s a clear pattern in how different groups approach AI tools. It’s not about pro-AI vs anti-AI — it’s about how much context you need before trusting the output.</p>

<p>Even the skeptics are using these tools — they’re just very specific about where and how.</p>

<p>That’s probably why the post resonated. We’re all trying to figure out how to use these tools effectively without breaking production. Some are just more… vocal about it than others.</p>

<h2 id="the-numbers-behind-the-noise">The numbers behind the noise</h2>

<p>The post stayed at #1 through Sunday morning. Here’s what that looked like in cold, hard data:</p>

<figure>
<img src="assets/hn-1-geo.png" />
</figure>

<figure>
<img src="assets/hn-1-cities.png" style="max-width: 100%; width: 20rem;" />
<figcaption>Top 10 cities by readers. And thanks for completely deleting #8, Google!</figcaption>
</figure>

<p>The US dominated with 3.5k readers, followed by UK (600) and Germany (560). What’s interesting is the city breakdown:</p>

<p>New York led with 355 readers, beating out Silicon Valley cities. This surprised me — I expected the bay area to dominate. The full top tech hub list:</p>

<ul>
  <li>New York: 355</li>
  <li>London: 296</li>
  <li>San Jose: 222</li>
  <li>Seattle: 192</li>
  <li>San Francisco: 159</li>
  <li>Los Angeles: 144</li>
</ul>

<p>Two things stand out:</p>

<ul>
  <li>East Coast tech showed up strong (NYC readers &gt; all Bay Area combined)</li>
  <li>European tech hubs made their presence known (London + Frankfurt + Berlin = 521 readers)</li>
</ul>

<p>In total, 8,500+ people read the post. Not bad for a Sunday morning blog about AI understanding code.</p>

<h2 id="critical-questions-we-need-to-solve">Critical questions we need to solve</h2>

<p>Reading through the discussion threads revealed something fascinating: while every comment approached AI differently, they all circled around the same core problems.</p>

<p>The technical debates weren’t really about my implementation — they were about fundamental challenges we’re all facing:</p>

<p>First, there’s the context problem. How much project context does AI need before it can make meaningful suggestions? More importantly, how do we provide that context efficiently? Several developers shared stories of AI seeming to understand their codebase, only to make changes that broke implicit patterns.</p>

<p>Then there’s the evaluation challenge. We know how to measure if code works, but how do we measure if AI truly understands what it’s modifying? The discussion showed that “working code” isn’t enough — we need ways to evaluate understanding.</p>

<p>But perhaps the most interesting questions came from teams already using AI. They weren’t asking if AI works, but how to work with it. When should AI suggest vs make changes? How do we maintain developer understanding when AI is modifying multiple files? What’s the right feedback loop?</p>

<h2 id="whats-next">What’s next</h2>

<p>This weekened discussion revealed something bigger than just AI and code. It showed how our relationship with development tools is evolving.</p>

<p>A year ago, AI coding discussions centered around replacement fears and capability debates. Now? We’re deep in the trenches, figuring out integration patterns and evaluation frameworks. The question isn’t whether to use AI — it’s how to use it effectively.</p>

<p>That’s probably why this resonated. We’re all exploring this new territory, trying to find the right balance between automation and understanding, between efficiency and control.</p>

<p>The next few years will be fascinating. Not because AI will get better at coding (it will), but because we’ll get better at working with AI. The tools will evolve, but more importantly, so will our understanding of how to use them.</p>

<p>I’m excited to keep exploring these questions, and based on this discussion, I’m not alone. If you’re interested in this evolution of development, reach out (twitter  <a href="https://x.com/NamanyayG">@NamanyayG</a> or email hi [at] nmn.gl).</p>

<p>The problems are real, the concerns are valid, and the solutions aren’t obvious. But fortunately, we’re software developers. And we’re used to solving problems.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="hackernews" />
        
      

      

      
      
        <summary type="html"><![CDATA[“Every developer has that one post they almost didn’t write…” For 2025, I decided to get back into technical writing. I’ve been obsessing over getting AI to actually understand production code. Not just generate it, but really grok what’s happening in a mature codebase. After countless nights exploring this rabbit hole, I wrote about a breakthrough I had. Sunday morning, I posted it on HackerNews. “Why not,” I thought, “maybe someone will find it interesting.” Then I went back to my weekend.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">The day I taught AI to read code like a Senior Developer</title>
      <link href="https://nmn.gl/blog/ai-senior-developer" rel="alternate" type="text/html" title="The day I taught AI to read code like a Senior Developer" />
      <published>2025-01-05T00:00:00+00:00</published>
      <updated>2025-01-05T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-senior-developer</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-senior-developer"><![CDATA[<p><em>A messy experiment that changed how we think about AI code analysis</em></p>

<p>Last week, I watched our AI choke on a React codebase - again. As timeout errors flooded my terminal, something clicked. We’d been teaching AI to read code like a fresh bootcamp grad, not a senior developer.</p>

<p>Here’s what I mean.</p>

<!--more-->

<h2 id="the-bootcamp-vs-senior-mindset">The Bootcamp vs Senior Mindset</h2>

<p>Remember your first day reading production code? Without any experience with handling mature codebases, you probably quickly get lost in the details<sup>[0]</sup></p>

<p>But watch a senior dev review a massive PR:</p>

<ul>
  <li>They jump straight to the core files</li>
  <li>Group changes by feature (“all auth changes, all db changes”)</li>
  <li>Build a mental model of architecture first</li>
  <li>Only then dive into implementation</li>
</ul>

<p>Obvious in hindsight, right? This realization led us to completely rewire our analyzer.</p>

<h2 id="the-experiment">The Experiment</h2>

<p>Instead of dumping files linearly, <a href="/blog/giga">we built a context-aware grouping system</a>:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nx">FileGroup</span> <span class="p">{</span>
  <span class="nl">files</span><span class="p">:</span> <span class="nx">ProjectFile</span><span class="p">[];</span>
  <span class="nl">totalSize</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
  <span class="nl">groupContext</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="c1">// 'auth', 'database', etc.</span>
<span class="p">}</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">groupFiles</span> <span class="o">=</span> <span class="p">(</span><span class="nx">files</span><span class="p">:</span> <span class="nx">ProjectFile</span><span class="p">[]):</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="c1">// Group files by related functionality and size</span>
  <span class="kd">const</span> <span class="nx">fileInfos</span> <span class="o">=</span> <span class="nx">files</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">file</span> <span class="o">=&gt;</span> <span class="p">({</span>
    <span class="nx">file</span><span class="p">,</span>
    <span class="na">size</span><span class="p">:</span> <span class="nx">file</span><span class="p">.</span><span class="nx">content</span><span class="p">?.</span><span class="nx">length</span> <span class="o">||</span> <span class="mi">0</span><span class="p">,</span>
    <span class="na">context</span><span class="p">:</span> <span class="nx">getFileContext</span><span class="p">(</span><span class="nx">file</span><span class="p">.</span><span class="nx">path</span><span class="p">)</span>
  <span class="p">}));</span>

  <span class="c1">// Process larger, more important files first</span>
  <span class="nx">fileInfos</span><span class="p">.</span><span class="nx">sort</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">size</span> <span class="o">-</span> <span class="nx">a</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span>

  <span class="kd">const</span> <span class="na">groups</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">let</span> <span class="nx">currentGroup</span> <span class="o">=</span> <span class="nx">createEmptyGroup</span><span class="p">();</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">{</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">context</span> <span class="p">}</span> <span class="k">of</span> <span class="nx">fileInfos</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">shouldStartNewGroup</span><span class="p">(</span><span class="nx">currentGroup</span><span class="p">,</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">context</span><span class="p">))</span> <span class="p">{</span>
      <span class="nx">groups</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">currentGroup</span><span class="p">);</span>
      <span class="nx">currentGroup</span> <span class="o">=</span> <span class="nx">createNewGroup</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">context</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nx">addFileToGroup</span><span class="p">(</span><span class="nx">currentGroup</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">size</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">groups</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then we changed how we prompt the AI. Instead of “analyze this file”, we give it context about the feature group first:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">buildGroupPrompt</span> <span class="o">=</span> <span class="p">(</span><span class="nx">group</span><span class="p">:</span> <span class="nx">FileGroup</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="s2">`
    Analyzing authentication system files:
    - Core token validation logic
    - Session management
    - Related middleware
    
    Focus on:
    1. How these integrate with existing auth patterns
    2. Security implications
    3. Performance impact on other systems

    Files to analyze:
    </span><span class="p">${</span><span class="nx">formatFiles</span><span class="p">(</span><span class="nx">group</span><span class="p">.</span><span class="nx">files</span><span class="p">)}</span><span class="s2">
  `</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="the-holy-shit-moment">The Holy Shit Moment</h2>

<p>The results broke our testing scripts. We thought it was a bug.</p>

<p>The AI went from:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"This file contains authentication logic using JWT tokens"
</code></pre></div></div>

<p>To:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"Warning: This auth change could impact websocket connections.
The token refresh logic shares patterns with the notification 
service (added last month), suggesting a potential race 
condition during high-traffic socket reconnects.

Related PR: #1234 (merged last week) modified the same
retry logic. Consider adding backoff."
</code></pre></div></div>

<p>That’s senior dev level awareness. It was catching connections we hadn’t explicitly taught it about.</p>

<h2 id="what-actually-changed">What Actually Changed?</h2>

<p>The magic isn’t in fancy ML or bigger models. It’s in mirroring how senior devs think:</p>

<ol>
  <li><strong>Context First</strong>: We front-load system understanding before diving into code</li>
  <li><strong>Pattern Matching</strong>: Group similar files to spot repeated approaches</li>
  <li><strong>Impact Analysis</strong>: Consider changes in relation to the whole system</li>
  <li><strong>Historical Understanding</strong>: Track why code evolved certain ways</li>
</ol>

<h2 id="the-unexpected-side-effects">The Unexpected Side Effects</h2>

<p>The system started catching things we didn’t design for:</p>

<ul>
  <li>Spotting copy-pasted code across different features</li>
  <li>Flagging inconsistent error handling patterns</li>
  <li>Warning about potential performance bottlenecks</li>
  <li>Suggesting architectural improvements based on usage patterns</li>
</ul>

<h2 id="why-this-matters">Why This Matters</h2>

<p>Every few days there’s a new “AI-powered IDE” on Product Hunt. They’re solving the wrong problem. Making code suggestions without deep context is like having a brilliant junior dev who just joined yesterday - they’ll write clean code that subtly breaks everything.</p>

<p>The key isn’t better code generation. It’s better code understanding.</p>

<h2 id="open-questions">Open Questions</h2>

<p>We’re still figuring out:</p>

<ul>
  <li>When to refresh vs preserve historical understanding</li>
  <li>How to handle conflicting patterns in different parts of the system</li>
  <li>Whether to expose uncertainty in the analysis</li>
</ul>

<h2 id="whats-next">What’s Next?</h2>

<p>I’m curious if we can teach AI to spot other senior dev instincts:</p>

<ul>
  <li>Identifying tech debt before it happens</li>
  <li>Suggesting architectural improvements</li>
  <li>Catching security issues from usage patterns</li>
  <li><a href="https://gigamind.dev/blog/ellie-ai-case-study">Understanding unwritten team conventions</a></li>
</ul>

<p>The problem isn’t making AI write more code. It’s teaching it to think about code the way experienced developers do.</p>

<p><small>
[0] Previously said <em>You probably did what I did - start at line 1, read every file top to bottom, get lost in the details.</em>, edited in response to <a href="https://news.ycombinator.com/item?id=42602156">feedback from advael</a>
</small></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      
        <category term="featured" scheme="http://namanyayg.com/post-types" />
      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
          <category term="giga" />
        
      

      

      
      
        <summary type="html"><![CDATA[A messy experiment that changed how we think about AI code analysis Last week, I watched our AI choke on a React codebase - again. As timeout errors flooded my terminal, something clicked. We’d been teaching AI to read code like a fresh bootcamp grad, not a senior developer. Here’s what I mean.]]></summary>
      

      
      

      
        <social:metrics>
          
          
          
            <social:hackernews>500+ votes</social:hackernews>
          
        </social:metrics>
      
    </entry>
  
    <entry>
      

      <title type="html">When perfect code isn’t enough: My journey with AI IDEs</title>
      <link href="https://nmn.gl/blog/ai-perfect-coding" rel="alternate" type="text/html" title="When perfect code isn’t enough: My journey with AI IDEs" />
      <published>2025-01-03T00:00:00+00:00</published>
      <updated>2025-01-03T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/ai-perfect-coding</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/ai-perfect-coding"><![CDATA[<p>Last month, I asked Claude to help refactor a React component. The code it wrote was beautiful - clean, well-documented, following all the best practices.</p>

<p>It also quietly broke our error tracking system, removed a crucial race condition check (that admittedly looked like a bug), and duplicated three utility functions with slightly different implementations.</p>

<p>Sound familiar?</p>

<p>The AI coding space is exploding right now. Cursor hit $50M ARR, Lovable.ai reached $4M in 4 weeks, and every day there’s a new “AI-powered IDE” on Product Hunt. Clearly, developers want AI assistance.</p>

<p>Yet despite these impressive numbers, I believe current AI coding tools are fundamentally solving the wrong problem. That’s why I’m building another one.</p>

<!--more-->

<h2 id="the-problem-isnt-code-generation">The Problem Isn’t Code Generation</h2>

<p>I’ve spent countless hours with current AI coding tools. The code generation is mind-blowing. The autocomplete feels magical. But there’s a deeper issue that becomes apparent only when you use these tools on real projects.</p>

<p>A Reddit comment perfectly captured it (sic): “Have AI agent fix what I ask for and on the way break 2-25 other things is the usual timesink where 95% of my time spent with agents goes to.”</p>

<p>This resonated deeply with me. Why? Because it’s not about the AI writing bad code - the code is usually quite good! It’s about the AI not understanding the hidden complexity and implicit patterns in our codebases.</p>

<p>I recently asked AI to add error handling to a component. The code it generated was clean and followed best practices. It also:</p>
<ul>
  <li>Added try-catch blocks that swallowed errors we specifically want to bubble up</li>
  <li>Created new utility functions that slightly differed from our existing ones</li>
  <li>Removed what looked like duplicate checks but were actually handling edge cases we discovered months ago</li>
</ul>

<p>All technically correct code… that subtly broke our established patterns.</p>

<h2 id="the-missing-piece-project-context--memory">The Missing Piece: Project Context + Memory</h2>

<p>When a senior dev joins your team, they don’t immediately start rewriting components. They take time to understand the invisible things:</p>
<ul>
  <li>Why that weird-looking useEffect is actually handling a crucial race condition</li>
  <li>Why we’re using a specific pattern for error handling</li>
  <li>Which parts of the “messy” code are actually addressing important edge cases</li>
</ul>

<p>Yet somehow, we expect AI to skip this crucial step and generate code in a vacuum.</p>

<p>I surveyed 33 developers about their AI tool usage. 76% use AI for code generation, but 85% reported spending significant time fixing AI-generated inconsistencies. Not because the code was bad, but because it didn’t align with their project’s reality.</p>

<h2 id="a-different-approach">A Different Approach</h2>

<p>This is why I’m building a new AI coding IDE extension that prioritizes understanding over generation. Instead of seeing each request in isolation, it maintains project memory through git history, commit messages, and codebase patterns.</p>

<p>The technical challenges are significant. But based on the conversations I’ve had, this project understanding is the missing piece in current AI coding tools. Line-by-line code generation is incredibly powerful, but without project context, it’s like having a brilliant programmer with severe short-term memory loss.</p>

<p>I’m building this in public and would love to hear your thoughts. How do you currently maintain project knowledge with AI tools? What aspects of project context do you find most important?</p>

<p><em>P.S.:</em> The project is in early development, with the first feature (project context management) already available. If you’re interested or want to know more, DM me on X (<a href="https://x.com/namanyayg">@namanyayg</a>) or email me (hi [at] nmn.gl).</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="ai" />
        
          <category term="dev" />
        
          <category term="giga" />
        
      

      

      
      
        <summary type="html"><![CDATA[Last month, I asked Claude to help refactor a React component. The code it wrote was beautiful - clean, well-documented, following all the best practices. It also quietly broke our error tracking system, removed a crucial race condition check (that admittedly looked like a bug), and duplicated three utility functions with slightly different implementations. Sound familiar? The AI coding space is exploding right now. Cursor hit $50M ARR, Lovable.ai reached $4M in 4 weeks, and every day there’s a new “AI-powered IDE” on Product Hunt. Clearly, developers want AI assistance. Yet despite these impressive numbers, I believe current AI coding tools are fundamentally solving the wrong problem. That’s why I’m building another one.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Hacked WordPress, Lost Rankings: My Jekyll Redemption Story</title>
      <link href="https://nmn.gl/blog/wordpress-hacked-jekyll-redemption" rel="alternate" type="text/html" title="Hacked WordPress, Lost Rankings: My Jekyll Redemption Story" />
      <published>2024-04-08T00:00:00+00:00</published>
      <updated>2024-04-08T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/wordpress-hacked-jekyll-redemption</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/wordpress-hacked-jekyll-redemption"><![CDATA[<p>I’ve been running multiple WordPress blogs for my friends and family on my own VPS since ~2012. I didn’t bother checking them for updates, and <em>surprise surprise</em> they all got hacked.</p>

<p>This is my journey of how I fixed it and how the latest version of my blog was born.</p>

<figure class="medium">
<img src="assets/wp-hacked-directory.png" />
<figcaption>Hmm, this is not what WordPress is supposed to look like is it?</figcaption>
</figure>

<!--more-->

<h2 id="0-initial-discovery">0. Initial Discovery</h2>

<p>I woke up one day to a notice from DigitalOcean claiming “intrusion” originating from my VPS’ IP.</p>

<figure class="medium">
<img src="assets/digitalocean-abuse-ticket.png" />
<figcaption>Oh no!</figcaption>
</figure>

<p>Well, <em>of course</em> my VPS couldn’t be at fault and this was a false alarm <em>(classic denial)</em>.</p>

<p>…But when I noticed the path containing <code class="language-plaintext highlighter-rouge">/wp-content/plugins/wp-striplple/</code>, I could guess what has happened.</p>

<h2 id="1-confirmation--anger">1. Confirmation &amp; Anger</h2>

<p>Could it really be that my multiple WordPress installs, many that I have not updated since many years, could have been hacked?</p>

<p>I read about this all the time, but certainly a hacked WordPress was something that happened to other people and not me!</p>

<p>Oh well.</p>

<p>As soon as I SSH’d into my server and opened my WP directory, I knew something was off. I had a lot of extra files present that I’m sure were not supposed to exist.</p>

<p>I deleted those which I could easily see, but then I checked all the WP sites that I was hosting. Seemed like most of them weren’t updated in years, so I had the easy option of shutting them down.</p>

<p>However, the site that was most impacted was my personal blog “Symmetrycode”. I had been blogging there since ~6 years. Now, the real posts on my blog were interspersed with articles with affiliate links. And worst of all, <strong>when I googled any article, my blog didn’t show up.</strong> I was deranked from Google for hosting malicious files.</p>

<p>At that point, I knew the Symmetrycode domain was dead. And I also knew that one day I’ll revive it.</p>

<p>Till then, I basically turned off each and every WP site I had installed by:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chown </span>newuser:newuser <span class="nt">-R</span> <span class="nb">.</span> <span class="c"># giving ownership of all files to a new user without any rights</span>
<span class="nb">chmod </span>600 <span class="nt">-R</span> <span class="nb">.</span> <span class="c"># make it only accessible by current user, not anyone else</span>
</code></pre></div></div>

<p>Now, the malware and all my sites were completely dead and inert. Phew?</p>

<h2 id="2-rebirth">2. Rebirth</h2>

<p>At this point, I have lost faith in any system with a backend.</p>

<p><em>The best way to prevent a hack is to not have anything hackable at all.</em></p>

<p>And you know what’s something that has powered the internet since it’s inception and cannot be hacked? Plain ol’ HTML and CSS.</p>

<p>So, I decided to move all my work from the past and in the future to static site hosting by moving to Github Pages.</p>

<p>I started off with my homepage, which is <a href="https://github.com/namanyayg/namanyayg.github.io/">now happily on Github</a>.</p>

<p>Then, I created a new Jekyll site for this blog. To move my old content, I followed these steps:</p>

<ol>
  <li>Take a dump of all db data using <code class="language-plaintext highlighter-rouge">mysqldump</code></li>
  <li>Import it into a local WordPress install on XAMPP</li>
  <li>Install the WordPress Jekyll Exporter plugin</li>
</ol>

<p>I got all my old posts in a friendly and beautiful markdown format.</p>

<p>I made myself a new Jekyll theme called “Pyaar2”, after the original WordPress theme I created for Symmetrycode <a href="https://github.com/namanyayg/pyaar">7 years ago</a>.</p>

<p>I finally was able to restore all posts and launch this blog around Christmas, as a small gift to myself.</p>

<h2 id="conclusion">Conclusion</h2>

<p>It’s heartbreaking to lose my old domain and rankings, but I’m sure I can figure out how to fix them. I have it in my todo list to add redirects, Lessons learned:</p>

<ul>
  <li><em>Every</em> piece of code needs maintenance and upgrades. So minimize writing code and avoid hosting yourself.</li>
  <li>Avoid hosting anything with a backend, especially if you want it to last decades and want no maintenance hassle.</li>
  <li>Storage and CDNs are cheap as dirt. Don’t pay for a VPS. Try to make everything into a static site, build locally, and then use Github Pages or Cloudflare to host the built files.</li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
      

      

      
      
        <summary type="html"><![CDATA[I’ve been running multiple WordPress blogs for my friends and family on my own VPS since ~2012. I didn’t bother checking them for updates, and surprise surprise they all got hacked. This is my journey of how I fixed it and how the latest version of my blog was born. Hmm, this is not what WordPress is supposed to look like is it?]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Resumable Tests Save &amp;amp; Load State for Jest and NodeJS</title>
      <link href="https://nmn.gl/blog/resume-state-on-tests" rel="alternate" type="text/html" title="Resumable Tests Save &amp;amp; Load State for Jest and NodeJS" />
      <published>2024-02-15T00:00:00+00:00</published>
      <updated>2024-02-15T00:00:00+00:00</updated>
      <id>https://nmn.gl/blog/resume-state-on-tests</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/resume-state-on-tests"><![CDATA[<p>At <a href="https://metricbooks.in/">Metric</a>, my latest accounting AI automation startup, we’ve built complex interconnected pipelines that query an LLM with multiple transactions and invoices.</p>

<p>Of course, the entire flow from beginning to end needs to be tested. Given the manual effort of uploading files and the long wait for LLM responses, we had to automate the entire test.</p>

<p>In the automation, I found many cases where an initial part of the test went correctly but a latter part got stuck and errored. Retrying from start would need at least ~1-2 minutes of waiting for all the initial cases to be processed, and I could not delete the initial cases either because the latter were connected with the response from the initial tests.</p>

<p>So I really had only one option — figure out how to save the state of a test frequently and allow resuming the test if there’s a crash.</p>

<p>To code this out, I broke down the problem into 2 parts:</p>

<ul>
  <li>Making generic methods to <code class="language-plaintext highlighter-rouge">load</code> and <code class="language-plaintext highlighter-rouge">save</code> state</li>
  <li>Saving the state at needed parts</li>
</ul>

<h2 id="state-methods-to-load-and-save">State methods to <code class="language-plaintext highlighter-rouge">load</code> and <code class="language-plaintext highlighter-rouge">save</code></h2>

<p>I like the pattern of <a href="https://transcoding.org/javascript/factory-functions/">Factory Functions</a>, so I created a <code class="language-plaintext highlighter-rouge">_stateManager</code> factory function.</p>

<p>It took a <code class="language-plaintext highlighter-rouge">testCode</code> paramter to indicate what test is running, and created a private variable <code class="language-plaintext highlighter-rouge">filePath</code> which was used to save the state of the test.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">_stateManager</span> <span class="o">=</span> <span class="p">(</span><span class="nx">testCode</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">autosaveStateDirectory</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">testCode</span><span class="p">}</span><span class="s2">-state.json`</span><span class="p">)</span>

  <span class="k">return</span> <span class="p">{</span>
    <span class="na">load</span><span class="p">:</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="c1">// load only the steps that are before loading transactions</span>
      <span class="c1">// i.e. transactions must always be retested</span>
      <span class="k">if</span> <span class="p">(</span>
        <span class="nx">testsSteps</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span><span class="p">)</span> <span class="o">&gt;</span>
        <span class="nx">testsSteps</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">UPLOAD_INITIAL_TRANSACTIONAL_DOCUMENTS</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">)</span>
        <span class="k">return</span> <span class="kc">false</span>
      <span class="k">try</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">rawData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">filePath</span><span class="p">,</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span><span class="p">)</span>
        <span class="kd">const</span> <span class="na">data</span><span class="p">:</span> <span class="nx">TestOutput</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">rawData</span><span class="p">)</span>
        <span class="nx">testOutput</span> <span class="o">=</span> <span class="nx">data</span>
        <span class="nx">log</span><span class="p">(</span><span class="s2">`Successfully loaded state! Last step completed: </span><span class="p">${</span><span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">true</span>
      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">false</span>
      <span class="p">}</span>
    <span class="p">},</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">save</code> function was easy: I just had to write the state to file.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">save</span><span class="p">:</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filePath</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">testOutput</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span>
  <span class="nx">log</span><span class="p">(</span><span class="s2">`Saved state after completing </span><span class="p">${</span><span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
  <span class="k">return</span> <span class="kc">true</span>
<span class="p">},</span>
</code></pre></div></div>

<p>For the <code class="language-plaintext highlighter-rouge">load</code> function, I also had to check whether the file exists or not.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">load</span><span class="p">:</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">rawData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">filePath</span><span class="p">,</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span><span class="p">)</span>
    <span class="kd">const</span> <span class="na">data</span><span class="p">:</span> <span class="nx">TestOutput</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">rawData</span><span class="p">)</span>
    <span class="nx">testOutput</span> <span class="o">=</span> <span class="nx">data</span>
    <span class="nx">log</span><span class="p">(</span><span class="s2">`Successfully loaded state! Last step completed: </span><span class="p">${</span><span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
    <span class="k">return</span> <span class="kc">true</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
    <span class="k">return</span> <span class="kc">false</span>
  <span class="p">}</span>
  <span class="p">},</span>
</code></pre></div></div>

<h2 id="setting-up-test-steps">Setting up test steps</h2>

<p>My test has a few steps like logging in, uploading documents, uploading transactions, etc. I represented those in an array</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">testsSteps</span> <span class="o">=</span> <span class="p">[</span>
  <span class="dl">'</span><span class="s1">LOGIN</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">CREATE_PARTIES</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">CREATE_RULES</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">UPLOAD_INITIAL_TRANSACTIONAL_DOCUMENTS</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">TRANSACTIONS</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">UPLOAD_FINAL_TRANSACTIONAL_DOCUMENTS</span><span class="dl">'</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Before starting any test, I instantiated the factory into a variable <code class="language-plaintext highlighter-rouge">stateManager</code></p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">stateManager</span> <span class="o">=</span> <span class="nx">_stateManager</span><span class="p">(</span><span class="nx">testInput</span><span class="p">.</span><span class="nx">testCode</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="saving-the-state-during-test">Saving the state during test</h2>

<p>I recorded all my state in a <code class="language-plaintext highlighter-rouge">testOutput</code> object, and stored the most recent completed step in <code class="language-plaintext highlighter-rouge">lastStepCompleted</code>.</p>

<p>At the start of each part, I updated <code class="language-plaintext highlighter-rouge">stepKey</code> which was the same as what I used in the <code class="language-plaintext highlighter-rouge">testsSteps</code> variable above.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// during the test, keep updating `testOutput` with data from the API</span>
<span class="nx">testOutput</span><span class="p">.</span><span class="nx">parties</span> <span class="o">=</span> <span class="p">...</span>

<span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span> <span class="o">=</span> <span class="nx">stepKey</span>
<span class="k">await</span> <span class="nx">stateManager</span><span class="p">.</span><span class="nx">save</span><span class="p">()</span>
</code></pre></div></div>

<p>Now, all that is left is to check if the specific part of the test has already loaded or not. If it has been loaded, skip that step entirely. This code should be at the very start of the test step.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">stepKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">CREATE_PARTIES</span><span class="dl">'</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">testsSteps</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">testOutput</span><span class="p">.</span><span class="nx">lastStepCompleted</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="nx">testsSteps</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">stepKey</span><span class="p">))</span> <span class="p">{</span>
  <span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\t</span><span class="s1">Skipping create parties due to loaded state</span><span class="dl">'</span><span class="p">)</span>
  <span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Optionally, you might have to “rollback” your server to the most recent step as well. This completely depends on your server so I won’t include code for that, but a rollback could be as easy as deleting a few rows in your DB.</p>

<h2 id="conclusion">Conclusion</h2>

<p>For good DX during long running tests, it’s important to be able to save and resume state as needed.</p>

<p>I came up with my own way to load and resume state, I am sure different libraries could have their own ways. But, I usually prefer writing my own code in such easy cases rather than depending on any abstractions.</p>

<p>With a simple factory function and a few changed lines during the tests, we were able to write a simple and easy to reason about test state manager.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
      

      

      
      
        <summary type="html"><![CDATA[At Metric, my latest accounting AI automation startup, we’ve built complex interconnected pipelines that query an LLM with multiple transactions and invoices. Of course, the entire flow from beginning to end needs to be tested. Given the manual effort of uploading files and the long wait for LLM responses, we had to automate the entire test. In the automation, I found many cases where an initial part of the test went correctly but a latter part got stuck and errored. Retrying from start would need at least ~1-2 minutes of waiting for all the initial cases to be processed, and I could not delete the initial cases either because the latter were connected with the response from the initial tests. So I really had only one option — figure out how to save the state of a test frequently and allow resuming the test if there’s a crash. To code this out, I broke down the problem into 2 parts: Making generic methods to load and save state Saving the state at needed parts State methods to load and save I like the pattern of Factory Functions, so I created a _stateManager factory function. It took a testCode paramter to indicate what test is running, and created a private variable filePath which was used to save the state of the test. const _stateManager = (testCode) =&gt; { const filePath = path.join(autosaveStateDirectory, `${testCode}-state.json`) return { load: async () =&gt; { // load only the steps that are before loading transactions // i.e. transactions must always be retested if ( testsSteps.indexOf(testOutput.lastStepCompleted) &gt; testsSteps.indexOf('UPLOAD_INITIAL_TRANSACTIONAL_DOCUMENTS') ) return false try { const rawData = await fs.readFile(filePath, 'utf8') const data: TestOutput = JSON.parse(rawData) testOutput = data log(`Successfully loaded state! Last step completed: ${testOutput.lastStepCompleted}`) return true } catch (e) { console.error(e) return false } }, } } The save function was easy: I just had to write the state to file. save: async () =&gt; { await fs.writeFile(filePath, JSON.stringify(testOutput, null, 2)) log(`Saved state after completing ${testOutput.lastStepCompleted}`) return true }, For the load function, I also had to check whether the file exists or not. load: async () =&gt; { try { const rawData = await fs.readFile(filePath, 'utf8') const data: TestOutput = JSON.parse(rawData) testOutput = data log(`Successfully loaded state! Last step completed: ${testOutput.lastStepCompleted}`) return true } catch (e) { console.error(e) return false } }, Setting up test steps My test has a few steps like logging in, uploading documents, uploading transactions, etc. I represented those in an array let testsSteps = [ 'LOGIN', 'CREATE_PARTIES', 'CREATE_RULES', 'UPLOAD_INITIAL_TRANSACTIONAL_DOCUMENTS', 'TRANSACTIONS', 'UPLOAD_FINAL_TRANSACTIONAL_DOCUMENTS', ] Before starting any test, I instantiated the factory into a variable stateManager const stateManager = _stateManager(testInput.testCode) Saving the state during test I recorded all my state in a testOutput object, and stored the most recent completed step in lastStepCompleted. At the start of each part, I updated stepKey which was the same as what I used in the testsSteps variable above. // during the test, keep updating `testOutput` with data from the API testOutput.parties = ... testOutput.lastStepCompleted = stepKey await stateManager.save() Now, all that is left is to check if the specific part of the test has already loaded or not. If it has been loaded, skip that step entirely. This code should be at the very start of the test step. const stepKey = 'CREATE_PARTIES' if (testsSteps.indexOf(testOutput.lastStepCompleted) &gt;= testsSteps.indexOf(stepKey)) { log('\tSkipping create parties due to loaded state') return false } Optionally, you might have to “rollback” your server to the most recent step as well. This completely depends on your server so I won’t include code for that, but a rollback could be as easy as deleting a few rows in your DB. Conclusion For good DX during long running tests, it’s important to be able to save and resume state as needed. I came up with my own way to load and resume state, I am sure different libraries could have their own ways. But, I usually prefer writing my own code in such easy cases rather than depending on any abstractions. With a simple factory function and a few changed lines during the tests, we were able to write a simple and easy to reason about test state manager.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Simple “Search” with LIKE in MySQL Sequelize</title>
      <link href="https://nmn.gl/blog/simple-search-with-like-in-mysql-sequelize" rel="alternate" type="text/html" title="Simple “Search” with LIKE in MySQL Sequelize" />
      <published>2018-10-13T06:07:37+00:00</published>
      <updated>2018-10-13T06:07:37+00:00</updated>
      <id>https://nmn.gl/blog/simple-search-with-like-in-mysql-sequelize</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/simple-search-with-like-in-mysql-sequelize"><![CDATA[<p>A simple way to implement a “search” feature into your node app is using the database engine to check for presence of tokenized search query. In my case, I’m using MySQL with the Sequelize ORM and needed to add a e-commerce like search form with product results for a client.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// sanitize plain text query to only keep alphanumeric lowercase</span>
<span class="kd">const</span> <span class="nx">sanitizedQuery</span> <span class="o">=</span> <span class="nx">query</span><span class="p">.</span><span class="nx">trim</span><span class="p">().</span><span class="nx">toLowerCase</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/</span><span class="se">[\W</span><span class="sr">_</span><span class="se">]</span><span class="sr">+/</span><span class="p">,</span> <span class="dl">''</span><span class="p">)</span>
<span class="c1">// split by space as basic tokenization</span>
<span class="kd">const</span> <span class="nx">queryTokens</span> <span class="o">=</span> <span class="nx">sanitizedQuery</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/</span><span class="se">\s</span><span class="sr">+/</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">where</span><span class="p">:</span> <span class="p">{</span>
    <span class="p">[</span><span class="nx">Op</span><span class="p">.</span><span class="nx">and</span><span class="p">]:</span> <span class="nx">queryTokens</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">token</span> <span class="o">=&gt;</span>
      <span class="c1">// check for presence of each token in lowercased product `title`</span>
      <span class="nx">Sequelize</span><span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">Sequelize</span><span class="p">.</span><span class="nx">fn</span><span class="p">(</span><span class="dl">'</span><span class="s1">lower</span><span class="dl">'</span><span class="p">,</span> <span class="nx">Sequelize</span><span class="p">.</span><span class="nx">col</span><span class="p">(</span><span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">)),</span> <span class="dl">'</span><span class="s1">LIKE</span><span class="dl">'</span><span class="p">,</span> <span class="s2">`%</span><span class="p">${</span><span class="nx">token</span><span class="p">}</span><span class="s2">%`</span><span class="p">)</span>
    <span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">results</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">Product</span><span class="p">.</span><span class="nx">FindAll</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span>
</code></pre></div></div>

<p>The main <code class="language-plaintext highlighter-rouge">cleverness</code> of this snippet is in mapping the <code class="language-plaintext highlighter-rouge">queryTokens</code> to generate <code class="language-plaintext highlighter-rouge">Sequelize.where()</code> queries that compare the lowercased title with our token. Sequelize has some powerful features to interact with the database at a low-level, and this is a great example of that used in a real-world project</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Code" />
        
          <category term="JavaScript" />
        
      

      
        <category term="javascript" />
      

      
      
        <summary type="html"><![CDATA[A simple way to implement a “search” feature into your node app is using the database engine to check for presence of tokenized search query. In my case, I’m using MySQL with the Sequelize ORM and needed to add a e-commerce like search form with product results for a client.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Better Vue SEO with Prerender SPA Plugin and vue-cli</title>
      <link href="https://nmn.gl/blog/better-vue-seo-with-prerender-spa-plugin-and-vue-cli" rel="alternate" type="text/html" title="Better Vue SEO with Prerender SPA Plugin and vue-cli" />
      <published>2018-01-18T21:41:03+00:00</published>
      <updated>2018-01-18T21:41:03+00:00</updated>
      <id>https://nmn.gl/blog/better-vue-seo-with-prerender-spa-plugin-and-vue-cli</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/better-vue-seo-with-prerender-spa-plugin-and-vue-cli"><![CDATA[<p>Vue loads content asynchronously, which means that Google’s crawlers won’t pick up your site for indexing. That is, until you give them a <em>rendered</em> version to see. We’re going to discuss a common way to serve content properly for crawlers here, called “Prerendering”.</p>

<p>One of the existing, common solutions is Server-side Rendering or SSR. They render all content on your server and then send it to the client along with JS for interaction. There’s even a new wave of easy to use SSR-compatible tooling, like <a href="https://nuxtjs.org/">Nuxt.js</a> (Vue) and <a href="https://github.com/zeit/next.js/">Next.js</a> (React). However, SSR does have downsides — it moves rendering load from client to server, and requires a node backend to be running.</p>

<p>A solid middle option is occupied by what’s called <em>Prerendering</em>, where you run a headless browser (either locally or on the server) to cache some of your site’s pages to disk. The <a href="https://github.com/chrisvfritz/prerender-spa-plugin">Prerender SPA Plugin</a> for Webpack makes it extremely easy to do all this.</p>

<h2 id="installation">Installation</h2>

<p><em>(I’m assuming you’re using the Webpack template for <code class="language-plaintext highlighter-rouge">vue-cli</code>, where all your configuration files are in the <code class="language-plaintext highlighter-rouge">./build</code> and you compile the final files to <code class="language-plaintext highlighter-rouge">./dist</code>.)</em></p>

<p>We’re going to create a new Webpack configuration file for the prerendering. We’ll also add a <code class="language-plaintext highlighter-rouge">script</code> to our <code class="language-plaintext highlighter-rouge">package.json</code> as an alias.</p>

<p>First, install and save by running <code class="language-plaintext highlighter-rouge">npm i prerender-spa-plugin</code> or <code class="language-plaintext highlighter-rouge">yarn add prerender-spa-plugin</code>. Then create a new file under <code class="language-plaintext highlighter-rouge">./build</code> named <code class="language-plaintext highlighter-rouge">webpack.prerender.conf.js</code>.</p>

<p>We can only prerender once we have a built <code class="language-plaintext highlighter-rouge">index.html</code> for us in the <code class="language-plaintext highlighter-rouge">./dist/</code> folder, so we need to run the existing build script to ensure we have that done correctly. Do so by requiring <code class="language-plaintext highlighter-rouge">path</code>, <code class="language-plaintext highlighter-rouge">merge</code>, and the production webpack config like so:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">merge</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">webpack-merge</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">baseWebpackConfig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">./webpack.prod.conf</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">PrerenderSpaPlugin</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">prerender-spa-plugin</span><span class="dl">'</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">webpackConfig</span> <span class="o">=</span> <span class="nx">merge</span><span class="p">(</span><span class="nx">baseWebpackConfig</span><span class="p">,</span> <span class="p">{</span>
</code></pre></div></div>

<p><a href="https://github.com/drewlustro">Drew Lustro</a> and <a href="https://github.com/chrisvfritz">Chris Fritz</a> have done a great job abstracting away the difficult work of prerendering, and so we simply add and configure their plugin like so:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">webpackConfig</span> <span class="o">=</span> <span class="nx">merge</span><span class="p">(</span><span class="nx">baseWebpackConfig</span><span class="p">,</span> <span class="p">{</span>
  <span class="na">plugins</span><span class="p">:</span> <span class="p">[</span>
    <span class="c1">// prerender the important pages</span>
    <span class="k">new</span> <span class="nx">PrerenderSpaPlugin</span><span class="p">(</span>
      <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">../dist</span><span class="dl">'</span><span class="p">),</span>
      <span class="p">[</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/about</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/faq</span><span class="dl">'</span> <span class="cm">/* and others... */</span><span class="p">],</span>
      <span class="p">{</span>
        <span class="cm">/**
          * A dirty hack: setting a very specific viewport size 
          * makes it very easy to check for the prerenderer in Vue's
          * `created()' via `window.innerWidth' and `window.innerHeight',
          * giving a way to server custom content for search engines
          */</span> 
        <span class="na">phantomPageViewportSize</span><span class="p">:</span> <span class="p">{</span>
          <span class="na">width</span><span class="p">:</span> <span class="mi">1242</span><span class="p">,</span>
          <span class="na">height</span><span class="p">:</span> <span class="mi">742</span>
        <span class="p">},</span>
        <span class="na">postProcessHtml</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
          <span class="c1">// `context.html' will contain the HTML returned by the</span>
          <span class="c1">// headless browser, and `context.route' will be the path</span>
          <span class="c1">// use this place to replace or fix the contents.</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">)</span>
  <span class="p">]</span>
<span class="p">})</span>

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nx">webpackConfig</span>
</code></pre></div></div>

<p>I’m using some of the plugins <code class="language-plaintext highlighter-rouge">options</code> since fine-tuning prerendering is often needed, but only the first two arguments are actually required — the path to <code class="language-plaintext highlighter-rouge">./dist</code> and an array of the routes you wish to prerender. Try to pick only routes that don’t change often to minimize time spent running the prerendering script.</p>

<p>The little hack I’ve added allows us to detect the viewport size can then be used in JS or CSS (via media queries) and then present a slightly different version of the page for crawlers. Setting a viewport size is needed if you’ve got responsive website anyway, to choose the version you wish to use for prerendering.</p>

<p>I’ve also needed to strip away all <code class="language-plaintext highlighter-rouge">&lt;style&gt;</code> tags from the page for a project since there was CSS from rarely-used third party modules being included in the pages that didn’t even use the modules. If you wish to do so too, use this one-liner in <code class="language-plaintext highlighter-rouge">postProcessHtml</code> which uses a simple RegEx:</p>

<p><code class="language-plaintext highlighter-rouge">return context.html.replace(/&lt;style type="text\/css"&gt;[\s\S]*?&lt;\/style&gt;/gi, '')</code></p>

<p>To make things easier, I copied the <code class="language-plaintext highlighter-rouge">./build/build.js</code> file to <code class="language-plaintext highlighter-rouge">./build/build.prerender.js</code> and made a few changes to get a pretty spinner when I prerender:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span>
<span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">./check-versions</span><span class="dl">'</span><span class="p">)()</span>

<span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">production</span><span class="dl">'</span>

<span class="kd">const</span> <span class="nx">ora</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">ora</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">chalk</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">chalk</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">webpack</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">webpack</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">webpackConfig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">./webpack.prerender.conf</span><span class="dl">'</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">spinner</span> <span class="o">=</span> <span class="nx">ora</span><span class="p">(</span><span class="dl">'</span><span class="s1">building for production...</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">spinner</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span>

<span class="nx">webpack</span><span class="p">(</span><span class="nx">webpackConfig</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">stats</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">spinner</span><span class="p">.</span><span class="nx">stop</span><span class="p">()</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">err</span>
  <span class="nx">process</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">toString</span><span class="p">({</span>
    <span class="na">colors</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">modules</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">children</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// If you are using ts-loader, setting this to true will make TypeScript errors show up during build.</span>
    <span class="na">chunks</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">chunkModules</span><span class="p">:</span> <span class="kc">false</span>
  <span class="p">})</span> <span class="o">+</span> <span class="dl">'</span><span class="se">\n\n</span><span class="dl">'</span><span class="p">)</span>

  <span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">hasErrors</span><span class="p">())</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">chalk</span><span class="p">.</span><span class="nx">red</span><span class="p">(</span><span class="dl">'</span><span class="s1">  Build failed with errors.</span><span class="se">\n</span><span class="dl">'</span><span class="p">))</span>
    <span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">chalk</span><span class="p">.</span><span class="nx">cyan</span><span class="p">(</span><span class="dl">'</span><span class="s1">  Build complete.</span><span class="se">\n</span><span class="dl">'</span><span class="p">))</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">chalk</span><span class="p">.</span><span class="nx">yellow</span><span class="p">(</span>
    <span class="dl">'</span><span class="s1">  Tip: built files are meant to be served over an HTTP server.</span><span class="se">\n</span><span class="dl">'</span> <span class="o">+</span>
    <span class="dl">'</span><span class="s1">  Opening index.html over file:// won</span><span class="se">\'</span><span class="s1">t work.</span><span class="se">\n</span><span class="dl">'</span>
  <span class="p">))</span>
<span class="p">})</span>
</code></pre></div></div>

<p>Finally, the following line in your <code class="language-plaintext highlighter-rouge">package.json</code> under <code class="language-plaintext highlighter-rouge">scripts</code> will give you an easy way to run the prerender from the command line: <code class="language-plaintext highlighter-rouge">"prerender": "node build/build.prerender.js"</code></p>

<p>Run the prerender using <code class="language-plaintext highlighter-rouge">npm run prerender</code>. See files generated in <code class="language-plaintext highlighter-rouge">./dist</code>. It’s like magic.</p>

<h2 id="testing-for-google">Testing for Google</h2>

<p>To check all the pages, make sure you run a local server for <code class="language-plaintext highlighter-rouge">dist</code> and browse every pre-rendered page with JavaScript turned off (Chrome: Inspector &gt; Settings &gt; “Disable Javascript” under Debugger). Things don’t always work the way you want them to, especially when you’re dealing with headless browsers.</p>

<p>Once you’ve gotten things looking visually perfect, do inspect the generated HTML for excess code that you could trim away to further decrease your filesize.</p>

<p>Finally, once you push and run the prerender on server, use the Google Webmaster’s Console to see if everything is working well; since your ultimate reason for prerendering is probably proper SEO anyway. Click “Request Indexing” under “Fetch as Google” to then get your app to show up on Google.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="https://github.com/chrisvfritz/prerender-spa-plugin">Prerender SPA Plugin Github</a></li>
  <li><a href="https://vuejsdevelopers.com/2017/04/01/vue-js-prerendering-node-laravel/">VueJSDeveloper’s Demo</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[Vue loads content asynchronously, which means that Google’s crawlers won’t pick up your site for indexing. That is, until you give them a rendered version to see. We’re going to discuss a common way to serve content properly for crawlers here, called “Prerendering”.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Bye Ghost, hello again WordPress!</title>
      <link href="https://nmn.gl/blog/bye-ghost-hello-again-wordpress" rel="alternate" type="text/html" title="Bye Ghost, hello again WordPress!" />
      <published>2017-08-28T09:51:34+00:00</published>
      <updated>2017-08-28T09:51:34+00:00</updated>
      <id>https://nmn.gl/blog/bye-ghost-hello-again-wordpress</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/bye-ghost-hello-again-wordpress"><![CDATA[<p>Ghost… I tried. Trust me, I really did. I <a href="http://symmetrycode.com/images-and-excerpts-a-few-practical-problems-with-ghost/">stuck with you for over 3 years</a>, developed a custom theme on you, hacked around any shortcomings you had. But yesterday, I had to give up. Trying to upgrade from 0.11.x to 1.x had to be one of the most annoying experiences I’ve had recently and enough is enough. It’s not me, it’s you.</p>

<p>…</p>

<p>So I login to <code class="language-plaintext highlighter-rouge">/ghost</code> to pen a <a href="http://symmetrycode.com/instant-real-time-rss-to-email/">new post</a> and I’m greeted with <em>2 bars at the top asking me to upgrade</em>. I agree and check out that Ghost is finally version 1, woohoo! Then I check out the <a href="https://docs.ghost.org/v1.0.0/docs/migrating-to-ghost-1-0-0">migration guide</a>…</p>

<p><em>Prepare your theme in advance</em></p>

<p><em>Backup your content using the JSON exporter</em></p>

<p><em>Create a brand new Ghost v1.0.0 site following our install guide</em></p>

<p><em>Use Ghost v1.0.0 Importer to import existing content</em></p>

<p><em>Copy your images to your new site.</em></p>

<p><em>Upload your theme – your theme will not be uploaded from the importer</em></p>

<p>Alright then — I start by preparing my custom theme. Upload it to <a href="https://themes.ghost.org/docs/gscan">gscan</a> and do a few find and replaces and I’m good. The backup is pretty quick, too. I then get to the part where everything got stuck — <a href="https://docs.ghost.org/v1.0.0/docs/install">installing Ghost CLI</a>.</p>

<h2 id="ghost-cli-is-opinionated-and-broken">Ghost CLI is opinionated and broken</h2>

<p>Ghost CLI seems to be focused more to make Ghost Pro easier to maintain for the team, simply because of how opinionated it is. Support only for Ubuntu + Nginx? Node v6 (I’ve read <a href="https://docs.ghost.org/v1.0.0/docs/supported-node-versions#section-why-follow-lts-">the reasons</a> and it seems like laziness, but I understand that they’re a business and need to be pragmatic with priorities)? I hit <code class="language-plaintext highlighter-rouge">n install lts</code> to downgrade to Node 6 from v8.4.0 and try to <code class="language-plaintext highlighter-rouge">npm i -g ghost-cli.</code></p>

<p>All’s good. I do <code class="language-plaintext highlighter-rouge">ghost install</code> in my ghost directory and everything breaks. I’m <a href="https://github.com/TryGhost/Ghost-CLI/issues/407">greeted with 3 screens of error logging</a> and so I browse around a couple of Github issues to make sense of it. I double, then triple, check MySQL and my MySQL credentials. I create a new user and new database to check if it helps. Nothing. After spending an hour here and growing tired of the fact that Ghost, in fact, didn’t make my writing any easier or faster; I give up on upgrading, go write and publish my post, and figure out how to move to WordPress.</p>

<h2 id="we-meet-again-wordpress">We meet again, WordPress</h2>

<p>WordPress might be old, clunky, and PHP (heh) — but it works, has basically the largest ecosystem with amazing plugins, and it makes it easier to work with pages <em>and</em> posts. I use the _s base theme, add a way to work with browser-sync and Stylus files, and whip up the <a href="http://github.com/namanyayg/pyaar">pyaar theme</a> (with lots of code copied from the last design). Turns out that WP and Ghost have pretty similar template hierarchies, just add a class with “entry” changed to “post” wherever you see it (<em>e.g.,</em> add <code class="language-plaintext highlighter-rouge">.post-content</code> to <code class="language-plaintext highlighter-rouge">.entry-content</code>) and you’ll get the theme more or less working.</p>

<p>Used <a href="http://ahmed.amayem.com/ghost-json-export-file-to-wordpress-xml-import-file-converter/">Ghost to WP XML converter</a> to transfer all content from posts, moved <code class="language-plaintext highlighter-rouge">/content/images</code> to <code class="language-plaintext highlighter-rouge">/wp-content/uploads</code> and added an <code class="language-plaintext highlighter-rouge">.htaccess</code> 301 Redirect.</p>

<p>I’ve yet to test the theme properly (<a href="mailto:mail@namanyayg.com">let me know if you see anything broken</a>) and have a lot more content planned that I need to integrate before I can make Symmetrycode what I envision it to be. I’m slightly sad that I lost Ghost, but I guess it made me appreciate WP’s stability a lot more. And it’s good to be back.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[Ghost… I tried. Trust me, I really did. I stuck with you for over 3 years, developed a custom theme on you, hacked around any shortcomings you had. But yesterday, I had to give up. Trying to upgrade from 0.11.x to 1.x had to be one of the most annoying experiences I’ve had recently and enough is enough. It’s not me, it’s you.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Instant, real-time, RSS to email</title>
      <link href="https://nmn.gl/blog/instant-real-time-rss-to-email" rel="alternate" type="text/html" title="Instant, real-time, RSS to email" />
      <published>2017-08-27T14:24:09+00:00</published>
      <updated>2017-08-27T14:24:09+00:00</updated>
      <id>https://nmn.gl/blog/instant-real-time-rss-to-email</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/instant-real-time-rss-to-email"><![CDATA[<p>I’ve been looking for a free option for instant RSS to email subscriptions, and while Zapier comes close; the free tier does not support the volume <em>or</em> speed that I require.</p>

<p>That’s why I mocked up <a href="https://github.com/namanyayg/sangh">Sangh</a>. Using Sangh with Gmail’s Filters allows you to have a powerful and well-regulated inbox with a real-time subscription to whatever feeds you want to follow.</p>

<h2 id="1-formatting-rss">1. Formatting RSS</h2>

<p>The <a href="https://www.npmjs.com/package/rss-parser"><code class="language-plaintext highlighter-rouge">rss-parser</code></a> looks to be a solid bet in terms of simplicity.</p>

<p>I grab the 3 latest entries from the feed and pass them to the parsing function which returns content in an object that can be consumed by <code class="language-plaintext highlighter-rouge">nodemailer</code> that we’ll set up later. The <code class="language-plaintext highlighter-rouge">formatEntry</code> function is where you’ll make changes and perform string interpolation to fit your content.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">formatEntry</span> <span class="o">=</span> <span class="nx">entry</span> <span class="o">=&gt;</span> <span class="p">({</span>  
  <span class="na">to</span><span class="p">:</span> <span class="nx">TO_EMAIL</span><span class="p">,</span>
  <span class="na">subject</span><span class="p">:</span> <span class="s2">`</span><span class="p">${</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">title</span> <span class="p">}</span><span class="s2">`</span><span class="p">,</span>
  <span class="na">html</span><span class="p">:</span> <span class="s2">`</span><span class="p">${</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">content</span> <span class="p">}</span><span class="s2">`</span>
<span class="p">})</span>

<span class="nx">parser</span><span class="p">.</span><span class="nx">parseURL</span><span class="p">(</span><span class="nx">RSS_URL</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">parsed</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>  
  <span class="kd">let</span> <span class="nx">entries</span> <span class="o">=</span> <span class="nx">parsed</span><span class="p">.</span><span class="nx">feed</span><span class="p">.</span><span class="nx">entries</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1">// only latest 3</span>

  <span class="k">for</span> <span class="p">(</span><span class="nx">entry</span> <span class="k">of</span> <span class="nx">entries</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">sendEmail</span><span class="p">(</span><span class="nx">formatEntry</span><span class="p">(</span><span class="nx">entry</span><span class="p">))</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<h2 id="2-configuring-sendmail">2. Configuring <code class="language-plaintext highlighter-rouge">sendmail</code></h2>

<p>I’m hosting things on a $5 DO droplet <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-16-04">which I’ve configured with Postfix</a> and can use <code class="language-plaintext highlighter-rouge">sendmail</code> on, which makes it really easy to use <a href="https://nodemailer.com/about/">Nodemailer</a>‘s <a href="https://nodemailer.com/transports/sendmail/">sendmail transport</a>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">nodemailer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">nodemailer</span><span class="dl">'</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">transporter</span> <span class="o">=</span> <span class="nx">nodemailer</span><span class="p">.</span><span class="nx">createTransport</span><span class="p">({</span>  
  <span class="na">sendmail</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
  <span class="na">newline</span><span class="p">:</span> <span class="dl">'</span><span class="s1">windows</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">path</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/usr/sbin/sendmail</span><span class="dl">'</span>
<span class="p">})</span>
</code></pre></div></div>

<h2 id="3-preventing-repeats">3. Preventing repeats</h2>

<p>Now here’s the challenge — <em>we need to ensure that no repeat emails are sent to us in case the RSS feed doesn’t actually update</em>. I solved this with a simple sqlite database that records RSS item’s ID and doesn’t send out the email if it’s already sent.</p>

<p>I use <code class="language-plaintext highlighter-rouge">Sequelize</code> + <code class="language-plaintext highlighter-rouge">sqlite</code> here. An ORM just makes simple read/write operations easier, and <code class="language-plaintext highlighter-rouge">sqlite</code>‘s flat file datbase is perfect for this approach.</p>

<p>Initialize the database like so:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">Sequelize</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">sequelize</span><span class="dl">'</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">sequelize</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Sequelize</span><span class="p">({</span>  
  <span class="na">host</span><span class="p">:</span> <span class="dl">'</span><span class="s1">localhost</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">dialect</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sqlite</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">storage</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./posts.sqlite</span><span class="dl">'</span>
<span class="p">})</span>

<span class="kd">const</span> <span class="nx">Post</span> <span class="o">=</span> <span class="nx">sequelize</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">post</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>  
  <span class="na">id</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">type</span><span class="p">:</span> <span class="nx">Sequelize</span><span class="p">.</span><span class="nx">STRING</span><span class="p">,</span>
    <span class="na">notNull</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">primaryKey</span><span class="p">:</span> <span class="kc">true</span>
  <span class="p">}</span>
<span class="p">},</span> <span class="p">{</span>
  <span class="na">timestamps</span><span class="p">:</span> <span class="kc">false</span>
<span class="p">})</span>

<span class="nx">Post</span><span class="p">.</span><span class="nx">sync</span><span class="p">()</span>  
</code></pre></div></div>

<p>We then need to modify our ‘parser’ function to check if post already exists in the DB or not.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">parser</span><span class="p">.</span><span class="nx">parseURL</span><span class="p">(</span><span class="nx">RSS_URL</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">parsed</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>  
  <span class="kd">let</span> <span class="nx">entries</span> <span class="o">=</span> <span class="nx">parsed</span><span class="p">.</span><span class="nx">feed</span><span class="p">.</span><span class="nx">entries</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>

  <span class="k">for</span> <span class="p">(</span><span class="nx">entry</span> <span class="k">of</span> <span class="nx">entries</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">await</span> <span class="nx">Post</span><span class="p">.</span><span class="nx">findOrCreate</span><span class="p">({</span>
      <span class="na">where</span><span class="p">:</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">id</span> <span class="p">}</span>
    <span class="p">}).</span><span class="nx">spread</span><span class="p">((</span><span class="nx">_</span><span class="p">,</span> <span class="nx">created</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">created</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// if a new entry had to be created, send an email</span>
        <span class="nx">sendEmail</span><span class="p">(</span><span class="nx">formatEntry</span><span class="p">(</span><span class="nx">entry</span><span class="p">))</span>
      <span class="p">}</span>
    <span class="p">})</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<p>Note my use of <code class="language-plaintext highlighter-rouge">async</code> and <code class="language-plaintext highlighter-rouge">await</code> here. This is for performance reasons, since it’s better for <code class="language-plaintext highlighter-rouge">sqlite</code> to run synchronous create operations rather than async ones, it often breaks on async operations. <code class="language-plaintext highlighter-rouge">await</code>ing the <code class="language-plaintext highlighter-rouge">findOrCreate</code> promise makes the loop run synchronously, which is exactly what we want.</p>

<h2 id="4-run-it-every-minute">4. Run it every minute</h2>

<p>I use <code class="language-plaintext highlighter-rouge">cron</code> to run the node script every minute to check for updates. Check your node install location by running <code class="language-plaintext highlighter-rouge">which node</code> (mine is <code class="language-plaintext highlighter-rouge">/usr/local/bin/node</code>) and run <code class="language-plaintext highlighter-rouge">crontab -e</code> to open the crontab editor.</p>

<p>Add the line <code class="language-plaintext highlighter-rouge">* * * * * /usr/local/bin/node /path/to/your/script/index.js</code> to check and email updates every minute. And you’re done!</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>A quick, small project highlighted the importance of using various tools in modular ways to come up with something great. <code class="language-plaintext highlighter-rouge">sqlite</code> is perfect for such applications, and ‘offloading’ the actual checks to <code class="language-plaintext highlighter-rouge">cron</code> is much better and optimized than running a node script via <code class="language-plaintext highlighter-rouge">forever</code>/<code class="language-plaintext highlighter-rouge">pm2</code> etc. <a href="https://github.com/namanyayg/sangh">Get the final version of Sangh at Github</a>.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[I’ve been looking for a free option for instant RSS to email subscriptions, and while Zapier comes close; the free tier does not support the volume or speed that I require.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">How to do X using Sequelize</title>
      <link href="https://nmn.gl/blog/how-to-do-x-using-sequelize" rel="alternate" type="text/html" title="How to do X using Sequelize" />
      <published>2017-07-06T08:36:09+00:00</published>
      <updated>2017-07-06T08:36:09+00:00</updated>
      <id>https://nmn.gl/blog/how-to-do-x-using-sequelize</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/how-to-do-x-using-sequelize"><![CDATA[<p>I don’t know about you guys, but I always have an annoying time trying to figure out how to make Sequelize work — the documentation seems to have a dearth of examples. Here’s a few examples for “common” functionality.</p>

<p><strong>Get plain objects instead of Sequelize Instances after bulkCreate</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">models</span><span class="p">.</span><span class="nx">Item</span><span class="p">.</span><span class="nx">bulkCreate</span><span class="p">(</span><span class="nx">values</span><span class="p">)</span>  
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">results</span> <span class="o">=&gt;</span> <span class="nx">results</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">result</span> <span class="o">=&gt;</span> <span class="nx">result</span><span class="p">.</span><span class="kd">get</span><span class="p">({</span> <span class="na">plain</span><span class="p">:</span> <span class="kc">true</span> <span class="p">})))</span> <span class="c1">// Convert to plain</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">items</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">items</span><span class="p">))</span> <span class="c1">// Plain object array [{...}, {...}, ...]</span>
</code></pre></div></div>

<p>Pretty easy, use <code class="language-plaintext highlighter-rouge">.get({ plain: true })</code> on any Sequelize instance to convert it to a plain JavaScript object.</p>

<p><strong>Sequelize bulkCreate but ignore any duplicates</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">models</span><span class="p">.</span><span class="nx">Item</span><span class="p">.</span><span class="nx">bulkCreate</span><span class="p">(</span><span class="nx">values</span><span class="p">,</span> <span class="p">{</span>  
    <span class="na">ignoreDuplicates</span><span class="p">:</span> <span class="kc">true</span> 
<span class="p">})</span>
</code></pre></div></div>

<p>Just use the <code class="language-plaintext highlighter-rouge">ignoreDuplicates</code> option and pass it as the second argument.</p>

<p><strong>Deleting a row</strong></p>

<p>Delete is called <code class="language-plaintext highlighter-rouge">destroy</code>, use it as so:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">models</span><span class="p">.</span><span class="nx">Items</span><span class="p">.</span><span class="nx">destroy</span><span class="p">({</span>  
    <span class="na">where</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">id</span><span class="p">:</span> <span class="mi">42</span>
    <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[I don’t know about you guys, but I always have an annoying time trying to figure out how to make Sequelize work — the documentation seems to have a dearth of examples. Here’s a few examples for “common” functionality.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Why is Gift Card Rebel everywhere on YouTube?</title>
      <link href="https://nmn.gl/blog/gift-cards-rebel-everywhere-youtube" rel="alternate" type="text/html" title="Why is Gift Card Rebel everywhere on YouTube?" />
      <published>2017-06-17T18:37:20+00:00</published>
      <updated>2017-06-17T18:37:20+00:00</updated>
      <id>https://nmn.gl/blog/gift-cards-rebel-everywhere-youtube</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/gift-cards-rebel-everywhere-youtube"><![CDATA[<p>Recently I’ve been noticing a bunch of spam comments all over YouTube — with different text and links, but all leading to one single site: <em>giftcardrebel.co</em>. Why, how, or for what purpose — I have no idea.</p>

<p>The site in question looks like:</p>

<p><img src="/content/images/2017/06/4rjhV5U-1-.png" alt="" /></p>

<p>Clicking through finally gets you to a page where you need to fill a survey to continue. Boo. I assume the spammer earns a fixed amount per completed survey (cost per action.)</p>

<p>The spammer is very thorough, they have a few different domains leading to the exact same template.</p>

<h2 id="the-spam">The spam</h2>

<p>This is a comment I saw on a very recent <a href="http://i.imgur.com/f9VevEK.png">DJ Khaled’s Behind The Scenes</a></p>

<p><img src="/content/images/2017/06/f9VevEK-1-.png" alt="" /></p>

<p>The video in question:</p>

<p><img src="/content/images/2017/06/kmwn4Px-1-.png" alt="" /></p>

<p>This link seems to be masked via the Google Cloud Platform, possibly to prevent the account from being shadow-banned. Clicked on the link redirects to the Gift Card Rebel site that I’ve been seeing everywhere (the link in plaintext <code class="language-plaintext highlighter-rouge">https://storage.cloud.google.com/49282391/index.html</code>).</p>

<p>Here’s another comment on one of <a href="https://www.youtube.com/watch?v=bWiH45VOM3o">Stephen Colberts’ videos</a>, #3 on Trending at the time of this comment.</p>

<p><img src="/content/images/2017/06/MHcwSnx-1-.png" alt="" /></p>

<p>A few days earlier, I noticed links to Google Drive and pointed it out to a friend. However, the comment is now deleted.</p>

<p>Another type of comments say “Guys Look this up on youtube ==&gt; markolife10x incredible﻿” (the screenshot is from <a href="https://www.youtube.com/watch?v=kL2RRy_eYW0">Convenience Store Cooking Challenge</a>, #28 on Trending at the time of posting)</p>

<p><img src="/content/images/2017/06/0S1Y9Eq-1-.png" alt="" /></p>

<p>Searching for the exact term, curiously, gives us a single result:</p>

<p><img src="/content/images/2017/06/prElVkN-1-.png" alt="" /></p>

<p>Which basically is:</p>

<p><img src="/content/images/2017/06/AjRsC0V-1-.png" alt="" /></p>

<p>We’re getting this video and this video only because the description of the video contains “#markolife10x” (and of course, the term is unique enough that it’s not used anywhere else.)</p>

<h2 id="summing-up">Summing up</h2>

<ul>
  <li>These are new accounts with real sounding names, usually without profile pictures.</li>
  <li>Each comment gets around 200-400 likes and is ranked in top 3 under the video.</li>
  <li>The spam comment is deleted after a few hours, approximately 24-48 hours of posting (or once the comment loses it’s position in the page).</li>
  <li>A comment is posted very soon after a video is uploaded, around 30-60 minutes on average. It seems like the spammer is keeping an eye on some the regularly (semi?) viral channels.</li>
  <li>The spammer seems to be trying new tricks regularly. Earlier it started with t.co links, then it was google drive links, and currently it is google cloud storage links along with “search for x on YouTube.”</li>
</ul>

<p>In short — the spammer seems to have a comprehensive and automated method and a hoard of different Google accounts that they use and cycle through regularly.</p>

<h2 id="my-curiosities">My curiosities</h2>

<p>I’m wondering:</p>

<ul>
  <li>Why is the spammer doing this?</li>
  <li>What is their daily investment like?</li>
  <li>How many people are required to pull this off?</li>
</ul>

<p>Any discussion or answers will be appreciated — let’s <a href="https://news.ycombinator.com/item?id=14578306">discuss it on HackerNews</a>. I’ll try updating this post as I see new spam/methods.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[Recently I’ve been noticing a bunch of spam comments all over YouTube — with different text and links, but all leading to one single site: giftcardrebel.co. Why, how, or for what purpose — I have no idea.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Super easy image uploads with Multer and Express</title>
      <link href="https://nmn.gl/blog/super-easy-image-uploads-with-multer-and-express" rel="alternate" type="text/html" title="Super easy image uploads with Multer and Express" />
      <published>2017-05-18T01:07:09+00:00</published>
      <updated>2017-05-18T01:07:09+00:00</updated>
      <id>https://nmn.gl/blog/super-easy-image-uploads-with-multer-and-express</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/super-easy-image-uploads-with-multer-and-express"><![CDATA[<p><a href="https://github.com/expressjs/multer">Multer</a> makes file uploads via <code class="language-plaintext highlighter-rouge">&lt;form enctype="multipart/form-data"&gt;&lt;/form&gt;</code> really simple. If you’re looking to host user images on your filesystem and are wondering about the best practices involved, here’s what works for me.</p>

<h2 id="1-the-form">1. The form</h2>

<p>For the purposes of the article, I’m going to have a really simple form that submits a <code class="language-plaintext highlighter-rouge">POST</code> request to our endpoint and has a single <code class="language-plaintext highlighter-rouge">&lt;input type="file"&gt;</code> field.</p>

<pre><code class="language-jade">form(method="post" action="/img" enctype="multipart/form-data")  
  input(type="file" name="photo")
</code></pre>

<h2 id="2-the-endpoint">2. The endpoint</h2>

<p>Since I want to show this works with the default Express boilerplate, we’re going to put our routing in the default <code class="language-plaintext highlighter-rouge">routes/index.js</code> file.</p>

<p>First, we need to include <code class="language-plaintext highlighter-rouge">multer</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">multer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">multer</span><span class="dl">'</span><span class="p">)</span>  
<span class="kd">const</span> <span class="nx">upload</span> <span class="o">=</span> <span class="nx">multer</span><span class="p">({</span>  
  <span class="na">dest</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./public/images/users</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">limits</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">fileSize</span><span class="p">:</span> <span class="mi">10000000</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<p>I recommend uploading the images to a subfolder of <code class="language-plaintext highlighter-rouge">./public/images</code>, in this case <code class="language-plaintext highlighter-rouge">users</code>, because this is the default directory where you’ll be storing all your images anyway. Note that the path described in <code class="language-plaintext highlighter-rouge">dest</code> is relative to the project directory (i.e. where your <code class="language-plaintext highlighter-rouge">app.js</code> is stored). I’ve also set a limit of <code class="language-plaintext highlighter-rouge">10mb</code> but that’s optional.</p>

<p>I’ve got a simple set up going on with <code class="language-plaintext highlighter-rouge">passport</code> for user logins and <code class="language-plaintext highlighter-rouge">sequelize</code> as my ORM. Using either, however, is optional — you can handle image submissions without any logins at all and can use <code class="language-plaintext highlighter-rouge">mongoose</code> or some other DB system for storing the data.</p>

<p>Using <code class="language-plaintext highlighter-rouge">upload.single('&lt;name of input&gt;')</code> middleware adds <code class="language-plaintext highlighter-rouge">req.file</code> to the <code class="language-plaintext highlighter-rouge">req</code> object which contains metadata about the file.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">router</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/img</span><span class="dl">'</span><span class="p">,</span>  
  <span class="nx">upload</span><span class="p">.</span><span class="nx">single</span><span class="p">(</span><span class="dl">'</span><span class="s1">photo</span><span class="dl">'</span><span class="p">),</span> 
  <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>

  <span class="c1">// Return user if they're not logged in</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">req</span><span class="p">.</span><span class="nx">isAuthenticated</span><span class="p">())</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="c1">// Just a safety check</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">req</span><span class="p">.</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="c1">// Your filename is stored in `req.file.filename`, which then goes</span>
  <span class="c1">// to your database</span>

  <span class="kd">const</span> <span class="nx">newUserData</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">photo</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">filename</span><span class="p">,</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="what-does-this-code-do">What does this code do?</h3>

<p>You should now see files with 16 letter hex filenames being stored to your <code class="language-plaintext highlighter-rouge">public/images/users</code> directory. <a href="https://github.com/expressjs/multer/issues/170">Multer does not add any file extensions</a> as a safety precaution, and we now need to write some simple code to send the files with the appropriate mime-type so they’re served appropriately.</p>

<h2 id="3-serving-files">3. Serving Files</h2>

<p>We’re going to use <code class="language-plaintext highlighter-rouge">read-chunk</code> and <code class="language-plaintext highlighter-rouge">image-type</code> modules to check the actual format of the file and serve it with the correct mime type. Install them with <code class="language-plaintext highlighter-rouge">npm --save read-chunk image-type</code> and include them in your <code class="language-plaintext highlighter-rouge">routes/index.js</code> as so. We also need <code class="language-plaintext highlighter-rouge">path</code> and <code class="language-plaintext highlighter-rouge">fs</code> default modules.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">readChunk</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">read-chunk</span><span class="dl">'</span><span class="p">)</span>  
<span class="kd">const</span> <span class="nx">imageType</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">image-type</span><span class="dl">'</span><span class="p">)</span>  
<span class="kd">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">)</span>  
<span class="kd">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">)</span>  
</code></pre></div></div>

<p>Now here’s the clever part — we’re going to detect what file type the image is by reading their <a href="https://en.wikipedia.org/wiki/List_of_file_signatures">‘magic bytes’</a> and allow only <code class="language-plaintext highlighter-rouge">png</code>, <code class="language-plaintext highlighter-rouge">jpg</code>, or <code class="language-plaintext highlighter-rouge">jpeg</code> formats.</p>

<p>We’re using <a href="https://expressjs.com/en/guide/routing.html#route-parameters">Express’ route parameters</a> so we get the filename dynamically. Then, we store the <code class="language-plaintext highlighter-rouge">UPLOAD_PATH</code> and <code class="language-plaintext highlighter-rouge">FILE_PATH</code> for the image in appropriate constants.</p>

<p>Using the <code class="language-plaintext highlighter-rouge">read-chunk</code> and <code class="language-plaintext highlighter-rouge">image-type</code> modules, we read the first 12 bytes of the file and see what file format do they correspond to. We then check for the file formats we’re allowing and serve a default image (that is actually a 1×1 transparent png that I’ve put in the folder) that is sent over if the file is anything fishy.</p>

<p>Otherwise, we set the appropriate mime type and pipe a stream of file data through <code class="language-plaintext highlighter-rouge">res</code> via the <code class="language-plaintext highlighter-rouge">fs.createReadStream()</code> method.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">router</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/images/users/:img</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>  
  <span class="kd">const</span> <span class="nx">UPLOAD_PATH</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">./public/images/users/</span><span class="dl">'</span>
  <span class="kd">const</span> <span class="nx">FILE_PATH</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">UPLOAD_PATH</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">img</span><span class="p">)</span>

  <span class="kd">let</span> <span class="nx">imageData</span> <span class="o">=</span> <span class="nx">imageType</span><span class="p">(</span><span class="nx">readChunk</span><span class="p">.</span><span class="nx">sync</span><span class="p">(</span><span class="nx">FILE_PATH</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">12</span><span class="p">))</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">imageData</span><span class="p">.</span><span class="nx">ext</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">png</span><span class="dl">'</span> 
    <span class="o">||</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">ext</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">jpg</span><span class="dl">'</span> 
    <span class="o">||</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">ext</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">jpeg</span><span class="dl">'</span><span class="p">))</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">UPLOAD_PATH</span><span class="p">,</span> <span class="dl">'</span><span class="s1">default.png</span><span class="dl">'</span><span class="p">)).</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span>

  <span class="nx">res</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">,</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">mime</span><span class="p">)</span>
  <span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="nx">FILE_PATH</span><span class="p">).</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Express makes image uploads slightly complicated, but once you understand the philosophy behind their approach you can get it going really fast and easily. Multer is a very extensible library, even having modules for memory and Amazon S3 storage, among others. The <code class="language-plaintext highlighter-rouge">router.get</code> approach makes our application extremely robust and secure against any attacks that are usually otherwise missed.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      
        <category term="express" />
      
        <category term="node" />
      
        <category term="tutorial" />
      

      
      
        <summary type="html"><![CDATA[Multer makes file uploads via &lt;form enctype="multipart/form-data"&gt;&lt;/form&gt; really simple. If you’re looking to host user images on your filesystem and are wondering about the best practices involved, here’s what works for me.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Setting Up a Local Shopify Theme Development Environment in Seconds</title>
      <link href="https://nmn.gl/blog/setting-up-a-shopify-theme-development-environment-in-seconds-2" rel="alternate" type="text/html" title="Setting Up a Local Shopify Theme Development Environment in Seconds" />
      <published>2017-04-07T13:01:34+00:00</published>
      <updated>2017-04-07T13:01:34+00:00</updated>
      <id>https://nmn.gl/blog/setting-up-a-shopify-theme-development-environment-in-seconds-2</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/setting-up-a-shopify-theme-development-environment-in-seconds-2"><![CDATA[<p>A quick way to get a local shopify dev environment going:</p>

<h2 id="installation-and-setting-up">Installation and Setting Up</h2>

<p>The first step is to <a href="https://shopify.github.io/themekit/">install ThemeKit</a>.</p>

<p>Then, from your Shopify admin, Apps &gt; ‘View Private Apps’ &gt; Generate API Credentials with “Theme Templates and Theme Assets” to “Read and write” (doesn’t matter what the rest are) &gt; Save &gt; Copy the ‘password’ and store it.</p>

<h2 id="the-environment">The Environment</h2>

<p>If you want a new theme, then:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>theme bootstrap <span class="nt">--password</span><span class="o">=[</span>password] <span class="nt">--store</span><span class="o">=[</span>yourstore.myshopify.com]  
</code></pre></div></div>

<p>If you want to use an existing theme, get the theme ID from the shopify admin. Go to Online Store &gt; Themes and select “Edit HTML/CSS” on the theme you want a local environment of. Copy the digits at the end of the URL — this is your theme ID.</p>

<p>Then, configure and download with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>theme configure --password=[password] --store=[youstore.myshopify.com] --themeid=[your-theme-id]  
theme download  
</code></pre></div></div>

<h2 id="running">Running</h2>

<p>Use <code class="language-plaintext highlighter-rouge">theme watch</code> to run with a watcher on all the theme files and live-upload to the site.</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[A quick way to get a local shopify dev environment going:]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">CBSE 2016 Board Result All India Data Analysis</title>
      <link href="https://nmn.gl/blog/cbse-2016-all-india-data-analysis" rel="alternate" type="text/html" title="CBSE 2016 Board Result All India Data Analysis" />
      <published>2016-05-25T11:13:22+00:00</published>
      <updated>2016-05-25T11:13:22+00:00</updated>
      <id>https://nmn.gl/blog/cbse-2016-all-india-data-analysis</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/cbse-2016-all-india-data-analysis"><![CDATA[<p>I recently presented you with <a href="https://nmn.gl/blog/cbse-2016-analysis-of-dps-vasant-kunj/">a frequency distribution of CBSE board marks for DPS Vasant Kunj</a>, and now I’m back with an analysis of <strong>4052</strong> CBSE schools across India, with results of over <strong>476898</strong> students.</p>

<p>Here are the interactive subject-wise charts plotted between number of students and number of marks scored.</p>

<p>I found many interesting trends, hope you enjoy sifting through the results as well. I might detail <em>how exactly</em> I obtained the data in another post, let me know if you’re looking forward to it. In the meanwhile, if NIC requires any sort of real security on their websites, tell them to hire me — my contact details are on the right.</p>

<p>Special thanks to Aayush Bhasin, Aditya Garg, Tanay Rajoria, Divyansh Sharma, Bhuvesh Mehendiratta.</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js"></script>

<script src="/blog/static/cbse-2016/all.js"></script>

<script>  
  var toHundred = [];
  for ( var i = 0; i <= 100; i++ ) {
    toHundred.push(i);
  }

  function getColor (i) {
    i = i || 0
    var colors = [
      '26, 188, 156,',
      '46, 204, 113,',
      '52, 152, 219,',
      '155, 89, 182,',
      '52, 73, 94,',
      '241, 196, 15,',
      '230, 126, 34,',
      '231, 76, 60,'
    ]

    return colors[i % colors.length]
  }

  function slugify (word) {
    return word.toLowerCase().replace(/ /g, '-')
  }

  function capitalCase (word) {
    var words = word.split(' ');
    words.forEach(function(word, idx) {
      words[idx] = word[0].toUpperCase() + word.substr(1).toLowerCase();
    });
    return words.join(' ')
  }

  function getTotal (distri) {
    var sum = 0;
    distri.forEach(function(item) {
      sum += item;
    })
    return sum;
  }

  var j = 0;
  for ( var subject in all ) {
    if (  !all.hasOwnProperty(subject) ) continue;
    var distribution = all[subject];
    var total = getTotal(distribution)
    if ( total < 10 ) continue;

    var ctx = document.createElement('canvas')
    ctx.setAttribute('id', slugify(subject))

    var title = document.createElement('h3')
    title.innerHTML = capitalCase(subject) + ' &mdash; Total: ' + total

    document.querySelector('.post').appendChild(title)
    document.querySelector('.post').appendChild(ctx)

    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: toHundred,
        datasets: [
          {
            label: capitalCase(subject) + ' | Number of students' ,
            backgroundColor: 'rgba('+ getColor(j) +'.6)',
            hoverBackgroundColor: 'rgba('+ getColor(j) +'.8)',
            data: distribution
          }
        ]
      },
      options: {
        scales: {
          xAxes: [{
            barPercentage: 1,
            categoryPercentage: 1,
            ticks: {
              fontSize: 12,
              maxTicksLimit: 25,
              maxRotation: 0,
              stepSize: 5
            }
          }],
          yAxes: [{
            // display: false
            stepSize: 1,
            ticks: {
              userCallback: function(value, index, values) {
                return (value/total * 100).toFixed(0) + '%'
              }
            }
          }]
        }
      }
    })
    j++;
  }
</script>

<p><a href="https://www.facebook.com/namanyayg">Catch me on Facebook</a></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[I recently presented you with a frequency distribution of CBSE board marks for DPS Vasant Kunj, and now I’m back with an analysis of 4052 CBSE schools across India, with results of over 476898 students.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">CBSE 2016 Analysis of DPS Vasant Kunj</title>
      <link href="https://nmn.gl/blog/cbse-2016-analysis-of-dps-vasant-kunj" rel="alternate" type="text/html" title="CBSE 2016 Analysis of DPS Vasant Kunj" />
      <published>2016-05-24T07:05:25+00:00</published>
      <updated>2016-05-24T07:05:25+00:00</updated>
      <id>https://nmn.gl/blog/cbse-2016-analysis-of-dps-vasant-kunj</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/cbse-2016-analysis-of-dps-vasant-kunj"><![CDATA[<p>Update: Check out the <a href="https://nmn.gl/blog/cbse-2016-all-india-data-analysis/">analysis done for 4L students across India</a>.</p>

<p>Our school has published a <a href="http://dpsvasantkunj.com/images/pdf/result/Mdetail-result2015-16.pdf">pretty exhaustive analysis</a>, but I decided to make something more prettier and with more statistics.</p>

<p>I’ve excluded subjects that have less than 10 students.</p>

<p>Many interesting conclusions can be drawn – I’ll leave those up to you.</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js"></script>

<script src="/blog/static/cbse-2016/dpsvk.js"></script>

<script>  
  var toHundred = [];
  for ( var i = 0; i <= 100; i++ ) {
    toHundred.push(i);
  }

  function getColor (i) {
    i = i || 0
    var colors = [
      '26, 188, 156,',
      '46, 204, 113,',
      '52, 152, 219,',
      '155, 89, 182,',
      '52, 73, 94,',
      '241, 196, 15,',
      '230, 126, 34,',
      '231, 76, 60,'
    ]

    return colors[i % colors.length]
  }

  function slugify (word) {
    return word.toLowerCase().replace(/ /g, '-')
  }

  function capitalCase (word) {
    var words = word.split(' ');
    words.forEach(function(word, idx) {
      words[idx] = word[0].toUpperCase() + word.substr(1).toLowerCase();
    });
    return words.join(' ')
  }

  function getTotal (distri) {
    var sum = 0;
    distri.forEach(function(item) {
      sum += item;
    })
    return sum;
  }

  var j = 0;
  for ( var subject in dpsvk ) {
    if (  !dpsvk.hasOwnProperty(subject) ) continue;
    var distribution = dpsvk[subject];
    var total = getTotal(distribution)
    if ( total < 10 ) continue;

    var ctx = document.createElement('canvas')
    ctx.setAttribute('id', slugify(subject))

    var title = document.createElement('h3')
    title.innerHTML = capitalCase(subject) + ' &mdash; Total: ' + total

    document.querySelector('.post').appendChild(title)
    document.querySelector('.post').appendChild(ctx)

    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: toHundred,
        datasets: [
          {
            label: capitalCase(subject) + ' | Number of students' ,
            backgroundColor: 'rgba('+ getColor(j) +'.6)',
            hoverBackgroundColor: 'rgba('+ getColor(j) +'.8)',
            data: distribution
          }
        ]
      },
      options: {
        scales: {
          xAxes: [{
            barPercentage: 1,
            categoryPercentage: 1,
            ticks: {
              fontSize: 12,
              maxTicksLimit: 25,
              maxRotation: 0,
              stepSize: 5
            }
          }],
          yAxes: [{
            // display: false
            stepSize: 1,
            ticks: {
              userCallback: function(value, index, values) {
                return (value/total * 100).toFixed(0) + '%'
              }
            }
          }]
        }
      }
    })
    j++;
  }
</script>

<p><a href="https://www.facebook.com/namanyayg">Catch me on Facebook</a></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[Update: Check out the analysis done for 4L students across India.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Simple image uploads in Meteor</title>
      <link href="https://nmn.gl/blog/simple-image-uploads-in-meteor" rel="alternate" type="text/html" title="Simple image uploads in Meteor" />
      <published>2016-01-05T08:13:00+00:00</published>
      <updated>2016-01-05T08:13:00+00:00</updated>
      <id>https://nmn.gl/blog/simple-image-uploads-in-meteor</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/simple-image-uploads-in-meteor"><![CDATA[<p>While working on a simple <a href="http://yearbook.code-warriors.org/">online yearbook</a> for my high school class of ‘16 in <a href="https://www.meteor.com/">Meteor</a>, I ran into the issue of uploading images to Meteor. Turns out, it’s not uncomplicated at all.</p>

<p>I messed around with <a href="https://github.com/CollectionFS/Meteor-CollectionFS">CollectionFS</a>, but unfortunately had the issue of images not loading and returning 503s, or taking a <em>long</em> while to load.</p>

<p>I decided to turn to the most popular and developer-friendly image host I know: <a href="http://imgur.com/">Imgur</a>.</p>

<hr />

<p>I used the <a href="https://atmospherejs.com/simple/imgur">simple:imgur</a> package. The <code class="language-plaintext highlighter-rouge">upload()</code> function takes two arguments, <code class="language-plaintext highlighter-rouge">options</code> and <code class="language-plaintext highlighter-rouge">callback</code>.</p>

<p>The options require an <code class="language-plaintext highlighter-rouge">apiKey</code>, the Imgur Client ID and <code class="language-plaintext highlighter-rouge">image</code>, the base64-encoded image data string. The <code class="language-plaintext highlighter-rouge">callback</code> function receives two arguments, the first being a <code class="language-plaintext highlighter-rouge">Meteor.Error</code> object and the latter being an object containing the response from Imgur.</p>

<h2 id="apikey-registering-an-imgur-application"><code class="language-plaintext highlighter-rouge">apiKey</code>: Registering an Imgur Application</h2>

<p>The first step is to get an <code class="language-plaintext highlighter-rouge">apiKey</code> by registering your application at <a href="https://api.imgur.com/oauth2/addclient">Imgur’s OAuth 2 page</a>. We need to choose ‘OAuth 2 authorization without a callback URL’, and once done, get the client ID (You’ll get an email about it, too).</p>

<h2 id="image-converting-file-to-base-64"><code class="language-plaintext highlighter-rouge">image</code>: Converting file to base 64</h2>

<p>In the <code class="language-plaintext highlighter-rouge">submit</code> event of my upload form, I add a line to get the file that the user uploaded:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">file</span> <span class="o">=</span> <span class="nx">$form</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="dl">'</span><span class="s1">input[type="file"]</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>  
</code></pre></div></div>

<p>I also check if the file is an image or not:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span> <span class="nx">file</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">file</span><span class="p">.</span><span class="nx">type</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="dl">'</span><span class="s1">image.*</span><span class="dl">'</span><span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="nx">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Upload must be an image</span><span class="dl">'</span><span class="p">)</span>  
</code></pre></div></div>

<p>We’re now going to use the <a href="https://developer.mozilla.org/en/docs/Web/API/FileReader"><code class="language-plaintext highlighter-rouge">FileReader</code> API</a> to get the base-64 representation of the image.</p>

<p>We’ll need to create a new reader and add a function to it’s <code class="language-plaintext highlighter-rouge">onload</code> event, where we handle the image upload logic.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">reader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">()</span>

<span class="nx">reader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>  
  <span class="c1">// e.target.result holds the file's text</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We’re going to convert the text into a <code class="language-plaintext highlighter-rouge">Uint8Array</code>, convert it to a string, and then finally use <code class="language-plaintext highlighter-rouge">btoa()</code> to convert the string to base-64 encoded ASCII string.</p>

<p>I directly do this in a convoluted one liner and a custom function inside an <code class="language-plaintext highlighter-rouge">options</code> object I created to call <code class="language-plaintext highlighter-rouge">Imgur.upload()</code>.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>  
  <span class="na">apiKey</span><span class="p">:</span> <span class="dl">'</span><span class="s1">XXXXXXXXXXXXXXX</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">image</span><span class="p">:</span> <span class="nx">btoa</span><span class="p">(</span><span class="nx">uint8ToString</span><span class="p">(</span><span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">)))</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">uint8ToString</code> function is simple: it converts the Unicode values we get from the <code class="language-plaintext highlighter-rouge">Uint8Array</code> representation of <code class="language-plaintext highlighter-rouge">e.target.result</code> into ASCII strings, which can be converted to base-64 easily.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">uint8ToString</span><span class="p">(</span><span class="nx">buffer</span><span class="p">)</span> <span class="p">{</span>  
  <span class="kd">var</span> <span class="nx">length</span> <span class="o">=</span> <span class="nx">buffer</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span> <span class="nx">str</span> <span class="o">=</span> <span class="dl">''</span>

  <span class="k">for</span> <span class="p">(</span> <span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span>
    <span class="nx">str</span> <span class="o">+=</span> <span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span> <span class="nx">buffer</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">)</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">str</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And with this, we’re able to create our <code class="language-plaintext highlighter-rouge">options</code> object easily.</p>

<h2 id="uploading">Uploading</h2>

<p>I created a <code class="language-plaintext highlighter-rouge">data</code> object to handle all data entered in the form by our user. In the <code class="language-plaintext highlighter-rouge">Imgur.upload()</code> function, I add to it.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Imgur</span><span class="p">.</span><span class="nx">upload</span><span class="p">(</span><span class="nx">options</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">errMsg</span><span class="p">,</span> <span class="nx">imgurData</span><span class="p">)</span> <span class="p">{</span>  
  <span class="k">if</span> <span class="p">(</span> <span class="nx">errMsg</span> <span class="p">)</span> <span class="k">return</span> <span class="nx">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">File upload failed. Please upload an image of a smaller file size</span><span class="dl">'</span><span class="p">)</span>
    <span class="kd">var</span> <span class="nx">imgData</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">link</span><span class="p">:</span> <span class="nx">imgurData</span><span class="p">.</span><span class="nx">link</span><span class="p">,</span>
      <span class="na">deletehash</span><span class="p">:</span> <span class="nx">imgurData</span><span class="p">.</span><span class="nx">deletehash</span>
    <span class="p">}</span>
    <span class="nx">data</span><span class="p">.</span><span class="nx">photo</span> <span class="o">=</span> <span class="nx">imgData</span>
  <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I only store the two important parts of the response: the <code class="language-plaintext highlighter-rouge">link</code> to the file and the <code class="language-plaintext highlighter-rouge">deletehash</code>. I can easily show the file by using <code class="language-plaintext highlighter-rouge">&lt;img src=""&gt;</code>.</p>

<p>And we’re done!</p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      
        <category term="meteor" />
      
        <category term="personal-project" />
      

      
      
        <summary type="html"><![CDATA[While working on a simple online yearbook for my high school class of ‘16 in Meteor, I ran into the issue of uploading images to Meteor. Turns out, it’s not uncomplicated at all.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">3D In 2D Canvas</title>
      <link href="https://nmn.gl/blog/3d-in-2d-canvas" rel="alternate" type="text/html" title="3D In 2D Canvas" />
      <published>2016-01-04T13:06:09+00:00</published>
      <updated>2016-01-04T13:06:09+00:00</updated>
      <id>https://nmn.gl/blog/3d-in-2d-canvas</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/3d-in-2d-canvas"><![CDATA[<p>In my recent <a href="http://projects.namanyayg.com/acgen">simulation of an AC generator</a>, I show the same device from two different views: A top view and a front view. To accomplish that, I used a clever technique called <strong>3D Projection</strong>. Here, I’m going to talk about how I did that in JavaScript and rendered it on canvas.</p>

<h2 id="what-is-3d-projection">What is 3D Projection?</h2>

<p>Basically, it means that I <em>define</em> items in 3D space (each point is defined as an array <code class="language-plaintext highlighter-rouge">[x, y, z]</code>) and that I <em>plot</em> them in 2D space. For simplification, the 2D render can only be seen in the <code class="language-plaintext highlighter-rouge">xy</code>, <code class="language-plaintext highlighter-rouge">xz</code>, or <code class="language-plaintext highlighter-rouge">yz</code> plane.</p>

<h2 id="defining-in-3d">Defining in 3D</h2>

<p>Each point is defined as an array, [x, y, z].</p>

<p>Since we are using Canvas, it is easiest to define a rectangular face as a 2D array of 5 points: The first vertex, the remaining 3 vertices, and then the initial vertex again. This allows us to <code class="language-plaintext highlighter-rouge">moveTo()</code> the first index and then <code class="language-plaintext highlighter-rouge">lineTo()</code> till the remaining length of the array.</p>

<p>Since faces exist in 2D, and we’re defining 3 coordinates per point, for a single face, either the x, y, or z, coordinate will remain constant for all points.</p>

<p>A cuboid is defined a 3D array of faces. Depending on our views, we might not need all 6 faces to define a cuboid and can get away with only two or three faces.</p>

<p>Example:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">cuboid</span> <span class="o">=</span> <span class="p">[</span>  
  <span class="c1">// xy face</span>
  <span class="p">[</span>
    <span class="p">[</span><span class="nx">xa1</span><span class="p">,</span> <span class="nx">ya1</span><span class="p">,</span> <span class="nx">za</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xa2</span><span class="p">,</span> <span class="nx">ya2</span><span class="p">,</span> <span class="nx">za</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xa3</span><span class="p">,</span> <span class="nx">ya3</span><span class="p">,</span> <span class="nx">za</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xa4</span><span class="p">,</span> <span class="nx">ya4</span><span class="p">,</span> <span class="nx">za</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xa5</span><span class="p">,</span> <span class="nx">ya5</span><span class="p">,</span> <span class="nx">za</span><span class="p">],</span>
  <span class="p">],</span>
  <span class="c1">// xz face</span>
  <span class="p">[</span>
    <span class="p">[</span><span class="nx">xb1</span><span class="p">,</span> <span class="nx">yb</span><span class="p">,</span> <span class="nx">zb1</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xb2</span><span class="p">,</span> <span class="nx">yb</span><span class="p">,</span> <span class="nx">zb2</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xb3</span><span class="p">,</span> <span class="nx">yb</span><span class="p">,</span> <span class="nx">zb3</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xb4</span><span class="p">,</span> <span class="nx">yb</span><span class="p">,</span> <span class="nx">zb4</span><span class="p">],</span>
    <span class="p">[</span><span class="nx">xb5</span><span class="p">,</span> <span class="nx">yb</span><span class="p">,</span> <span class="nx">zb5</span><span class="p">],</span>
  <span class="p">],</span>
  <span class="p">...</span>
<span class="p">]</span>
</code></pre></div></div>

<h2 id="rendering-in-2d">Rendering in 2D.</h2>

<p>Since we’re defining faces parallel to the primary planes, it is easiest to render the views of the primary planes themselves.</p>

<p>Imagine a shape on the <code class="language-plaintext highlighter-rouge">xy</code> plane. All points that define it can be written as <code class="language-plaintext highlighter-rouge">(xi, yi, 0)</code>. Similarly, any shape on the <code class="language-plaintext highlighter-rouge">xz</code> plane defines all points as <code class="language-plaintext highlighter-rouge">(xi, 0, zi)</code>. Basically, whichever axis you’re <em>not</em> rendering the shape on is 0.</p>

<p>This makes things easy for us.</p>

<p>Creating a function called <code class="language-plaintext highlighter-rouge">plotFace</code>, which takes three parameters:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">face</code>: The 2D array of points</li>
  <li><code class="language-plaintext highlighter-rouge">path</code>: The path to plot the shape on</li>
  <li><code class="language-plaintext highlighter-rouge">a</code>: The first axis of the plane</li>
  <li><code class="language-plaintext highlighter-rouge">b</code>: The second axis of the plane</li>
</ul>

<p>Since in our arrays, the 0 index represents x and so on, we can simplify the function if <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code> are directly passed as integers.</p>

<p>The plotFace function basically <code class="language-plaintext highlighter-rouge">movesTo</code> the initial point and <code class="language-plaintext highlighter-rouge">linesTo</code> the remaining ones.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">plotFace</span><span class="p">(</span><span class="nx">face</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>  
  <span class="kd">var</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">face</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
  <span class="nx">path</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span> <span class="nx">face</span><span class="p">[</span><span class="nx">a</span><span class="p">],</span> <span class="nx">face</span><span class="p">[</span><span class="nx">b</span><span class="p">]</span> <span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span> <span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">point</span> <span class="o">=</span> <span class="nx">face</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
    <span class="nx">path</span><span class="p">.</span><span class="nx">lineTo</span><span class="p">(</span> <span class="nx">point</span><span class="p">[</span><span class="nx">a</span><span class="p">],</span> <span class="nx">point</span><span class="p">[</span><span class="nx">b</span><span class="p">]</span> <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A simple <code class="language-plaintext highlighter-rouge">map</code> through all faces and using <code class="language-plaintext highlighter-rouge">plotFace</code> on each can plot the entire shape to a single path.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">path</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Path2D</span><span class="p">();</span>  
<span class="nx">ctx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>  
<span class="nx">item</span><span class="p">.</span><span class="nx">faces</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">face</span><span class="p">)</span> <span class="p">{</span>  
  <span class="nx">plotFace</span><span class="p">(</span><span class="nx">face</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">);</span>
<span class="p">})</span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">closePath</span><span class="p">();</span>  
</code></pre></div></div>

<p>Finally, rendering the context using <code class="language-plaintext highlighter-rouge">fill()</code> will get our shape.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ctx</span><span class="p">.</span><span class="nx">fill</span><span class="p">(</span><span class="nx">path</span><span class="p">);</span>  
</code></pre></div></div>

<p>…And we’re done!</p>

<h2 id="whats-next">What’s next</h2>

<p>Currently, we’re only projecting in the <code class="language-plaintext highlighter-rouge">xy</code>, <code class="language-plaintext highlighter-rouge">xz</code>, or <code class="language-plaintext highlighter-rouge">xy</code> plane. Next would be to be able to project in <em>any</em> arbitrary plane. I have yet to figure out the math for it, and I’m trying to do it without external help, so it might be a while before I publish a new article.</p>

<p>After that, perhaps manually raycasting to create shadows? That could be interesting, both aesthetic and performance wise.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/3D_projection">3D Projection</a></li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[In my recent simulation of an AC generator, I show the same device from two different views: A top view and a front view. To accomplish that, I used a clever technique called 3D Projection. Here, I’m going to talk about how I did that in JavaScript and rendered it on canvas.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Two really cool Node MySQL tips</title>
      <link href="https://nmn.gl/blog/node-mysql-best-practices" rel="alternate" type="text/html" title="Two really cool Node MySQL tips" />
      <published>2015-08-20T05:54:17+00:00</published>
      <updated>2015-08-20T05:54:17+00:00</updated>
      <id>https://nmn.gl/blog/node-mysql-best-practices</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/node-mysql-best-practices"><![CDATA[<p>Node MySQL is a great traditional alternative to mongo and all the jazz youngins are using. One important advice – <strong>never use <code class="language-plaintext highlighter-rouge">+</code> to concatenate queries unless you know what you’re doing.</strong></p>

<h2 id="1-always-escape-using--as-placeholders">1. Always escape using <code class="language-plaintext highlighter-rouge">?</code> as placeholders</h2>

<p>Queries are usually written as:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">connection</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="dl">'</span><span class="s1">SELECT * FROM foo WHERE bar = baz</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>  
    <span class="c1">// ...</span>
<span class="p">});</span>

</code></pre></div></div>

<p>If you want to check against a custom property, <strong>don’t do this.</strong></p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">connection</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="dl">'</span><span class="s1">SELECT * FROM foo WHERE bar = </span><span class="dl">'</span> <span class="o">+</span> <span class="nx">someVariable</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>  
    <span class="c1">// ...</span>
<span class="p">});</span>

</code></pre></div></div>

<p>Instead,</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">connection</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="dl">'</span><span class="s1">SELECT * FROM foo WHERE bar = ?</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="nx">someVariable</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>  
    <span class="c1">// ...</span>
<span class="p">});</span>

</code></pre></div></div>

<p>You can use multiple <code class="language-plaintext highlighter-rouge">?</code> like so:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">connection</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="dl">'</span><span class="s1">SELECT * FROM foo WHERE ? = ?</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="nx">someProperty</span><span class="p">,</span> <span class="nx">someValue</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>  
    <span class="c1">// ...</span>
<span class="p">});</span>

</code></pre></div></div>

<h2 id="2-use-the-set--syntax">2. Use the <code class="language-plaintext highlighter-rouge">SET ?</code> syntax</h2>

<p>Node MySQL converts objects from <code class="language-plaintext highlighter-rouge">{ a: 'b' }</code> to <code class="language-plaintext highlighter-rouge">a = 'b'</code> when escaped. Insertions with objects is thus easy:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Namanyay Goel</span><span class="dl">"</span> <span class="p">};</span>  
<span class="nx">connection</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="dl">'</span><span class="s1">INSERT INTO users SET ?`, user, function(err, result) {  
    // ... 
}); 

</span></code></pre></div></div>

<p><a href="http://stackoverflow.com/questions/30147983/separate-keys-and-values-from-object">Then you never have to do this</a>…</p>

<hr />

<p><a href="https://github.com/felixge/node-mysql/#escaping-query-values">Learn more about Node MySQL’s escaping</a></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      
        <category term="mysql" />
      
        <category term="node" />
      
        <category term="tips" />
      

      
      
        <summary type="html"><![CDATA[Node MySQL is a great traditional alternative to mongo and all the jazz youngins are using. One important advice – never use + to concatenate queries unless you know what you’re doing.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Jade locals with Gulp</title>
      <link href="https://nmn.gl/blog/jade-locals-with-gulp" rel="alternate" type="text/html" title="Jade locals with Gulp" />
      <published>2014-07-26T08:57:16+00:00</published>
      <updated>2014-07-26T08:57:16+00:00</updated>
      <id>https://nmn.gl/blog/jade-locals-with-gulp</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/jade-locals-with-gulp"><![CDATA[<p>One of the coolest features of Jade is the concept of <code class="language-plaintext highlighter-rouge">locals</code>: An object that can be passed to the compiler and used in the Jade code, allowing better separation of content and templates. Ideally, these locals are held in an external file.</p>

<p>After much tinkering, I figured something out:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">);</span>  
<span class="p">...</span>
<span class="p">.</span><span class="nx">pipe</span><span class="p">(</span> <span class="nx">p</span><span class="p">.</span><span class="nx">jade</span><span class="p">({</span> 
    <span class="na">pretty</span><span class="p">:</span> <span class="nx">uglyLevel</span><span class="p">,</span>
    <span class="na">data</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">'</span><span class="s1">src/data.js</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span> <span class="p">})</span> <span class="p">)</span>
<span class="p">})</span> <span class="p">)</span>

</code></pre></div></div>

<h2 id="what">…What?</h2>

<ul>
  <li><a href="https://github.com/phated/gulp-jade#options">Gulp Jade’s docs</a> show that the <code class="language-plaintext highlighter-rouge">data</code> or <code class="language-plaintext highlighter-rouge">locals</code> option to could be used to pass in a single object holding all the external data.</li>
  <li><a href="http://nodejs.org/api/fs.html">File I/O</a>, or <code class="language-plaintext highlighter-rouge">fs</code> is node’s way of reading files. Using <a href="http://nodejs.org/api/fs.html#fs_fs_readfilesync_filename_options">fs.readFileSync</a>, I used a <code class="language-plaintext highlighter-rouge">JSON</code> file to hold all the data.</li>
  <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse">JSON.parse()</a> is a native JS method to convert a string (The output of <code class="language-plaintext highlighter-rouge">fs.readFileSync</code> with <code class="language-plaintext highlighter-rouge">utf8</code> encoding) to JSON.</li>
</ul>

<p>Combining the three resulted in the above one liner, allowing me to use a <code class="language-plaintext highlighter-rouge">data.js</code> file to host all raw data and use loops to better template the code within. Win!</p>

<p>PS: <a href="https://nmn.gl/blog/debug-mode-in-gulp/">If you’re wondering what the <code class="language-plaintext highlighter-rouge">uglyLevel</code> bit is…</a></p>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      

      
      
        <summary type="html"><![CDATA[One of the coolest features of Jade is the concept of locals: An object that can be passed to the compiler and used in the Jade code, allowing better separation of content and templates. Ideally, these locals are held in an external file.]]></summary>
      

      
      

      
    </entry>
  
    <entry>
      

      <title type="html">Images and excerpts – A few practical problems with Ghost</title>
      <link href="https://nmn.gl/blog/images-and-excerpts-a-few-practical-problems-with-ghost" rel="alternate" type="text/html" title="Images and excerpts – A few practical problems with Ghost" />
      <published>2014-05-28T02:32:00+00:00</published>
      <updated>2014-05-28T02:32:00+00:00</updated>
      <id>https://nmn.gl/blog/images-and-excerpts-a-few-practical-problems-with-ghost</id>
      
      
        <content type="html" xml:base="https://nmn.gl/blog/images-and-excerpts-a-few-practical-problems-with-ghost"><![CDATA[<p>Ghost is awesome, it really is! I’ve just started using and developing on it, but I love it already. It’s simple, smooth, and <em>fast</em>. You can <em>feel</em> the speed when you compare it to traditional CMS’ like WordPress or static generators like Jekyll – I find it to triumph both.</p>

<p>Development is pretty damn easy too. Installing Ghost on Windows was a breeze, and starting development even easier. I fired up Prepros, creating a <code class="language-plaintext highlighter-rouge">SCSS</code> file for better CSS, and started coding!</p>

<p>Ghost’s writer is it’s biggest advantage, though. Markdown is great to write, and the side-by-side compilation makes writing so much more fun.</p>

<p>However, there are indeed a few practical problems with Ghost that you may encounter soon in one of your projects. I’m going to talk about these here, along with some hacky solutions for them.</p>

<ul>
  <li>Images can’t use figure/figcaption: Currently, images on Ghost are simple <code class="language-plaintext highlighter-rouge">&lt;img&gt;</code> tags in paragraphs. I was looking around for image captioning and using figure/figcaption there, but with little results. A <a href="https://ghost.org/forum/bugs-suggestions/601-suggestion-image-video-captioning/">workaround by Lee Lam</a> could be a quick solution, though.</li>
</ul>

<p>This is a problem both of Markdown (Which does not seem to support two types of captions, <em>i.e.</em> one for the <code class="language-plaintext highlighter-rouge">alt</code> attribute and other a standard caption) and Ghost.</p>

<p>The <a href="https://github.com/TryGhost/Ghost/issues/985">issue</a> is set to <strong>won’t fix</strong> until the <a href="https://github.com/TryGhost/Ghost/wiki/Haunted-Markdown">Haunted Markdown parser</a> is implemented.</p>

<ul>
  <li>No support for advanced excerpts: With WordPress, you could simple add a <code class="language-plaintext highlighter-rouge">&lt;!-- more --&gt;</code> somewhere and it handled excerpts with read more for you. Unfortuantely, this isn’t the case with Ghost and by default you see a paragraph of plaintext with a trailing …. Not something particularly beautiful.</li>
</ul>

<p><a href="https://ghost.org/forum/using-ghost/1287-controlling-excerpts/4/">Kraftner on Ghost Forums</a> gives a great solution to that problem. Using <code class="language-plaintext highlighter-rouge">{{ content }}</code> instead of <code class="language-plaintext highlighter-rouge">{{ excerpt }}</code> allows you to output HTML instead of plaintext, and combining that with some clever CSS rules displays only one paragraph. I use a similar trick at <a href="http://tldrte.ch/">TLDRtech</a> where all <code class="language-plaintext highlighter-rouge">ul</code>s are hidden in the ‘excerpt’.</p>

<p>My goal with the post was to highlight some of the common issues, and give <del>hacky</del> solutions for them. That said, I do love Ghost for many, many reasons</p>

<ul>
  <li>Ghost is fast. You literally feel the difference on the admin panel of Ghost compared to WordPress’</li>
  <li>Installing Ghost is a breeze. It took me less than 2 minutes to install Ghost on my Windows computer. Granted, installing it on Apache is a bit more difficult, but <a href="http://www.tfq.me/how-to-host-multiple-ghost-blogs-on-ubuntu-12-04-using-apache/">there are good guides for that</a>.</li>
  <li>Theme development on Ghost is fun. Handlebars is fun to write, and I’ve set up SCSS compilation with Prepos as well.</li>
</ul>]]></content>
      

      
      
      
      
      

      <author>
          <name>namanyayg</name>
        
        
      </author>

      

      
        
          <category term="Uncategorized" />
        
      

      
        <category term="blogging" />
      
        <category term="ghost" />
      

      
      
        <summary type="html"><![CDATA[Ghost is awesome, it really is! I’ve just started using and developing on it, but I love it already. It’s simple, smooth, and fast. You can feel the speed when you compare it to traditional CMS’ like WordPress or static generators like Jekyll – I find it to triumph both.]]></summary>
      

      
      

      
    </entry>
  
</feed> 
