<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Chuma Kabaghe]]></title><description><![CDATA[Chuma Kabaghe]]></description><link>https://chuma.blog</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 10:56:39 GMT</lastBuildDate><atom:link href="https://chuma.blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Use Claude Code Every Day]]></title><description><![CDATA[I've been using Claude Code as my primary development tool for months now. I joke that my job is essentially managing Claude Codes, but it's not far from the truth.
Understanding Code with Claude
Claude Code helps me write code and acts as a reasonin...]]></description><link>https://chuma.blog/how-i-use-claude-code-every-day</link><guid isPermaLink="true">https://chuma.blog/how-i-use-claude-code-every-day</guid><category><![CDATA[claude-code]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Fri, 13 Feb 2026 18:55:27 GMT</pubDate><content:encoded><![CDATA[<p>I've been using Claude Code as my primary development tool for months now. I joke that my job is essentially managing Claude Codes, but it's not far from the truth.</p>
<h2 id="heading-understanding-code-with-claude">Understanding Code with Claude</h2>
<p>Claude Code helps me write code and acts as a reasoning partner for understanding complex systems. When working in unfamiliar code, I start by asking Claude Code to explain what the code does and how it works, including diagrams where helpful.</p>
<p>As I get a better understanding of the code, I have Claude trace specific scenarios through the codebase: "What happens if this config option is set but the feature flag is disabled?" It walks through the state transitions, surfaces edge cases I wouldn't have thought to check, and I end up with a behavior matrix I can turn into tests.</p>
<h2 id="heading-git-worktrees-for-parallel-work"><strong>Git Worktrees for Parallel Work</strong></h2>
<p>This is the workflow change I'd recommend to anyone. Instead of constantly stashing and switching branches, I use git worktrees, so each branch lives in its own directory. Claude Code operates in whichever worktree I point it at.</p>
<p>The real value comes with stacked PRs. I work in chains of dependent PRs, a bug fix, then a feature built on top of it, then gating logic on top of that. When an earlier PR merges, I rebase the downstream ones.</p>
<h2 id="heading-some-specific-patterns-i-use-daily">Some specific patterns I use daily:</h2>
<p><strong>Conflict resolution.</strong> Conflicts are inevitable with rebasing stacked PRs. I tell Claude exactly how to resolve them: "Keep my changes for the test files, accept theirs for the generated files." It handles the conflicts without me re-reading every single one.</p>
<p><strong>Branch verification.</strong> Before Claude Code does anything significant, I check what branch we’re working in. In a multi-worktree setup, this has saved me more than once from changes ending up in the wrong place. I have the branch name as part of my terminal prompt for easy reference.</p>
<p><strong>ITerm tabs:</strong> Use them! I have a tab for each active Claude Code session. I use a column layout so I can quickly scan what I’m working on and which session needs my attention.</p>
<p>My iTerm setup with tabs for each active claude code session</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771009049371/2711bfa9-e261-44af-985e-056f8dcb1a42.png" alt class="image--center mx-auto" /></p>
<p><strong>MCPs are your friend:</strong> I use the datadog mcp to query logs, the Slack mcp to ping me in Slack when Claude Code needs my attention, the Playwright mcp to test front end changes, and more.</p>
<p><strong>Skills:</strong> Claude.mds are good but skills are a game-changer when it comes to processes you repeat often. I have a skill that hunts for bugs in my code, one that cleans up stale local branches and worktrees, <a target="_blank" href="https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-review">the code review plugin</a>, and my new favorite <a target="_blank" href="https://github.com/anthropics/claude-plugins-official/tree/main/plugins/frontend-design/skills/frontend-design">frontend design skill</a>. I appreciate being able to spin up nice looking UIs with minimal effort.</p>
<p><strong>Claude:</strong> When in doubt ask Claude! Claude knows how to use Claude Code and what tools and skills it has available. It has the history of the session. If you have way too many active sessions and forget which is which, just ask Claude!</p>
<p><strong>Claude code on the web/ in Slack</strong>: When I'm on the go and remember something I need implemented or someone reports a bug in Slack, I kick off a remote Claude Code session from either Slack or the web to implement my random idea or fix the bug and create the PR from the web UI.</p>
<h2 id="heading-write-test-review-iterate"><strong>Write, Test, Review, Iterate</strong></h2>
<p>My development loop with Claude Code is:</p>
<p><strong>Write.</strong> I describe what I want with specifics, not "fix this" but "This function should add disabled items to the blocklist, but it's silently dropping them instead. Please fix." The more precise the problem statement, the fewer iterations needed. Steer Claude and reject its proposed edits if they are incorrect. You're still the engineer in charge. Claude is really good, but in many cases you have more context than it does.</p>
<p><strong>Test.</strong> I start by asking what tests already exist and what they cover. Then I describe the behavior I want to verify and let Claude write the test. For complex code, that usually means a matrix of inputs and expected outcomes. I describe the expectations and Claude generates the full test suite.</p>
<p><strong>Review.</strong> I have Claude self-review before pushing. I prompt "Use the code review skill on the code in this branch." It then invokes my code review skill and catches nil handling gaps, comment/code mismatches, test assertion contradictions, regressions etc. Once it’s addressed all issues, I review the code.</p>
<p><strong>Iterate.</strong> I make updates to the skills and docs as needed, so Claude can do better the next time. More often than not, I make these updates by telling Claude to reflect on what we did and update the skills/ docs accordingly.</p>
<p>The models are improving fast enough that my workflow from three months ago already feels dated. What used to require careful hand-holding now just works. With Opus 4.5 &amp; 4.6 I've been delegating larger chunks of work to Claude Code and letting it autonomously drive the full development cycle. Stay curious, keep experimenting, and be ready to let go of more than feels comfortable.</p>
]]></content:encoded></item><item><title><![CDATA[Why Codebases Don't Need RAG]]></title><description><![CDATA[[All opinions are my own]
I've been using Claude Code a lot lately, and I continue to be both amused and impressed at how well its grep-based agentic search works. It seemed counterintuitive to me at first. RAG (Retrieval-Augmented Generation) is oft...]]></description><link>https://chuma.blog/why-codebases-dont-need-rag</link><guid isPermaLink="true">https://chuma.blog/why-codebases-dont-need-rag</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[claude-code]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Fri, 07 Nov 2025 19:00:36 GMT</pubDate><content:encoded><![CDATA[<p>[All opinions are my own]</p>
<p>I've been using <a target="_blank" href="https://claude.com/product/claude-code">Claude Code</a> a lot lately, and I continue to be both amused and impressed at how well its grep-based agentic search works. It seemed counterintuitive to me at first. RAG (Retrieval-Augmented Generation) is often talked about as the standard for AI knowledge retrieval. So why was this simple grep-based approach working so well?</p>
<p>The thing I didn't appreciate until recently is that a codebase is technically a giant DAG, where nodes represent a file, class, module, or library, and directed edges represent a dependency (e.g., file A imports a function from file B, so A depends on B). Having that built-in graphical structure makes them functionally equivalent to knowledge graphs for the purpose of code navigation and retrieval… even if the relationships are simpler and more limited in type.</p>
<p>By searching through the files, reading import statements and function implementations, Claude is able to "understand" the relationships in the codebase and pull the relevant snippets and files into its context.</p>
<h3 id="heading-the-difference-with-documents">The Difference with Documents</h3>
<p>Traditional text files or documents don't inherently have this graphical structure. You may get some of it when one document references another, but most documents don't.</p>
<p>Without that graph structure, an AI agent has three options: read all file content into context to understand relationships (bloats the context window and is slow), build a knowledge graph on top of the files (notoriously hard to maintain and keep updated), or use RAG.</p>
<h3 id="heading-tldr">TLDR</h3>
<p>Code already encodes its relationships in a graph. Imports, function definitions, dependencies... it's all there, explicit and parseable. When that structure exists, the agent can just follow it… much like how humans do when exploring a code base. No indexing overhead, no staleness when code changes, no security concerns about where an index lives.</p>
<p>For unstructured content like documents where relationships aren't explicitly encoded, RAG absolutely makes sense. But for code? The graph is already there.</p>
]]></content:encoded></item><item><title><![CDATA[Finding the Right Puzzle: My Approach to the Job Search]]></title><description><![CDATA[I applied to five jobs. Just five.
Everyone told me I was doing it wrong. "You need to cast a wider net," they said. "It's a numbers game." But that approach has never worked for me. It's exhausting and inefficient.
About six weeks into my new job, I...]]></description><link>https://chuma.blog/finding-the-right-puzzle-my-approach-to-the-job-search</link><guid isPermaLink="true">https://chuma.blog/finding-the-right-puzzle-my-approach-to-the-job-search</guid><category><![CDATA[job search]]></category><category><![CDATA[Job Hunting]]></category><category><![CDATA[Job Interview]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Mon, 20 Oct 2025 02:02:29 GMT</pubDate><content:encoded><![CDATA[<p>I applied to five jobs. Just five.</p>
<p>Everyone told me I was doing it wrong. "You need to cast a wider net," they said. "It's a numbers game." But that approach has never worked for me. It's exhausting and inefficient.</p>
<p>About six weeks into my new job, I found myself reflecting on whether I'd made the right choice. I decided to grade myself against my own criteria from this <a target="_blank" href="https://chuma.blog/what-i-want-in-my-next-role-and-how-i-figured-it-out">post</a>:</p>
<ul>
<li><p><strong>Does this place have the steepest learning curve?</strong> Absolutely! So much to learn, so much value to add.</p>
</li>
<li><p><strong>Will I be surrounded by my people?</strong> Still early, but so far everyone I've worked with has been incredible at what they do with refreshingly low egos about it. Very high density of exceptional talent and just people I enjoy working with.</p>
</li>
<li><p><strong>Can I continue pushing the boundaries of what's possible with AI?</strong> Yes! The team I joined was my first choice when I was job searching particularly for this reason. We're at the intersection of product and research within a frontier AI lab that's focused on building AI safely and responsibly. I struggle to think of many other places that could check this box as well as my team does.</p>
</li>
</ul>
<p>I've now been at Anthropic, my top choice, for three months working on features like <a target="_blank" href="https://www.anthropic.com/news/create-files">file creation in Claude</a> and my grading still stands. The truth is, the whole process started long before I sent those five applications. </p>
<h2 id="heading-finding-the-right-puzzle"><strong>Finding the right puzzle</strong></h2>
<p>I'll caveat upfront that this approach may not be feasible for everyone, and I appreciate that I had the luxury of being able to take time off work to rest and then dedicate focused energy to my job search.</p>
<p>I view the job search as finding the right puzzle. I see myself as a single, perhaps quirky, piece. My shape is defined by my strengths, the value I bring, and the environments where I thrive (or, crucially, where I don't). My entire goal is to find the puzzle where my piece can truly fall into place, contributing to the incredible, developing picture it's revealing.</p>
<p>This meant quality over quantity. I applied to five jobs and gave each one the attention it deserved.</p>
<h2 id="heading-my-job-search-framework"><strong>My Job Search Framework</strong></h2>
<h3 id="heading-1-tap-into-your-network-strategically"><strong>1. Tap Into Your Network (Strategically)</strong></h3>
<p>I reached out to people I knew and trusted and asked them what companies or sectors they were excited about and what working at these places was like. This wasn't about asking for referrals right away. It was about gathering information about the companies and their cultures.</p>
<p>Once I narrowed down jobs and companies that aligned with what I was looking for, <em>then</em> I reached out to my network to see if they knew of opportunities that matched.</p>
<p>I also used <a target="_blank" href="https://trueup.io/">TrueUp</a> to get notifications for jobs I was interested in. I found it had the best signal-to-noise ratio for what I wanted.</p>
<h3 id="heading-2-journal-your-way-to-clarity"><strong>2. Journal Your Way to Clarity</strong></h3>
<p>This was probably the most valuable thing I did. Writing down and reflecting on my career helped me identify patterns. Identifying when I was happiest, what I enjoyed, what I valued etc. This clarity was especially important for Anthropic's culture interview, which requires you to have a deep understanding of your values and what's important to you.</p>
<p>I found that without this reflection work, I would have struggled to articulate why certain opportunities excited me and others didn't.</p>
<h3 id="heading-3-build-a-comprehensive-resume-then-customize"><strong>3. Build a Comprehensive Resume, Then Customize</strong></h3>
<p>I created a comprehensive resume braindump where I didn't care about page limits or formatting. I just wrote down all my past roles and everything I could remember that was significant. Then I used Claude (yes, really) to help me pick and choose what to showcase for each specific job.</p>
<p>I tend to get in the weeds and focus on <em>what</em> I did without tying it to <em>why</em> or the outcome/impact. Claude was incredibly helpful for reframing my accomplishments to highlight the right achievements for each role. This made a real difference in how I presented myself.</p>
<h3 id="heading-4-build-interview-momentum"><strong>4. Build Interview Momentum</strong></h3>
<p>I started by interviewing with companies I cared less about to get the nerves out of my system and get a feel for what kinds of questions were being asked. This was more valuable than I expected. Companies seemed to have more involved processes than I remembered, with a wide range of interviews: LeetCode-style coding, speed coding, debugging, OOP, architecture design, project walkthroughs. It was all over the place.</p>
<p>I focused on one company at a time so I could prepare for whatever wildcard interview they had in addition to the standard coding, system design, behavioral, and leadership questions.</p>
<p>Out of five applications: I got rejected from two, I rejected two, and I accepted Anthropic.</p>
<h3 id="heading-5-prep-prep-and-more-prep"><strong>5. Prep, Prep, and more Prep</strong></h3>
<p><strong>Coding prep:</strong> I did 100 questions on <a target="_blank" href="https://www.techinterviewhandbook.org/grind75/">Grind75</a> (39 easy, 60 medium, 1 hard) because 100 felt like a good place to stop. But when I first started, I quickly realized I needed a refresher on LeetCode question patterns. I signed up for <a target="_blank" href="https://www.educative.io/">Educative.io</a> and worked through the coding portion of their <a target="_blank" href="https://www.educative.io/interview">personalized interview prep plan</a>. I highly recommend this. After going through the topics there, I was able to breeze through the Grind75 questions and felt much more confident about coding interviews.</p>
<p><strong>System design and storytelling:</strong> I used <a target="_blank" href="https://www.hellointerview.com/">HelloInterview</a>. I really loved the guided practice for system design interviews. Speaking and drawing my answers in a time-bounded environment felt more representative of actual interviews. I initially tried just watching YouTube videos, but they gave me a false sense of confidence. I thought I knew the material but struggled to reason through details under time pressure.</p>
<p>I highly recommend their <a target="_blank" href="https://www.hellointerview.com/learn/system-design/in-a-hurry/delivery">delivery framework</a>. It made it easier to focus on the core parts during interviews and helped interviewers follow my thought process. Their <a target="_blank" href="https://www.hellointerview.com/learn/behavioral/story-builder">story builder</a> was somewhat useful but a little clunky. What I appreciated was how it helped me narrow down to a few core stories and figure out how to piece them together to answer different questions and highlight my skills in a level-appropriate way.</p>
<h3 id="heading-6-track-everything-including-red-flags"><strong>6. Track Everything (Including Red Flags)</strong></h3>
<p>I kept a Google Doc where I logged information and outcomes from interviews with each company. If there were yellow flags, I noted them so I could get clarity during the interview process. If there were things I could improve upon in how I structured my answers, I noted those too so I could work on them for the next interview.</p>
<p>And if there were red flags, like the one interview where I walked away thinking "working with this person feels exhausting," I noted it and immediately pulled myself from the process. This might sound dramatic, but life's too short to ignore clear signals about culture fit.</p>
<h2 id="heading-reflections-on-the-journey"><strong>Reflections on the Journey</strong></h2>
<p>Looking back, the biggest lesson is this: know yourself, and trust that knowledge. The time I spent journaling and clarifying what I actually wanted wasn't wasted. It was the foundation that made everything else work.</p>
<p>The "quality over quantity" approach only works if you genuinely understand what quality means <em>for you</em>. For me, it meant a steep learning curve, exceptional people, and the ability to push boundaries in AI. For you, it might be something completely different.</p>
<p>I'm excited to have found the right job for me and am looking forward to the journey ahead. If you're in the middle of a job search right now, I hope some of these strategies are helpful. And if you've found approaches that worked well for you, I'd love to hear about them.</p>
]]></content:encoded></item><item><title><![CDATA[What I Want in My Next Role (And How I Figured It Out)]]></title><description><![CDATA[As I was transitioning from being a startup founder back into the job market, I felt this weird mix of being overwhelmed with options yet somehow underwhelmed at the same time. Coming from founding a startup after working at both early-stage companie...]]></description><link>https://chuma.blog/what-i-want-in-my-next-role-and-how-i-figured-it-out</link><guid isPermaLink="true">https://chuma.blog/what-i-want-in-my-next-role-and-how-i-figured-it-out</guid><category><![CDATA[Career]]></category><category><![CDATA[job search]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Sat, 05 Jul 2025 22:46:19 GMT</pubDate><content:encoded><![CDATA[<p>As I was transitioning from being a startup founder back into the job market, I felt this weird mix of being overwhelmed with options yet somehow underwhelmed at the same time. Coming from founding a startup after working at both early-stage companies and big tech, I knew this was the perfect moment to really figure out what direction I wanted my career to take next.</p>
<p>Should I stick with the very early-stage startup world I knew so well? Or was it time to try something more established? The current hiring environment wasn't exactly encouraging, but I decided to put on my founder hat and focus on what I could control: figuring out exactly what I wanted and how I'd go after it.</p>
<h2 id="heading-finding-clarity"><strong>Finding Clarity</strong></h2>
<p>I was having one of those meandering career conversations with my mentor when they dropped two pieces of advice that completely reframed how I was thinking about my search. These weren't groundbreaking revelations, but they helped me articulate what had been bouncing around in my head.</p>
<p><strong>"You want the place with the steepest learning curve"</strong> and <strong>"Find your people."</strong></p>
<p>Simple? Yes. But hearing someone else say it out loud made everything click into place.</p>
<h2 id="heading-what-actually-matters-to-me"><strong>What Actually Matters to Me</strong></h2>
<p>Once I had that clarity, I realized there were three non-negotiables for whatever came next:</p>
<ul>
<li><h3 id="heading-staying-in-the-ai-space"><strong>Staying in the AI Space</strong></h3>
<p>  I wanted to continue working in AI, and not just because it's the hot thing right now. I genuinely believe this technology is going to fundamentally change how we live and work. I wanted to be somewhere I could thoughtfully push the boundaries of what's possible with AI and build products that actually matter to people.</p>
</li>
<li><h3 id="heading-the-steepest-learning-curve"><strong>The Steepest Learning Curve</strong></h3>
<p>  This one came straight from my mentor's mouth, but it resonated because it captured what I loved most about being a startup founder. As a solo technical founder, I was constantly getting thrown into domains way outside my comfort zone - go-to-market strategy, UX design, branding, fundraising. You name it, I probably had to figure it out on a Tuesday afternoon.</p>
<p>  That experience was incredibly valuable. I knew I brought this unique blend of technical expertise, product intuition, team leadership, and entrepreneurial thinking to the table. I wanted a place that would not only value that mix but give me room to keep growing and pushing myself into new territories.</p>
</li>
<li><h3 id="heading-finding-my-people"><strong>Finding My People</strong></h3>
<p>  This was the second piece of mentor wisdom, and honestly, it might be the most important one. I was going to be spending a significant chunk of my time with these people, I owed it to myself to make sure I actually wanted to work with them.</p>
<p>  I've learned that I do my best work alongside people who are incredible at what they do but have low egos about it. I was looking for folks who seemed grounded yet genuinely excited about the customers they serve and the mission they're pursuing. People who are comfortable admitting they don't have all the answers but have the drive to tackle bold problems anyway.</p>
<p>  Most importantly, I wanted to work with people who believe we'll go further together - where everyone's unique perspective actually gets heard and valued.  </p>
</li>
</ul>
<p>Once I had this clarity, I was able to focus on what actually mattered to me. Instead of getting lost in job titles or company valuations, I had a filter I could apply to any opportunity. Three simple questions:</p>
<ul>
<li><p><strong>Does this place have the steepest learning curve?</strong></p>
</li>
<li><p><strong>Will I be surrounded by my people?</strong></p>
</li>
<li><p><strong>Can I continue pushing the boundaries of what's possible with AI?</strong></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Lessons on Finding Product-Market Fit: Insights for B2B Founders]]></title><description><![CDATA[I’ve learnt some valuable lessons during my journey as a founder trying to find product-market fit (PMF). I'm sharing some key takeaways and documenting this so I can reference it as I keep building. Most, if not all, of this is relevant only to B2B ...]]></description><link>https://chuma.blog/lessons-on-finding-product-market-fit-insights-for-b2b-founders</link><guid isPermaLink="true">https://chuma.blog/lessons-on-finding-product-market-fit-insights-for-b2b-founders</guid><category><![CDATA[Entrepreneurship]]></category><category><![CDATA[pmf]]></category><category><![CDATA[Startups]]></category><category><![CDATA[Founders]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Sat, 07 Dec 2024 00:57:52 GMT</pubDate><content:encoded><![CDATA[<p>I’ve learnt some valuable lessons during my journey as a founder trying to find product-market fit (PMF). I'm sharing some key takeaways and documenting this so I can reference it as I keep building. Most, if not all, of this is relevant only to B2B startups.</p>
<p><strong>Key Takeaways on Product-Market Fit (PMF)</strong></p>
<ol>
<li><p><strong>PMF is Obvious in Hindsight but Not During the Journey</strong>. As you build, you may sense a shift in momentum when hitting certain milestones, but it’s usually only with time that you can tell if you've truly achieved PMF or just experienced a fleeting surge of luck. That said, PMF has multiple levels: starting with a small group of happy, engaged customers, progressing to consistently exceeding many more users’ expectations, feeling a strong “pull” of demand, and ultimately solving the problem effectively while achieving economic viability.</p>
</li>
<li><p><strong>Most Companies Never Achieve PMF</strong>. That’s the reality of it.</p>
</li>
<li><p><strong>Product-channel fit (PCF) is equally important but often overlooked.</strong> Nailing PCF ensures you're consistently reaching the right audience, increasing your chances of converting them into customers and solving their problems effectively. Closely related is Founder-Market Fit, which refers to the alignment between a founder’s background, skills, passion, and experience with the market or problem they’re tackling. When this fit is strong, it becomes easier to identify the right channels, craft compelling messaging, and spot valuable opportunities.</p>
</li>
<li><p><strong>Measuring PMF: Quantitative vs. Qualitative</strong>. Quantitative metrics aren't helpful in the early stages due to low volume, you have fewer customers and less data points to analyze. Qualitative measures, focusing on how users feel and if your product exceeds their expectations, are more valuable. This is achieved by understanding your users' expectations and use cases. Watching their actions and behaviors is the most effective way to learn, followed by talking to them and asking the right questions.</p>
</li>
<li><p><strong>Pre-qualify Your Users.</strong> This is crucial especially in the early days. Determine a good Ideal Customer Profile (ICP), big enough to have the problem but small enough for you to help consistently. Then, say no to everyone else. </p>
</li>
<li><p><strong>Avoid Becoming Consultants in Disguise.</strong> This is such an easy trap to fall into, especially as an early stage startup. Protect yourself from becoming a consultant, especially with design partners. Ensure that any manual work you take on can be productized across clients and that these partnerships consistently create value.</p>
</li>
<li><p><strong>Trust Your Instincts.</strong> They’ve brought you this far as a founder, so there’s clearly value in them. Your unique perspective on the market and your product comes from your experiences and interactions with customers and your target audience. Lean into that insight and let it guide you.that.</p>
</li>
</ol>
<p>This illustration below of how PMF relates to exceeding user expectations was extremely helpful for me. It's worth noting that after achieving PMF, growth comes from increasing user satisfaction. User expectations continue to grow over time, and as a company matures, it can adjust factors that influence the growth rate at the cost of revenue.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcNQAckoXGsJC_54G2IwnI9Ux_nlAoTahuOhDLBJoCYEBoHjh-qsteodLiqG4s-lbLhZzAdQpiutvx68l84lPnj6tKoHEtdQOI-ayZjQU9n-pCSKWw0i6LOnvZtjPLlx0TPV4wHZOoXAEhS5B88D5TG5SM?key=a15GpfUzU1sSMusfGxNUqg" alt /></p>
]]></content:encoded></item><item><title><![CDATA[Breaking Down Python’s String Reversal]]></title><description><![CDATA[I’ve known for a while that reversing a string in Python is "text"[::-1]. However, it’s only now that I’ve truly understood how that works. Here’s what I learned today!
Breaking Down the Syntax
String slicing in Python uses the [start:stop:step] synt...]]></description><link>https://chuma.blog/breaking-down-pythons-string-reversal</link><guid isPermaLink="true">https://chuma.blog/breaking-down-pythons-string-reversal</guid><category><![CDATA[Python]]></category><category><![CDATA[python beginner]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Fri, 22 Nov 2024 05:31:59 GMT</pubDate><content:encoded><![CDATA[<p>I’ve known for a while that reversing a string in Python is <code>"text"[::-1]</code>. However, it’s only now that I’ve truly understood how that works. Here’s what I learned today!</p>
<p><strong>Breaking Down the Syntax</strong></p>
<p>String slicing in Python uses the <code>[start:stop:step]</code> syntax. Here’s what each part does when reversing a string:</p>
<ul>
<li><p><strong>Start</strong> (<code>:</code>): Specifies the starting index. It defaults to the beginning of the string.</p>
</li>
<li><p><strong>Stop</strong> (<code>:</code>): Specifies the stopping index. It defaults to the end of the string.</p>
</li>
<li><p><strong>Step</strong> (<code>-1</code>): Determines the step size. A negative step, such as <code>-1</code>, means take one step backwards.</p>
</li>
</ul>
<p>Let’s take the string <code>a1b2c3</code> . To reverse it, we’d use:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; "a1b2c3"[::-1]
'3c2b1a'
</code></pre>
<p>The fun part is you can use the step size to extract just the numbers or just the letters from this string. Let’s explore:</p>
<p><strong>Extract every second element (letters only):</strong></p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; "a1b2c3"[::2] 
'abc'
</code></pre>
<p><strong>Extract every second element starting from index 1 (numbers only):</strong></p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; "a1b2c3"[1::2] 
'123'
</code></pre>
<p>We can use negative step sizes to extract specific parts of the string in reverse order.</p>
<p><strong>Reverse and grab every second character (numbers):</strong></p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; "a1b2c3"[::-2] 
'321'
</code></pre>
<p><strong>Reverse and grab every second character starting from the second-to-last position (letters):</strong></p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; "a1b2c3"[-2::-2] 
'cba'
</code></pre>
]]></content:encoded></item><item><title><![CDATA[The Power of Positive Feedback in Code Reviews]]></title><description><![CDATA[Recently, a team member left the comment above on my pull request. It brought to mind a similar comment from an engineer at my previous job—a person whose opinion I valued. He had said that my code read like a story and was great to review.
This expe...]]></description><link>https://chuma.blog/the-power-of-positive-feedback-in-code-reviews</link><guid isPermaLink="true">https://chuma.blog/the-power-of-positive-feedback-in-code-reviews</guid><category><![CDATA[code review]]></category><category><![CDATA[CodeReadability]]></category><category><![CDATA[best practices]]></category><category><![CDATA[lessons]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[learning]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Wed, 17 Apr 2024 06:46:28 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://lh7-us.googleusercontent.com/uWmyaTCEJ16Jj6whj28q48kQBTr-f1cfvN-9Lbzx-P6oltZYPgS3LgcrtAcbaTAMjVHjt2VvV6EFxshFXZFbbYCXp28tIkytF2Ru1GocR1YMd9OUpl4WRlRlOei_0g3e2HBLpvT7cSiRmNVymjx6rmM" alt /></p>
<p>Recently, a team member left the comment above on my pull request. It brought to mind a similar comment from an engineer at my previous job—a person whose opinion I valued. He had said that my code read like a story and was great to review.</p>
<p>This experience reminded me of a valuable lesson I learned early in my career, one that I sometimes overlook but is crucial: <strong>the importance of leaving positive feedback in code reviews. It can make such a huge difference to the PR author.</strong></p>
<p>This is especially true for larger pull requests or ones where the author may have faced significant challenges. A simple "nicely done" or acknowledging an elegant solution or praising well-chosen names for variables and functions can boost a developer's confidence and make their day—naming, as we all know, can be particularly tricky.</p>
<p>Many developers, including myself, find submitting code for review to be a vulnerable experience, especially since reviews often have no face-to-face interaction. As a reviewer, it’s easy to focus solely on what needs to be improved, filling a review with suggestions and change requests. However, remembering the effort that went into the code can transform the review process into a more positive and constructive experience. Especially for more junior engineers, a thumbs up or “neat solution!” can go a long way in building confidence and camaraderie. It’s a small reminder that we’re in this together and we all win by writing better code.</p>
<p>If you couldn’t tell already, the compliment made my day because code readability is something I not only value but have spent a lot of time thinking about. Having worked in startups where product directions frequently change, I've seen how vital it is that the code reflects current assumptions and product state. In my previous role I joked that our code base contained all the hopes and dreams of our startup. Good readability leads to better quality feedback during reviews, and makes it easier for reviewers to focus on higher-level issues. They spend less time and mental energy deciphering your code, variable names, and code organization. Instead, they can think about how the code fits into the larger system and consider more complex scenarios and edge cases.</p>
<p>Moreover, readable code is easier to maintain. A well written pull request provides an effective way to share context, ensuring that team members understand the changes and can contribute to that area of the code more effectively.</p>
<p>For a comprehensive guide to effective code reviews and best practices, I highly recommend <a target="_blank" href="https://google.github.io/eng-practices/review/">Google's engineering practices documentation on code reviews</a>. I've used it as a guide throughout my career because it's simple, to the point, and thorough. I’ve adapted it to help create code review best practices and guidelines at the last few companies I’ve worked at, and now at <a target="_blank" href="https://nexel.ai/">Nexel</a>.</p>
<p>Remember, kind words can be a superpower in code review, I’m still talking about one comment many years later! Have you ever received a piece of positive feedback during a code review that had a significant impact on you? Or perhaps you have tips on how to give constructive, positive comments? I’d love to hear it! Share your stories and suggestions in the comments below.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Python's Compatible Release Clause for Dependency Versioning]]></title><description><![CDATA[When recently updating our project's requirements.txt file, which had dependencies locked to specific versions (like pydantic==2.6.0), I found myself wanting to allow minor version updates without opening the door to potentially disruptive major vers...]]></description><link>https://chuma.blog/understanding-pythons-compatible-release-clause-for-dependency-versioning</link><guid isPermaLink="true">https://chuma.blog/understanding-pythons-compatible-release-clause-for-dependency-versioning</guid><category><![CDATA[Python]]></category><category><![CDATA[dependency management]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Tue, 09 Apr 2024 20:15:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712693650014/93192418-d99b-4f28-aa51-45a93bcaf999.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When recently updating our project's requirements.txt file, which had dependencies locked to specific versions (like <code>pydantic==2.6.0</code>), I found myself wanting to allow minor version updates without opening the door to potentially disruptive major version changes. Initially, I considered setting a version range, such as <code>pydantic&gt;=2.6.0,&lt;3.0.0</code> which is how I always done it in the past. However, this approach felt clunky and I wondered if there was a better way.</p>
<p>After a bit of research, I stumbled upon a nifty feature called the compatible release clause, symbolized by the <code>~=</code> operator, followed by a version number. According to the <a target="_blank" href="https://peps.python.org/pep-0440/#compatible-release">official documentation</a>, a compatible release clause aims to match any candidate version that is expected to be compatible with the specified version, effectively allowing updates within a safe boundary.</p>
<p>Here’s how it works: For a given release identifier <code>V.N</code>, the compatible release clause is roughly equivalent to combining the following comparison clauses: <code>&gt;= V.N, == V.*</code>. This means:</p>
<ul>
<li><p><code>~=2.6</code> would match versions <code>&gt;= 2.6</code> and <code>==2.*</code>, allowing any version greater than or equal to 2.6 but less than 3.</p>
</li>
<li><p><code>~= 2.6.0</code> matches versions <code>&gt;=2.6.0</code> and <code>==2.6.*</code>, enabling any version greater than or equal to 2.6.0 but less than the next minor increment.</p>
</li>
</ul>
<p>Note that the <code>~=</code> operator should not be used with a single-segment version number, such as <code>~=2</code>.</p>
<p>For the sake of completeness, other clauses for specifying versions include:</p>
<ul>
<li><p><code>~=</code>: Compatible release clause</p>
</li>
<li><p><code>==</code>: Version matching clause</p>
</li>
<li><p><code>!=</code>: Version exclusion clause</p>
</li>
<li><p><code>&lt;=, &gt;=</code>: Inclusive ordered comparison clause</p>
</li>
<li><p><code>&lt;, &gt;</code>: Exclusive ordered comparison clause</p>
</li>
<li><p><code>===</code>: Arbitrary equality clause</p>
</li>
</ul>
<p>I found the compatible release clause to be an elegant solution for managing project dependencies safely and efficiently, keeping updates within a compatible range without the fear of inadvertently upgrading to a major version that could break compatibility.</p>
]]></content:encoded></item><item><title><![CDATA[How Custom GPTs Save Me Hours of Prompting and Research]]></title><description><![CDATA[Custom GPTs allow you to create custom versions of ChatGPT that combine instructions, extra knowledge, and any combination of skills. They have quickly become one of my favorite ChatGPT features, saving me time and significantly boosting my productiv...]]></description><link>https://chuma.blog/how-custom-gpts-save-me-hours-of-prompting-and-research</link><guid isPermaLink="true">https://chuma.blog/how-custom-gpts-save-me-hours-of-prompting-and-research</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Tue, 02 Apr 2024 19:25:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712085724013/8c270bbf-f0dd-4d01-8b05-fa28d0345b19.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Custom GPTs allow you to create custom versions of ChatGPT that combine instructions, extra knowledge, and any combination of skills. They have quickly become one of my favorite ChatGPT features, saving me time and significantly boosting my productivity. These tailored assistants, crafted for specific tasks like coding, copy editing, and research have become indispensable in my daily routine. Initially, I would instruct ChatGPT to adopt certain personas or focus areas—be it a friendly, professional tone for messages or meticulous attention to grammar in blog editing. This often involved repetitive prompting for similar tasks.</p>
<p>GPTs allow me to design assistants with specific personalities and expertise tailored exactly to my needs. The conversations then require less guidance and back-and-forth to produce helpful results. I either get the answer I need within the first couple of responses or quickly realize when the model might not meet my needs.</p>
<p>I kind of wish OpenAI would build a feature that automates this whole process by suggesting or even creating a custom GPT based on prompts/tasks I’m repeating often.</p>
<p>Here are some screenshots of some simpler tasks that I’ve used my Software Engineer GPT for in the past week:</p>
<p><strong>Explaining High Level Concepts</strong></p>
<p><img src="https://lh7-us.googleusercontent.com/LZDtCMMd6_eLnyzXvp-5SVn7aUumlNRyFtHZLrR3o451HS_OpPhZU1YssAv6sOjjkBMZkzBtxqCDGpJScFvgodyucLQPIZ1bWQBx1G2K6l3kmUq2-on5ts2w-2MkFV-9DK0IGSu7Y7VbVxANa31ucMI" alt /></p>
<p><strong>Creating Regex</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712084564969/ddf2291b-50c5-4807-ae58-ee50528cddd3.png" alt class="image--center mx-auto" /></p>
<p><strong>Explaining how to use libraries</strong></p>
<p><img src="https://lh7-us.googleusercontent.com/49wQh_7HZRBCTGo8VNeQntbnqQHpCdyyaV-Vp3WTXCedbpRSYSGw_b6LZPQfiU6T9gJGpSNK0F-h7NT-eX1E5V5j2MVL3yxjOIhkBdJd4Sbof0tJ_E9BDRTz3410jk7eYFFu1BHknoTxuWI2-CqizGw" alt /></p>
<p>Link to my Software Engineer GPT: <a target="_blank" href="https://chat.openai.com/g/g-D8jEasiEF-senior-software-engineer">https://chat.openai.com/g/g-D8jEasiEF-senior-software-engineer</a></p>
<p>Learn more about GPTs: <a target="_blank" href="https://openai.com/blog/introducing-gpts">https://openai.com/blog/introducing-gpts</a></p>
<p>Guide on how to create GPTs: <a target="_blank" href="https://help.openai.com/en/articles/8554397-creating-a-gpt">https://help.openai.com/en/articles/8554397-creating-a-gpt</a></p>
]]></content:encoded></item><item><title><![CDATA[Hello There!]]></title><description><![CDATA[Welcome to my blog! I've already published a couple of posts but I figured I should do an intro post. So, here we are.
I'm Chuma Kabaghe, passionate about using technology to solve meaningful problems and fascinated by the nuances of human interactio...]]></description><link>https://chuma.blog/hello-there</link><guid isPermaLink="true">https://chuma.blog/hello-there</guid><category><![CDATA[Hello World]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Thu, 28 Mar 2024 19:35:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/SymZoeE8quA/upload/8affa697a1d141980ecf8d7dc39419d1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to my blog! I've already published a couple of posts but I figured I should do an intro post. So, here we are.</p>
<p>I'm Chuma Kabaghe, passionate about using technology to solve meaningful problems and fascinated by the nuances of human interactions and incentives that drive us. My journey in tech, from software engineer to now founder of <a target="_blank" href="https://nexel.ai/">Nexel</a> (an AI automation startup), has been driven by a commitment to personal growth and the empowerment of others. I've enjoyed teaching and mentoring since my secondary school days.</p>
<p>Through this blog, I aim to keep sharing my insights and perspectives with the world. My journey from a software engineer to a startup founder has been filled with valuable lessons and insights, and I'm excited to write about them here. The content will vary, covering both technical topics and more general reflections. While some posts might be more polished than others, my goal is to always share something thought-provoking or intriguing that sparks your curiosity.</p>
<p>I’m excited to bring you along on this journey!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Cookie Sizing and Chunking: A Supabase Dilemma]]></title><description><![CDATA[While working on our app (at Nexel), we encountered a peculiar bug with Supabase where it was generating duplicate auth cookies - one chunked and the other not. This bug led me to explore cookie sizing and chunking in order to understand why one cook...]]></description><link>https://chuma.blog/understanding-cookie-sizing-and-chunking-a-supabase-dilemma</link><guid isPermaLink="true">https://chuma.blog/understanding-cookie-sizing-and-chunking-a-supabase-dilemma</guid><category><![CDATA[Web Development]]></category><category><![CDATA[cookies]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[supabase]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Thu, 28 Mar 2024 19:26:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711653687509/fb4dff61-7b9c-4f7c-bda4-5578ce9bcf28.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While working on our app (at <a target="_blank" href="https://nexel.ai/">Nexel</a>), we encountered a peculiar bug with Supabase where it was generating duplicate auth cookies - one chunked and the other not. This bug led me to explore cookie sizing and chunking in order to understand why one cookie was chunked and the other not. Although I won't delve into the bug's specifics here, those curious about it can find more information in the <a target="_blank" href="https://github.com/supabase/auth-helpers/issues/704">Github issue</a>.</p>
<h3 id="heading-what-exactly-is-a-cookie"><strong>What exactly is a cookie?</strong></h3>
<p>An HTTP Cookie, also known as a web or browser cookie, is a small piece of data sent from a server to the user's web browser. The browser may store it and send it back with subsequent requests to the same server. Cookies are primarily used to recognize requests from the same browser, facilitating functionalities like session management (user logins, shopping carts etc.), personalization, and tracking.</p>
<p><strong>Size Matters</strong></p>
<p>The size of a cookie is not determined by the length of its content (like I had wrongly assumed) but by the space it occupies, capped at 4,096 bytes (4KB). This includes the cookie's name, value, and attributes. This means the auth cookie Supabase generates has to be within this limit. </p>
<p>Supabase was generating three cookies named: <code>sb-prefix-auth-token</code>, <code>sb-prefix-auth-token.0</code> and <code>sb-prefix-auth-token.1</code>.</p>
<p><code>sb-prefix-auth-token</code> was a single cookie that contained all the necessary auth information. While <code>sb-prefix-auth-token.0</code> and <code>sb-prefix-auth-token.1</code> had the auth information split between them. Their sizes were 3,216 bytes and 244 bytes, totaling 3,460 bytes - well within the 4KB limit. This raised the question: Why were they chunked if they didn't exceed the size limit?</p>
<p>Upon investigation, I found that Supabase sets its maximum chunk size to 3,180 bytes[1]. However, the reasoning behind this specific limit remains unclear, as there's no documentation or discussion in their codebase explaining this choice. Interestingly, this limit was initially set higher at 3,600 bytes before being reduced to 3,180 bytes shortly after.</p>
<p>I wondered if this is related to browser limitations, but a quick search revealed that most browsers support cookies of at least 4KB. So, the mystery continues.</p>
<p>Well, I could keep digging but that’s as far down the rabbit hole as time allows. If anyone has insights into Supabase's decision to chunk cookies at less than 4KB, I'd love to hear from you!</p>
<p><strong>A Note on Cookie Chunking</strong></p>
<p>It's worth mentioning that the .0 and .1 suffixes in the cookie names serve a practical purpose - they indicate the order in which the cookie chunks should be merged, with .0 preceding .1.</p>
<p>Learn more about cookies here: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies</a></p>
<p>[1] <a target="_blank" href="https://github.com/supabase/auth-helpers/blob/84ef39c4a498a94717660842a20df3d10b86c794/packages/ssr/src/utils/chunker.ts#L6">https://github.com/supabase/auth-helpers/blob/84ef39c4a498a94717660842a20df3d10b86c794/packages/ssr/src/utils/chunker.ts#L6</a></p>
]]></content:encoded></item><item><title><![CDATA[Protecting Pages in Next.js Apps with Middleware]]></title><description><![CDATA[TLDR: Implementing middleware for authentication or other purposes in a Next.js app involves understanding its execution context, placement within the project structure, and how to effectively use matchers and conditional logic to control access to r...]]></description><link>https://chuma.blog/protecting-pages-in-nextjs-apps-with-middleware</link><guid isPermaLink="true">https://chuma.blog/protecting-pages-in-nextjs-apps-with-middleware</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[Middleware]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Chuma Kabaghe]]></dc:creator><pubDate>Tue, 26 Mar 2024 19:43:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711482669386/7e2189ff-105a-4921-8c70-e5346f6ee7eb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TLDR:</strong> Implementing middleware for authentication or other purposes in a Next.js app involves understanding its execution context, placement within the project structure, and how to effectively use matchers and conditional logic to control access to routes. Through the lessons below, I've shared insights that can help streamline the process of securing pages in your Next.js applications.</p>
<h1 id="heading-content">Content</h1>
<p>When developing a Next.js application, ensuring that certain pages are accessible only to authenticated users is a common requirement. This blog post will share insights and challenges from my experience setting up middleware for enforcing authentication in a Next.js app, specifically using Supabase Auth with the Next.js App Router.</p>
<h3 id="heading-understanding-middleware-in-nextjs">Understanding Middleware in Next.js</h3>
<p>Middleware in the Next.js context is essentially code that runs before a request is completed. It allows you to modify the response based on the request, whether that's through rewriting, redirecting, modifying headers, or directly responding.</p>
<h2 id="heading-key-lessons-learned">Key Lessons Learned</h2>
<h3 id="heading-lesson-1-file-placement-matters">Lesson 1: File Placement Matters</h3>
<p>The location of your middleware.ts file within your project structure is crucial. For effective functioning, it should be placed at the root level, aligning with the structure of your app. In my case, that meant at the same level as the app folder. Like this:</p>
<pre><code class="lang-plaintext">├── src
│   ├── app
│   ├── middleware.ts
</code></pre>
<h3 id="heading-lesson-2-utilizing-matchers">Lesson 2: Utilizing Matchers</h3>
<p>Matchers define the paths on which the middleware should run, which for us was all paths except static files, image optimization files, and auth callback routes. They can be as simple as a single path or encompass multiple paths in an array. For instance:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Single path, should run on /home</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  matcher: <span class="hljs-string">'/home'</span>,
}
</code></pre>
<p>OR</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Multiple paths, should run on /home and /about</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  matcher: [<span class="hljs-string">'/home'</span>, <span class="hljs-string">'/about'</span>],
}
</code></pre>
<p>The matcher can be exact paths or regex patterns, enabling a broad or specific match as needed.</p>
<p>It can also include parameters eg. <code>/home/:path</code> matches <code>/home/a</code> and <code>/home/b</code>.</p>
<p>Further, <code>/about/:path*</code> would match on the same routes as <code>/about/(.*)</code>. Essentially, any past that starts with <code>/about</code> would be a match. For example <code>/about/a/b/c</code>.</p>
<p><strong>💡</strong> The matcher values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored.</p>
<p><strong>Lesson 3: Conditional Statements in Middleware</strong></p>
<p>Middleware can include conditional statements to modify requests or responses. These can also be used to set which routes the middleware should run on. We used conditional statements to enforce authentication on pages.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { NextRequest } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">middleware</span>(<span class="hljs-params">request: NextRequest</span>) </span>{

  <span class="hljs-comment">// gets the signed in user</span>
  <span class="hljs-keyword">const</span> {
    data: { user },
  } = <span class="hljs-keyword">await</span> supabase.auth.getUser()

  ...

  <span class="hljs-comment">// if user is signed in and the current path is /login </span>
  <span class="hljs-comment">// redirect the user to /home</span>
  <span class="hljs-keyword">if</span> (user &amp;&amp; req.nextUrl.pathname === <span class="hljs-string">'/login'</span>) {
    <span class="hljs-keyword">return</span> NextResponse.redirect(<span class="hljs-keyword">new</span> URL(<span class="hljs-string">'/home'</span>, req.url))
  }

  <span class="hljs-comment">// if user is not signed in and the current path is not /login</span>
  <span class="hljs-comment">// redirect the user to /login</span>
  <span class="hljs-keyword">if</span> (!user &amp;&amp; req.nextUrl.pathname !== <span class="hljs-string">'/login'</span>) {
    <span class="hljs-keyword">return</span> NextResponse.redirect(<span class="hljs-keyword">new</span> URL(<span class="hljs-string">'/login'</span>, req.url))
  }

  <span class="hljs-comment">// More conditions and matcher can be added here...</span>
}
</code></pre>
<p>The first condition will only run on the <code>/login</code> route and redirect the signed in user to <code>/home</code>.</p>
<p>The second condition ensures that if a person who is not signed in lands on a path that is not <code>/login</code>, they are redirected to our login page at <code>/login</code></p>
<p><strong>Lesson 4: Middleware Execution Order</strong></p>
<p>Understanding the order in which middleware and other routing configurations are executed is fundamental. The process follows a specific sequence from headers and redirects in next.config.js, through middleware functions, to filesystem and dynamic routes, and finally fallback rewrites.</p>
<p>Nextjs version used: 14.0.3</p>
<p>More info in the official docs: <a target="_blank" href="https://nextjs.org/docs/pages/building-your-application/routing/middleware">Routing: Middleware | Next.js</a></p>
]]></content:encoded></item></channel></rss>