<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Joana Nicolaas Ponder</title>
    <description>The latest articles on Forem by Joana Nicolaas Ponder (@joananicolaasponder).</description>
    <link>https://forem.com/joananicolaasponder</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3489277%2Fb8babf00-5b84-4033-9bad-4951429cd1a1.jpeg</url>
      <title>Forem: Joana Nicolaas Ponder</title>
      <link>https://forem.com/joananicolaasponder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joananicolaasponder"/>
    <language>en</language>
    <item>
      <title>Lets get technical...and hormonal?</title>
      <dc:creator>Joana Nicolaas Ponder</dc:creator>
      <pubDate>Mon, 10 Nov 2025 10:21:20 +0000</pubDate>
      <link>https://forem.com/joananicolaasponder/lets-get-technicaland-hormonal-4gc7</link>
      <guid>https://forem.com/joananicolaasponder/lets-get-technicaland-hormonal-4gc7</guid>
      <description>&lt;p&gt;I’ve been trying to continue building the AI features for &lt;a href="https://backlogexplorer.com/" rel="noopener noreferrer"&gt;Backlog Explorer&lt;/a&gt; this week, but honestly? I’ve been distracted. &lt;/p&gt;

&lt;p&gt;Not in the ‘I’m procrastinating’ kind of way — more like my brain just didn’t want to build. It wanted to analyze. Why? Because, hi, I’m a woman with a menstrual cycle. And that means I move through different phases each month — creative, energetic phases, and more introspective, inward ones.&lt;/p&gt;

&lt;p&gt;Right now, I’m in my inner autumn (aka luteal phase), and I always feel a bit slower, more analytical, and way more likely to deep-dive into something I want to fix.&lt;/p&gt;

&lt;p&gt;So today, when I felt that inner pull to go deep instead of build, I remembered Martin Fowler’s Refactoring book sitting on my desk — and thought, “Okay. Let’s actually try this.”&lt;/p&gt;

&lt;p&gt;I opened up my &lt;code&gt;HomePage.tsx&lt;/code&gt; file and said:&lt;br&gt;
“You know what? I’m gonna refactor this mess.”&lt;/p&gt;

&lt;p&gt;I didn’t realize it at first, but I was about to invent my own flavor of Test Driven Development — &lt;strong&gt;Cycle Driven Development™&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Intimidated by refactoring — but ready to try
&lt;/h2&gt;

&lt;p&gt;The component was a tangled web — a giant &lt;code&gt;useEffect&lt;/code&gt; trying to juggle too many tasks at once.&lt;br&gt;
It fetched the user, set state, checked onboarding, fetched games, formatted them, managed loading logic… all bundled into one block. I wrote the code, then never looked back because, well, it worked (yay!). But reading it afterward? Total gibberish.&lt;/p&gt;

&lt;p&gt;I was intimidated by the idea of refactoring it. What if I broke something? What if I made it worse? The code was messy, sure, but it was working. That felt like enough — or at least, that’s what I told myself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCurrentGames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_metadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
        &lt;span class="nf"&gt;setUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;totalGames&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_games&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;setShowOnboarding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalGames&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;games&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currently_playing_with_latest_note&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;note_created_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ascending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nullsFirst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedGames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="nx"&gt;games&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;game_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platforms&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="na"&gt;genres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;nextIntent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next_session_plan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;nextNote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next_session_plan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="nf"&gt;setCurrentGames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedGames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching current games:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;fetchCurrentGames&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, it functioned. But it felt fragile, like it could break with the slightest touch. I was hesitant to change it because I didn’t want to risk breaking what was already working.&lt;br&gt;
Honestly, my code wasn’t pretty - it was like a cluttered closet (don’t open the door!). But it got the job done.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing — a mystery until I tried it
&lt;/h2&gt;

&lt;p&gt;I remembered how in bootcamp we learned testing, TDD, and writing clean code—and I was like:&lt;/p&gt;

&lt;p&gt;“YES, I’m going to test all the things and build things the right way!!”&lt;/p&gt;

&lt;p&gt;But… I never really &lt;em&gt;got&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;I didn’t understand what tests were for, beyond just checking if the code worked.&lt;br&gt;
I didn’t feel confident writing them.&lt;br&gt;
And when I started working on Backlog Explorer, I skipped them completely.&lt;br&gt;
I told myself I’d go back and add them later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4fchszb7e7kf02ilsk4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4fchszb7e7kf02ilsk4.gif" alt="GIF of terminator saying I'll be back" width="500" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides, I thought, &lt;em&gt;Why do I need tests? I can just open the app and visually check if it works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I never wrote them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Leaning into menstrual cycle awareness — inner autumn focus
&lt;/h2&gt;

&lt;p&gt;This time, I wrote tests &lt;em&gt;before&lt;/em&gt; refactoring.&lt;br&gt;
Why? Because &lt;strong&gt;Martin Fowler told me to&lt;/strong&gt;. So I did. I trusted the book. 🙃&lt;/p&gt;

&lt;p&gt;I wrote just a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one for the onboarding state&lt;/li&gt;
&lt;li&gt;one for the loading spinner&lt;/li&gt;
&lt;li&gt;one for rendering currently playing games&lt;/li&gt;
&lt;li&gt;one for handling errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing fancy.&lt;/p&gt;

&lt;p&gt;And suddenly… it felt like the beast got a makeover.&lt;/p&gt;

&lt;p&gt;If I messed something up? The tests would catch it.&lt;br&gt;
If I renamed something wrong? The tests would tell me.&lt;br&gt;
If I broke onboarding logic? The tests would scream at me.&lt;/p&gt;

&lt;p&gt;It was honestly wild. I finally understood. Like — ohhh, this is why people love tests. This is why TDD exists. This is why you don’t just visually check everything every time like a dummy.&lt;/p&gt;

&lt;p&gt;It’s so cool.&lt;br&gt;
I love seeing all the green check marks.&lt;br&gt;
It’s actually… fun??&lt;br&gt;
I’m enjoying refactoring. A lot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjc30tgqqcft9rbyol8z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjc30tgqqcft9rbyol8z.gif" alt="Beauty and the beast ballroom dance" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The “great big scary” refactor — turning messy into maintainable
&lt;/h2&gt;

&lt;p&gt;I created a helper file:&lt;br&gt;
&lt;code&gt;/src/pages/helpers/homePageHelpers.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And started pulling logic out into focused little functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getUserOrRedirect(navigate)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;extractFirstName(user)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isNewUser(userId)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fetchFormattedCurrentGames(userId)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one does one thing.&lt;br&gt;
Each one reads clearly.&lt;br&gt;
Each one makes the useEffect feel tameable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;Now my HomePage’s &lt;code&gt;useEffect&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadUserHomepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserOrRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

      &lt;span class="nf"&gt;setUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extractFirstName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nf"&gt;setShowOnboarding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;isNewUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nf"&gt;setCurrentGames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchFormattedCurrentGames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching current games:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;loadUserHomepage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As Martin Fowler describes in the very first chapter of the book, “…with good names, I don’t have to read the body of the function to see what it does.”&lt;/p&gt;

&lt;p&gt;It breathes.&lt;br&gt;
It flows.&lt;br&gt;
This is the kind of code I want to come back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this real refactoring? Yes, it absolutely is.
&lt;/h2&gt;

&lt;p&gt;I had a moment where I wondered, &lt;em&gt;Is this really refactoring?&lt;/em&gt; It felt small, not flashy, not a big rewrite or a shiny new feature. But that’s the thing — refactoring isn’t about grand gestures. It’s about thoughtful, incremental improvements that make the code clearer and easier to work with.&lt;/p&gt;

&lt;p&gt;This process reminded me that refactoring is a form of care — care for the code, and care for my future self (and anyone else who might read this code later).&lt;/p&gt;

&lt;p&gt;It’s not just technical cleanup. It’s about reducing friction. Creating ease. Making space. And honestly, that mirrors what I’m learning to do for myself during my inner autumn, too — to reduce friction, slow down, and tend to what needs support. There’s something beautifully aligned about doing that in my code at the same time I’m doing it in my body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring + Inner Autumn = Perfect Combo
&lt;/h2&gt;

&lt;p&gt;This kind of work felt so aligned with my cycle.&lt;br&gt;
It wasn’t fast or flashy.&lt;br&gt;
It was quiet. Focused. Intentional.&lt;/p&gt;

&lt;p&gt;I wasn’t building something new — I was tending to something that already existed. Making it stronger. Clearer. Easier to understand and easier to change or update in the future.&lt;/p&gt;

&lt;p&gt;It also just… fit the kind of energy I had this week. I didn’t want to rush. I wanted to investigate deeply. I wanted to problem-solve meaningfully. And I wanted to make something that already existed feel better to work with—not just for me, but for future me too.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring is creative too — just in a quieter, more grounded way&lt;/li&gt;
&lt;li&gt;Testing makes me feel more confident&lt;/li&gt;
&lt;li&gt;My cycle isn’t a blocker&lt;/li&gt;
&lt;li&gt;Recognizing when and how to improve messy but working code is a huge part of becoming a better developer&lt;/li&gt;
&lt;li&gt;Refactoring isn’t punishment for messy code — it’s care work. Care for your code, your future self, and your team.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ✨ Final Thought
&lt;/h2&gt;

&lt;p&gt;Progress doesn’t always look like shipping new features or building something big.&lt;/p&gt;

&lt;p&gt;Sometimes, it’s cleaning up what you’ve already made.&lt;br&gt;
Sometimes, it’s learning to enjoy the parts that used to scare you.&lt;br&gt;
And sometimes — especially for those of us with menstrual cycles — it’s about trusting the natural rhythms of our energy.&lt;/p&gt;

&lt;p&gt;There are parts of the month when I feel wildly creative and inspired, and parts like now when I feel slower, more inward, more focused on fixing things. And that’s not a flaw — it’s a feature.&lt;/p&gt;

&lt;p&gt;This week reminded me that I don’t have to fight my cycle. I can work with it. I like to call it &lt;strong&gt;Cycle Driven Development™ ☺️&lt;/strong&gt;&lt;br&gt;
And honestly? My code — and my nervous system — are better for it.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>testing</category>
      <category>womenintech</category>
      <category>wecoded</category>
    </item>
    <item>
      <title>My App Died When a Real User Showed Up</title>
      <dc:creator>Joana Nicolaas Ponder</dc:creator>
      <pubDate>Wed, 10 Sep 2025 09:39:28 +0000</pubDate>
      <link>https://forem.com/joananicolaasponder/my-app-died-when-a-real-user-showed-up-2ef4</link>
      <guid>https://forem.com/joananicolaasponder/my-app-died-when-a-real-user-showed-up-2ef4</guid>
      <description>&lt;small&gt;Cover image by &lt;a href="https://unsplash.com/photos/text-heNwUmEtZzo?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;David Pupăză on Unsplash&lt;/a&gt;&lt;/small&gt;
      

&lt;p&gt;If you've been following along with my journey, you know that I have been busy building Backlog Explorer. It's a way for gamers to manage their backlogs of unplayed games. As a notorious gamer who constantly buys new games without finishing the ones they already own, I thought this was a great product and would help me play games I already owned. &lt;/p&gt;

&lt;p&gt;I took my time to really think about what I wanted this app to be and do. I wanted users to be able to add games, mark them as complete, filter by genre, by mood, and all the good stuff. My app would stand out from others because it would be more about you rediscovering what you already owned rather than spending all your money. My MVP was working really great for me, and the handful of friends I roped into testing it. &lt;/p&gt;

&lt;p&gt;Until...this one user came along and wrecked it all. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbnVoeXoyZXgzaXB1amViaTZtbDQ5a3d4MWtuMzdtZTEzbnlqbncwNCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FyfEjNtvqFBfTa%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbnVoeXoyZXgzaXB1amViaTZtbDQ5a3d4MWtuMzdtZTEzbnlqbncwNCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FyfEjNtvqFBfTa%2Fgiphy.gif" alt="A muppet setting fire to a kitchen basket" width="305" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not with bad intent or anything. They just used it as it was meant to be used. Except...they had a lot of games. Like, over 1000 games in their library. And when they tried to load their dashboard, my poor little app just...died. Loading times went from snappy to "is this thing working?" The stats dashboard would timeout. Filtering became laggy. It was honestly embarrassing. &lt;br&gt;
And when I received their feedback in my email, while on vacation in Spain, I was horrified. I couldn't even clock in to attempt to fix anything until a week later. I immediately emailed them back and apologized and told them I would be looking into it as soon as possible and would keep them posted. I was really worried that this was it for Backlog Explorer. That I clearly was not meant to be a developer and develop things for real people (because apparently...I'm not a real person? 🤷)&lt;/p&gt;

&lt;p&gt;But actually, this was the best thing that could have happened to me as a developer. &lt;/p&gt;
&lt;h2&gt;
  
  
  The Reality Check
&lt;/h2&gt;

&lt;p&gt;You see, I made the assumption that I was the norm. That a BIG backlog meant 50-100 games. That's what I had. I had a big backlog and it seemed like a lot to me. I tested with a small dataset because that's what I had. But this user showed me what real-world usage looks like. And real-world usage broke my assumptions pretty quickly. &lt;/p&gt;

&lt;p&gt;So when I got home from vacation, and my son was back at daycare, I dug in. And the problems were pretty clear once I started digging: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I was loading ALL games at once instead of paginating&lt;/li&gt;
&lt;li&gt;My database queries weren't optimized for larger datasets&lt;/li&gt;
&lt;li&gt;Dashboard stats were calculating on the frontend instead of the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, I had built something that worked great in my controlled environment of small datasets, but fell apart when it met actual users. Also, all the problems were things I had not learned at bootcamp. So I had to learn this fast in order to make it work for this user (they wrote how excited they were to use my app so I felt very personally attached to making this work for them). And I don't blame my bootcamp for this. There is only so much they can teach in such a short time. I got the basics, but now it was up to me to level up. &lt;/p&gt;
&lt;h2&gt;
  
  
  Learning Pagination the Hard Way
&lt;/h2&gt;

&lt;p&gt;The biggest issue was that I was fetching every single game from the database every time someone loaded their library. For 50 games? No problem. For 1000 games...yeah that's not going to work.&lt;/p&gt;

&lt;p&gt;I had to learn about pagination, and not just a "show 10 results per page" kind. I needed server-side pagination that would work with all the complex filtering I had built (more on that in another post).&lt;/p&gt;

&lt;p&gt;Here is what I ended up with in my &lt;code&gt;useLibraryGames&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;totalCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTotalCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchGames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_games&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      id,
      status,
      progress,
      platforms,
      image,
      updated_at,
      game:games!user_games_game_id_fkey (
        id,
        title,
        background_image,
      )
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userGames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="nf"&gt;setGames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userGames&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
  &lt;span class="nf"&gt;setTotalCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* other dependencies */&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key breakthrough was using Supabase's &lt;code&gt;range(from, to)&lt;/code&gt; method combined with &lt;code&gt;{ count: 'exact' }&lt;/code&gt;. This meant:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server-side pagination&lt;/strong&gt;: Only fetch the 30 games needed for the current page, not all 1000+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accurate totals&lt;/strong&gt;: Get the exact count for pagination controls without loading all records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Page loads went from 10+ seconds back to milliseconds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then in the UI component, I added simple pagination controls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center items-center gap-2 mt-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-outline"&lt;/span&gt;
    &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Previous
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Page &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; of &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalCount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-outline"&lt;/span&gt;
    &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;totalCount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Next
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Filtering Challenge (Coming Next)
&lt;/h2&gt;

&lt;p&gt;But here's where it got tricky - I had all these filters (by status, genre, mood, platform, year, search query) that users loved. With client-side pagination, filters just worked. With server-side pagination, I had to be more thoughtful about where filtering happened.&lt;/p&gt;

&lt;p&gt;Solving this required building a complex system with 16 interdependent filter states that work together through React's dependency system - but that's a whole technical deep-dive for my next post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Database Design That Saved My Butt
&lt;/h2&gt;

&lt;p&gt;When I first started building Backlog Explorer, I spent a lot of time thinking about the database schema. At the time, it felt like maybe I was overthinking it. I mean, I was just storing games and user progress, right? How complicated could it be? But I'm really glad I took the time to design it properly from the beginning, because it's what saved me when everything started breaking.&lt;/p&gt;

&lt;p&gt;Here's how I structured it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;games table:&lt;/strong&gt; This holds all the basic game information - title, cover art, description, release date, genres, platforms. Stuff that's the same for everyone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;user_games table:&lt;/strong&gt; This is where all the personal stuff lives - whether a user owns the game, what status it's in (not started, playing, completed), their progress percentage, personal notes, when they added it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;genres and platforms tables:&lt;/strong&gt; Normalized lookup tables that multiple games can reference.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight was separating shared data from personal data. When 20 users all have "The Witcher 3" in their libraries, there's still only one record in the &lt;code&gt;games&lt;/code&gt; table. All the user-specific stuff (status, progress, notes) lives in separate &lt;code&gt;user_games&lt;/code&gt; records that reference the shared game.&lt;/p&gt;

&lt;p&gt;This meant a couple of really important things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No data duplication.&lt;/strong&gt; I wasn't storing "The Witcher 3" information 20 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy optimization.&lt;/strong&gt; When I needed to add database indexes for performance, I could target specific tables without affecting user data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent game information.&lt;/strong&gt; If I update a game's cover art or genre information, it updates for everyone automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When that power user with 1000+ games joined and broke my app, this design is what let me fix the performance issues without having to restructure everything. I could add indexes, create optimized views, and improve queries because the data was already organized properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;Building Backlog Explorer has been this weird mix of "I'm so proud of this thing I built" and "oh god, I have no idea what I'm doing."But here's what I've figured out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start simple, but think ahead.&lt;/strong&gt; I'm glad I designed a normalized database schema from the beginning, even when I was only thinking about small use cases. It made scaling up so much easier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real users will break your assumptions.&lt;/strong&gt; I thought 100 games was a lot. Some people have thousands. Design for edge cases, or at least make sure your app fails gracefully.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance matters, even for side projects.&lt;/strong&gt; Nobody wants to wait 10 seconds for their library to load, even if it's "just" a personal project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The coolest part? After fixing all these issues, Backlog Explorer actually works really well now. Users with massive libraries can browse their games smoothly, filtering is snappy, and the dashboard stats load quickly It's not perfect - there's still so much I want to add and improve. But it's a real thing that real people use, and that feels pretty amazing.&lt;/p&gt;

&lt;p&gt;Breaking things and fixing them taught me more than any tutorial ever could. Now I know what happens when your database queries get slow, when your state management gets complicated, when external APIs fail. That's the kind of experience you can't get from following along with a course. You have to build something real, let it break, and figure out how to fix it. I can put "performance optimization," "state management," and "API integration" on my resume now. But more than that, I know I can figure stuff out when things go wrong. And in development, things go wrong a lot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to check out Backlog Explorer, it's live at &lt;a href="https://backlogexplorer.com" rel="noopener noreferrer"&gt;backlogexplorer.com&lt;/a&gt;. Feel free to break it - It'll give me more learning opportunities 😅&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>react</category>
      <category>database</category>
    </item>
  </channel>
</rss>
