<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Build in Public on Haseeb Majid</title>
    <link>https://haseebmajid.dev/series/build-in-public/</link>
    <description>Recent content in Build in Public on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Mon, 04 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/series/build-in-public/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Voxicle Build Log 16</title>
      <link>https://haseebmajid.dev/posts/2025-07-07-voxicle-build-log-16/</link>
      <pubDate>Mon, 04 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-07-07-voxicle-build-log-16/</guid>
      <description>&lt;h2 id=&#34;-previous-build-log-objectives&#34;&gt;⏮️ Previous Build Log Objectives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Finish the setting page&lt;/li&gt;
&lt;li&gt;Improve observability
&lt;ul&gt;
&lt;li&gt;Send data to LGTM Grafana Cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-what-i-worked-on&#34;&gt;🛠️ What I Worked On&lt;/h2&gt;
&lt;h3 id=&#34;observability&#34;&gt;Observability&lt;/h3&gt;
&lt;p&gt;This was more for my Gophercon talk, but I fixed the local LGTM stack setup so that we can send logs, metrics and traces
and view it all in Grafana. Then correleate them correctly and move between them in the GUI.&lt;/p&gt;
&lt;p&gt;Worked on the terraform code to then create the same stack in Grafana cloud to setup the LGTM stack. Including deploying
an alloy agent on my VPS. So we can send all of our OTLP data their and have consumed by the Grafana cloud.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="-previous-build-log-objectives">⏮️ Previous Build Log Objectives</h2>
<ul>
<li>Finish the setting page</li>
<li>Improve observability
<ul>
<li>Send data to LGTM Grafana Cloud</li>
</ul>
</li>
</ul>
<h2 id="-what-i-worked-on">🛠️ What I Worked On</h2>
<h3 id="observability">Observability</h3>
<p>This was more for my Gophercon talk, but I fixed the local LGTM stack setup so that we can send logs, metrics and traces
and view it all in Grafana. Then correleate them correctly and move between them in the GUI.</p>
<p>Worked on the terraform code to then create the same stack in Grafana cloud to setup the LGTM stack. Including deploying
an alloy agent on my VPS. So we can send all of our OTLP data their and have consumed by the Grafana cloud.</p>
<h3 id="setting-page">Setting Page</h3>
<p>Started work on the settings page to allow the user to update various settings about the organization.
Such as the name, logo and description.</p>
<p>Added ways for user to change their org name, avatar and description.</p>
<h2 id="-wins">✅ Wins</h2>
<ul>
<li>Learnt a lot more about OTLP observability</li>
</ul>
<h2 id="-challenges">⚠️ Challenges</h2>
<ul>
<li>Didn&rsquo;t work much on this project got busy with real life</li>
</ul>
<h2 id="-what-i-learned">💡 What I Learned</h2>
<ul>
<li>How to correlate logs, traces and metrics in the Grafana UI using the LGTM stack.</li>
</ul>
<h2 id="-next-build-log-objectives">⏭️ Next Build Log Objectives</h2>
<ul>
<li>Finish the setting page</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Build Log 15</title>
      <link>https://haseebmajid.dev/posts/2025-06-23-voxicle-build-log-15/</link>
      <pubDate>Mon, 23 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-06-23-voxicle-build-log-15/</guid>
      <description>&lt;h2 id=&#34;-previous-build-log-objectives&#34;&gt;⏮️ Previous Build Log Objectives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Mark feedback public/private&lt;/li&gt;
&lt;li&gt;Start on settings page&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-what-i-worked-on&#34;&gt;🛠️ What I Worked On&lt;/h2&gt;
&lt;h3 id=&#34;mark-feedback-as-privatepublic&#34;&gt;Mark feedback as private/public&lt;/h3&gt;
&lt;p&gt;Logged in users can mark feedback as private so it won&amp;rsquo;t be shown on the public dashboard.&lt;/p&gt;
&lt;h3 id=&#34;gitlab-dependency-proxy&#34;&gt;Gitlab Dependency Proxy&lt;/h3&gt;
&lt;p&gt;I started using the Gitlab dependency proxy so that we can pull in image from Docker hub using Gitlab to avoid hiting
rate limits. But also means we can use image cache which should makes jobs a quicker when pulling say &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="-previous-build-log-objectives">⏮️ Previous Build Log Objectives</h2>
<ul>
<li>Mark feedback public/private</li>
<li>Start on settings page</li>
</ul>
<h2 id="-what-i-worked-on">🛠️ What I Worked On</h2>
<h3 id="mark-feedback-as-privatepublic">Mark feedback as private/public</h3>
<p>Logged in users can mark feedback as private so it won&rsquo;t be shown on the public dashboard.</p>
<h3 id="gitlab-dependency-proxy">Gitlab Dependency Proxy</h3>
<p>I started using the Gitlab dependency proxy so that we can pull in image from Docker hub using Gitlab to avoid hiting
rate limits. But also means we can use image cache which should makes jobs a quicker when pulling say <code>postgres</code>.</p>
<h3 id="add-dev-environment">Add Dev Environment</h3>
<p>Previously there was just a local and production environment. Now there is a development environment we can deploy to.
This is now done CI on a MR via GitLab CI.</p>
<p>It is available at: <a href="https://dev.voxicle.app">https://dev.voxicle.app</a></p>
<h3 id="fix-generate-ci-job">Fix generate CI job</h3>
<p>For a while the CI job to check if generated code was correct was failing with extra CSS classes in the tailwind generated
file. For the life of me I couldn&rsquo;t work out why. Recently I decided to have a crack, testing it locally with <code>gitlab-ci-local</code>.
In the end it seemed to be caused by mockery. After moving mockery under the tailwind cli tool it seemed to work fine.
But again I have no clue why, it had extra classes like <code>sr-only</code> but doing a grep (or ripgrep) for the generated mocks
there was no <code>sr-only</code>. So I still don&rsquo;t know why this fixed anything. Will need to investigate more later.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">generate</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">desc</span><span class="p">:</span><span class="w"> </span><span class="l">Generates all the code needed for the project i.e. sqlc, templ &amp; tailwindcss</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">cmds</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">templ generate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">tailwindcss -i ./static/css/tailwind.css -o ./static/css/styles.css --minify</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">mockery --all</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">sqlc generate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">gomod2nix generate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">task</span><span class="p">:</span><span class="w"> </span><span class="l">format</span><span class="w">
</span></span></span></code></pre></div><h2 id="-wins">✅ Wins</h2>
<h2 id="-challenges">⚠️ Challenges</h2>
<ul>
<li>Getting the GitLab proxy to work took longer than expected</li>
<li>Trying to work out how much is left in this app before I should start sharing with people and collecting real feedback</li>
<li>Why moving <code>mockery</code> under <code>tailwindcss</code> works</li>
</ul>
<h2 id="-what-i-learned">💡 What I Learned</h2>
<ul>
<li>The  GitLab dependency proxy doesn&rsquo;t work for personal projects, they need to be in a group i.e. not hmajid2301/voxicle but voxicle/voxicle</li>
</ul>
<h2 id="-next-build-log-objectives">⏭️ Next Build Log Objectives</h2>
<ul>
<li>Finish the setting page</li>
<li>Improve observability
<ul>
<li>Send data to LGTM Grafana Cloud</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Build Log 14</title>
      <link>https://haseebmajid.dev/posts/2025-06-02-voxicle-build-log-14/</link>
      <pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-06-02-voxicle-build-log-14/</guid>
      <description>&lt;h2 id=&#34;-previous-build-log-objectives&#34;&gt;⏮️ Previous Build Log Objectives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Public page for feedback
&lt;ul&gt;
&lt;li&gt;private and public feedbacks&lt;/li&gt;
&lt;li&gt;allow anonymous voting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-what-i-worked-on&#34;&gt;🛠️ What I Worked On&lt;/h2&gt;
&lt;h3 id=&#34;public-feedback-dashboard&#34;&gt;Public Feedback Dashboard&lt;/h3&gt;
&lt;p&gt;We started of with this:&lt;/p&gt;
&lt;p&gt;&lt;img
        loading=&#34;lazy&#34;
        src=&#34;https://haseebmajid.dev/posts/2025-06-02-voxicle-build-log-14/images/old_public_dashboard.png&#34;
        type=&#34;&#34;
        alt=&#34;Old Dashboard&#34;
        
      /&gt;&lt;/p&gt;
&lt;p&gt;And finish with this&lt;/p&gt;
&lt;p&gt;&lt;img
        loading=&#34;lazy&#34;
        src=&#34;https://haseebmajid.dev/posts/2025-06-02-voxicle-build-log-14/images/new_public_dashboard.png&#34;
        type=&#34;&#34;
        alt=&#34;New Dashboard&#34;
        
      /&gt;&lt;/p&gt;
&lt;p&gt;Users can now do the almost everything they can when logged in but when anonymous. We attach a &lt;code&gt;deviceID&lt;/code&gt; cookie to them
to be able to spot if its the same user. This only works for the same device and until they clear their cookies but
should be good enough for most of the users I think for now.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="-previous-build-log-objectives">⏮️ Previous Build Log Objectives</h2>
<ul>
<li>Public page for feedback
<ul>
<li>private and public feedbacks</li>
<li>allow anonymous voting</li>
</ul>
</li>
</ul>
<h2 id="-what-i-worked-on">🛠️ What I Worked On</h2>
<h3 id="public-feedback-dashboard">Public Feedback Dashboard</h3>
<p>We started of with this:</p>
<p><img
        loading="lazy"
        src="/posts/2025-06-02-voxicle-build-log-14/images/old_public_dashboard.png"
        type=""
        alt="Old Dashboard"
        
      /></p>
<p>And finish with this</p>
<p><img
        loading="lazy"
        src="/posts/2025-06-02-voxicle-build-log-14/images/new_public_dashboard.png"
        type=""
        alt="New Dashboard"
        
      /></p>
<p>Users can now do the almost everything they can when logged in but when anonymous. We attach a <code>deviceID</code> cookie to them
to be able to spot if its the same user. This only works for the same device and until they clear their cookies but
should be good enough for most of the users I think for now.</p>
<h3 id="devex-improvements">DevEx improvements</h3>
<p>Add <code>mprocs</code> to be able to nicely manage running multiple commands in parallel and need leaving floating processes running.
Such as being ableto watch file changes with templ and tailwindcss and also start the hot reloaded web service with air.</p>
<p><img
        loading="lazy"
        src="/posts/2025-06-02-voxicle-build-log-14/images/mprocs_dev.png"
        type=""
        alt="mprocs dev task"
        
      />
<img
        loading="lazy"
        src="/posts/2025-06-02-voxicle-build-log-14/images/mprocs_watch.png"
        type=""
        alt="mprocs watch task"
        
      /></p>
<p>Try to run dependencies using <code>docker-compose</code> vs services in Gitlab CI. So we don&rsquo;t have to make changes in multiple
places.</p>
<p>Also started a new tab in zellij to be able to run some common commands easily, I just need to go into the pane
and hit enter i.e. <code>zellij run -- task lint</code>.</p>
<p><img
        loading="lazy"
        src="/posts/2025-06-02-voxicle-build-log-14/images/zellij.png"
        type=""
        alt="zellij"
        
      /></p>
<h3 id="opentofu">OpenTofu</h3>
<p>There are lots of stuff I setup manually and I would like to move into code, declaratively using terraform/open tofu.
In this case:</p>
<ul>
<li>cloudflare DNS</li>
</ul>
<h3 id="observability">Observability</h3>
<p>I have been working on my slides for my Gophercon talk about otel and observability, so I have been fixing gaps
and the broken setup in Voxicle. So now we have logs, metrics and traces viewable in a locally running instance of Grafana.
It will be easy enough to set this up in production to point to which ever tool I decide to use.</p>
<h2 id="-wins">✅ Wins</h2>
<ul>
<li>Subdomains take you to voxicle i.e. <code>orga.voxicle.app</code></li>
<li>Public page shows public feedbacks</li>
<li>Made some DevEx improvements, taskfiles, gitlab CI</li>
<li>Made my infra setup more declarative with OpenTofu</li>
</ul>
<h2 id="-challenges">⚠️ Challenges</h2>
<ul>
<li>Getting podman to work with the <code>dind</code> service in Gitlab CI
<ul>
<li>Going down a rabbit hole of trying to run podman vs using the Docker daemon inside</li>
</ul>
</li>
</ul>
<h2 id="-what-i-learned">💡 What I Learned</h2>
<ul>
<li>How to setup open tofu new project</li>
</ul>
<h2 id="-next-build-log-objectives">⏭️ Next Build Log Objectives</h2>
<ul>
<li>Mark feedback public/private</li>
<li>Start on settings page</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Build Log 13</title>
      <link>https://haseebmajid.dev/posts/2025-05-19-voxicle-build-log-week-13/</link>
      <pubDate>Mon, 19 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-05-19-voxicle-build-log-week-13/</guid>
      <description>&lt;p&gt;I will stop making these weekly I think and produce them when there is actual stuff to share, as not make them
stale and boring. Some times they may end up weekly, depending on how much progress I am making.&lt;/p&gt;
&lt;h2 id=&#34;-previous-build-log-objectives&#34;&gt;⏮️ Previous Build Log Objectives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ Better error modals (fixed)&lt;/li&gt;
&lt;li&gt;✅ Start working on public page&lt;/li&gt;
&lt;li&gt;✅ Multi tenant sub domains
&lt;ul&gt;
&lt;li&gt;i.e. org1.voxicle.app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-what-i-worked-on&#34;&gt;🛠️ What I Worked On&lt;/h2&gt;
&lt;h3 id=&#34;error-modal-bug&#34;&gt;Error Modal Bug&lt;/h3&gt;
&lt;p&gt;I have some middleware that will show an error modal when we return an error back to the client. I may change this to
be a toast for certain actions vs a full on modal but we can do that in the middleware.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I will stop making these weekly I think and produce them when there is actual stuff to share, as not make them
stale and boring. Some times they may end up weekly, depending on how much progress I am making.</p>
<h2 id="-previous-build-log-objectives">⏮️ Previous Build Log Objectives</h2>
<ul>
<li>✅ Better error modals (fixed)</li>
<li>✅ Start working on public page</li>
<li>✅ Multi tenant sub domains
<ul>
<li>i.e. org1.voxicle.app</li>
</ul>
</li>
</ul>
<h2 id="-what-i-worked-on">🛠️ What I Worked On</h2>
<h3 id="error-modal-bug">Error Modal Bug</h3>
<p>I have some middleware that will show an error modal when we return an error back to the client. I may change this to
be a toast for certain actions vs a full on modal but we can do that in the middleware.</p>
<p>There was a bug where the modal wouldn&rsquo;t work insted it would return the status code and HTML like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">401 Unauthorized (Failed to login.)<span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>...<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Set <code>HX-Retarget</code>, to replace other items i.e. not <code>#feedback_list</code> but we want to update <code>#error_modal_container</code>.
So in the middleware we now set this header, with an error modal.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;HX-Retarget&#34;</span><span class="p">,</span> <span class="s">&#34;#error_modal_container&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s">&#34;text/html&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>With the following script, to show the modal after a HTMX swap:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;htmx:afterSwap&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">evt</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">evt</span><span class="p">.</span><span class="nx">detail</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">id</span> <span class="o">===</span> <span class="s1">&#39;error_modal_container&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;error_modal&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nx">showModal</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="hx-indicator-trigger">hx-indicator trigger</h3>
<p>For really fast request we don&rsquo;t want to show the loading spinner quickly and then hide it. So for the first x seconds
don&rsquo;t show the loading spinner wait say 1 second then show it. So it is less likely to flash on screen and disappear.
Which doesn&rsquo;t provide an amazing experience for the user, of course there is a chance that this could still happen.</p>
<p>I fixed it by doing something like this, when we make a request to our backend.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;delete-indicator&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;hidden justify-center items-center transition duration-300 delay-2000 hx-indicator&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;loading loading-spinner&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;ml-2&#34;</span><span class="p">&gt;</span>Sending...<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="public-feedback-page">Public Feedback Page</h3>
<p>One of the main features of the web app is allowing other people to vote on features, so we allow anonymous votes.
So decided to manage anonymous votes in its own table, which keeps track of a device_id.  A long lived cookie we
populate. Which of course means you can vote again from another device. But this is a good enough trade off.</p>
<p>So started work to show the feedbacks on a single public page, which a user can then filter on like the private one
but then also vote for it anonymously or if you are logged in the vote counts as normal.</p>
<h3 id="subdomains">Subdomains</h3>
<p>Part of the public page is allowing the app to handle subdomains i.e. <code>org1.voxicle.app/public/feedback</code> to show
the public feedback page.</p>
<h2 id="refactored-schema">Refactored Schema</h2>
<p>Remove organization_id and replaced with slug as this is already unique and means we have to do fewer looks up to the DB.
Involved a long migration script and updating tests and some code as well.</p>
<h2 id="-wins">✅ Wins</h2>
<ul>
<li>Error modal fixed</li>
<li>Worked out how to handle anonymous votes</li>
</ul>
<h2 id="-challenges">⚠️ Challenges</h2>
<ul>
<li>Not understanding why <code>hx-swap-oob</code> wouldn&rsquo;t work with <code>HX-Retarget</code>.</li>
</ul>
<h2 id="-what-i-learned">💡 What I Learned</h2>
<ul>
<li>(remembered) HTMX only swaps by default on 200 HTTP Status code
<ul>
<li><code>&lt;meta name=&quot;htmx-config&quot; content='{&quot;responseHandling&quot;: [{&quot;code&quot;:&quot;...&quot;, &quot;swap&quot;: true}]}'/&gt;</code></li>
</ul>
</li>
<li><code>hx-swap-oob</code> used for other changes first container is replaced, we can use <code>hx-retarget</code></li>
</ul>
<h2 id="-next-build-log-objectives">⏭️ Next Build Log Objectives</h2>
<ul>
<li>Public page for feedback
<ul>
<li>private and public feedbacks</li>
<li>allow anonymous voting</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Build Log Week 12</title>
      <link>https://haseebmajid.dev/posts/2025-05-12-voxicle-build-log-week-12/</link>
      <pubDate>Mon, 12 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-05-12-voxicle-build-log-week-12/</guid>
      <description>&lt;h2 id=&#34;-last-weeks-objectives&#34;&gt;⏮️ Last Weeks Objectives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Implement Authorization using RLS in Postgres&lt;/li&gt;
&lt;li&gt;Better error modals (fixed)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;-what-i-worked-on&#34;&gt;🛠️ What I Worked On&lt;/h2&gt;
&lt;h3 id=&#34;-row-level-security-authorization&#34;&gt;🔒 Row Level Security (Authorization)&lt;/h3&gt;
&lt;p&gt;Row Level Security for Feedback and Upvotes. With row level security we can add policies and use config variables.
Such that we set the organization ID and users can only get feedback that is part of their organization.
This means we can simplify our SQL queries with fewer joins and fewer where clauses. Making them easier to read.
Essentially it lets us do a simple form of authorization inside the web app.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="-last-weeks-objectives">⏮️ Last Weeks Objectives</h2>
<ul>
<li>Implement Authorization using RLS in Postgres</li>
<li>Better error modals (fixed)</li>
</ul>
<h2 id="-what-i-worked-on">🛠️ What I Worked On</h2>
<h3 id="-row-level-security-authorization">🔒 Row Level Security (Authorization)</h3>
<p>Row Level Security for Feedback and Upvotes. With row level security we can add policies and use config variables.
Such that we set the organization ID and users can only get feedback that is part of their organization.
This means we can simplify our SQL queries with fewer joins and fewer where clauses. Making them easier to read.
Essentially it lets us do a simple form of authorization inside the web app.</p>
<p>An example on select statements for the user <code>voxicle</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="n">policy</span><span class="w"> </span><span class="n">feedback_select_policy</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">public</span><span class="p">.</span><span class="n">feedback</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">for</span><span class="w"> </span><span class="k">select</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">voxicle</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">using</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">organization_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">current_setting</span><span class="p">(</span><span class="s1">&#39;app.current_org_id&#39;</span><span class="p">)::</span><span class="n">uuid</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>Where we then have some &ldquo;hooks&rdquo; in pgx which look like this when we configure it. Where the auth is set in the ctx
in some middleware. It does make testing a bit more awkward but its a fine trade off.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">pgxConfig</span><span class="p">.</span><span class="nx">BeforeAcquire</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">a</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">conn</span> <span class="o">*</span><span class="nx">pgx</span><span class="p">.</span><span class="nx">Conn</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">state</span> <span class="o">:=</span> <span class="nx">auth</span><span class="p">.</span><span class="nf">GetFromContext</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">state</span><span class="p">.</span><span class="nx">OrgID</span> <span class="o">!=</span> <span class="nx">uuid</span><span class="p">.</span><span class="nx">Nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="s">&#34;SELECT set_config(&#39;app.current_org_id&#39;, $1, false);&#34;</span><span class="p">,</span> <span class="nx">state</span><span class="p">.</span><span class="nx">OrgID</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// TODO: log the error
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">pgxConfig</span><span class="p">.</span><span class="nx">AfterRelease</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">conn</span> <span class="o">*</span><span class="nx">pgx</span><span class="p">.</span><span class="nx">Conn</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Exec</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="s">&#34;SELECT set_config(&#39;app.current_org_id&#39;, $1, false);&#34;</span><span class="p">,</span> <span class="nx">uuid</span><span class="p">.</span><span class="nx">Nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// TODO: log the error
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I will do a write up/video on how I setup this up with sqlc, goose and pgx.</p>
<h3 id="-refactor">✏️ Refactor</h3>
<p>Refactored the get feedback query to not include upvotes, split that into another query. More DB queries by they are
simpler, and easier to understand now. Can change later if the app feels sluggish, needs to do some profiling.</p>
<h3 id="-tests">🧪 Tests</h3>
<p>Added more tests for upvotes, unit tests, integration tests and E2E tests (playwright).
Made it so the tests can run in parallel <code>-parallel 10</code> (GOMAXPROCS). This has caused the run time to decrease by about
50% in CI. Even though there are actually more tests now.</p>
<h2 id="-wins">✅ Wins</h2>
<ul>
<li>RLS meant we could simplify our queries</li>
<li>Added more tests</li>
<li>Made the tests run faster in CI
<ul>
<li>Run them in parallel</li>
</ul>
</li>
</ul>
<h2 id="-challenges">⚠️ Challenges</h2>
<p>Implementing RLS took a longer to implement than I expected. Spent the better part of a week on it. Has issues because
I was testing with a super user on postgres i.e. the default one in Docker. It took using AI to give me the hint what
could be the issues.</p>
<h2 id="-what-i-learned">💡 What I Learned</h2>
<p>RLS - Doesn&rsquo;t apply to super users so needed to create a separate user i.e. voxicle for local tests.
Vs using postgres the default admin user in Docker.</p>
<p>That you need to be careful running tests with <code>t.Parallel</code> in table test <a href="https://posener.github.io/go-table-driven-tests-parallel/">See more here</a>.</p>
<h2 id="-next-weeks-objectives">⏭️ Next Weeks Objectives</h2>
<ul>
<li>Better error modals (fixed)</li>
<li>Start working on public page</li>
<li>Multi tenant sub domains
<ul>
<li>i.e. org1.voxicle.app</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Week 11</title>
      <link>https://haseebmajid.dev/posts/2025-05-05-voxicle-week-11/</link>
      <pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-05-05-voxicle-week-11/</guid>
      <description>&lt;p&gt;Cricket is back this week, so I didn&amp;rsquo;t have as much time as I would over the weekend as I was out playing both weekends.
But I made progress every day even a little on Voxicle. I need to work out, what the minimum features I want before
I start to market and share with others to try it and provide feedback.&lt;/p&gt;
&lt;h2 id=&#34;last-week&#34;&gt;Last Week&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fixed my Tailwind LSP for a better DevEx&lt;/li&gt;
&lt;li&gt;Improved my Gitlab CI file when building the dev Docker image&lt;/li&gt;
&lt;li&gt;Improved the UI&lt;/li&gt;
&lt;li&gt;Allow users to change the sort filters i.e. latest or oldest&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;next-week&#34;&gt;Next Week&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Implement Authorization using RLS in Postgres&lt;/li&gt;
&lt;li&gt;Better error modals (fixed)&lt;/li&gt;
&lt;/ul&gt;</description>
      <content:encoded><![CDATA[<p>Cricket is back this week, so I didn&rsquo;t have as much time as I would over the weekend as I was out playing both weekends.
But I made progress every day even a little on Voxicle. I need to work out, what the minimum features I want before
I start to market and share with others to try it and provide feedback.</p>
<h2 id="last-week">Last Week</h2>
<ul>
<li>Fixed my Tailwind LSP for a better DevEx</li>
<li>Improved my Gitlab CI file when building the dev Docker image</li>
<li>Improved the UI</li>
<li>Allow users to change the sort filters i.e. latest or oldest</li>
</ul>
<h2 id="next-week">Next Week</h2>
<ul>
<li>Implement Authorization using RLS in Postgres</li>
<li>Better error modals (fixed)</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Week 9 &amp; Week 10</title>
      <link>https://haseebmajid.dev/posts/2025-04-28-voxicle-week-9-week-10/</link>
      <pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-04-28-voxicle-week-9-week-10/</guid>
      <description>&lt;h3 id=&#34;last-2-week&#34;&gt;Last 2 week&lt;/h3&gt;
&lt;p&gt;Again got really busy with some personal stuff, and stuff at work so didn&amp;rsquo;t really have much motivation to work
on this project. But I have been forcing myself to code, so at least make some progeres.&lt;/p&gt;
&lt;p&gt;I did the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Worked out we can use row level security in Postgres for better authorization&lt;/li&gt;
&lt;li&gt;Added lots more tests&lt;/li&gt;
&lt;li&gt;Edit Feedback
&lt;ul&gt;
&lt;li&gt;Dynamic URL using Alpine&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Figure out duplication bug&lt;/li&gt;
&lt;li&gt;Filter on search&lt;/li&gt;
&lt;li&gt;More filter options&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;next-week&#34;&gt;Next Week&lt;/h3&gt;
&lt;p&gt;Next week I want to;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="last-2-week">Last 2 week</h3>
<p>Again got really busy with some personal stuff, and stuff at work so didn&rsquo;t really have much motivation to work
on this project. But I have been forcing myself to code, so at least make some progeres.</p>
<p>I did the following:</p>
<ul>
<li>Worked out we can use row level security in Postgres for better authorization</li>
<li>Added lots more tests</li>
<li>Edit Feedback
<ul>
<li>Dynamic URL using Alpine</li>
</ul>
</li>
<li>Figure out duplication bug</li>
<li>Filter on search</li>
<li>More filter options</li>
</ul>
<h3 id="next-week">Next Week</h3>
<p>Next week I want to;</p>
<ul>
<li>Tidy up the UI for main feedback page</li>
<li>Better error modals</li>
<li>Implement RLS in Postgres</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Week 7 &amp; Week 8</title>
      <link>https://haseebmajid.dev/posts/2025-04-14-voxicle-week-7-week-8/</link>
      <pubDate>Mon, 14 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-04-14-voxicle-week-7-week-8/</guid>
      <description>&lt;h3 id=&#34;last-2-week&#34;&gt;Last 2 week&lt;/h3&gt;
&lt;p&gt;Some stuff kept me busy so I couldn&amp;rsquo;t really work on my project work.&lt;/p&gt;
&lt;p&gt;I did the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added tests
&lt;ul&gt;
&lt;li&gt;E2E with new skeleton&lt;/li&gt;
&lt;li&gt;How to debug E2E tests with delve (i.e. the server running separately)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fixed a bunch of TODOs in the code base&lt;/li&gt;
&lt;li&gt;Delete feedback&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However I got a busy with other random pieces of work that come up that kept me busy. Hopefully this week will be
more productive.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="last-2-week">Last 2 week</h3>
<p>Some stuff kept me busy so I couldn&rsquo;t really work on my project work.</p>
<p>I did the following:</p>
<ul>
<li>Added tests
<ul>
<li>E2E with new skeleton</li>
<li>How to debug E2E tests with delve (i.e. the server running separately)</li>
</ul>
</li>
<li>Fixed a bunch of TODOs in the code base</li>
<li>Delete feedback</li>
</ul>
<p>However I got a busy with other random pieces of work that come up that kept me busy. Hopefully this week will be
more productive.</p>
<h3 id="next-week">Next Week</h3>
<p>Next week I want to;</p>
<ul>
<li>Figure out duplication bug</li>
<li>Filter on search</li>
<li>More filter options</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Week 6</title>
      <link>https://haseebmajid.dev/posts/2025-03-31-voxicle-week-6/</link>
      <pubDate>Mon, 31 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-03-31-voxicle-week-6/</guid>
      <description>&lt;h3 id=&#34;this-week&#34;&gt;This week&lt;/h3&gt;
&lt;p&gt;I did the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finish core feedback page
&lt;ul&gt;
&lt;li&gt;Add new feedback&lt;/li&gt;
&lt;li&gt;upvote&lt;/li&gt;
&lt;li&gt;display as list or grid&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stretch
&lt;ul&gt;
&lt;li&gt;try to implement basic RBAC (more of a PoC)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However I got a busy with other random pieces of work that come up that kept me busy. Hopefully this week will be
more productive.&lt;/p&gt;
&lt;h3 id=&#34;next-week&#34;&gt;Next Week&lt;/h3&gt;
&lt;p&gt;Next week I want to;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finish core feedback page
&lt;ul&gt;
&lt;li&gt;search&lt;/li&gt;
&lt;li&gt;display as list or grid&lt;/li&gt;
&lt;li&gt;email updates when a feature has been completed&lt;/li&gt;
&lt;li&gt;more filter options&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stretch
&lt;ul&gt;
&lt;li&gt;public/private views&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Carry on working on the core feature set and make it so we can share with other people.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="this-week">This week</h3>
<p>I did the following:</p>
<ul>
<li>Finish core feedback page
<ul>
<li>Add new feedback</li>
<li>upvote</li>
<li>display as list or grid</li>
</ul>
</li>
<li>Stretch
<ul>
<li>try to implement basic RBAC (more of a PoC)</li>
</ul>
</li>
</ul>
<p>However I got a busy with other random pieces of work that come up that kept me busy. Hopefully this week will be
more productive.</p>
<h3 id="next-week">Next Week</h3>
<p>Next week I want to;</p>
<ul>
<li>Finish core feedback page
<ul>
<li>search</li>
<li>display as list or grid</li>
<li>email updates when a feature has been completed</li>
<li>more filter options</li>
</ul>
</li>
<li>Stretch
<ul>
<li>public/private views</li>
</ul>
</li>
</ul>
<p>Carry on working on the core feature set and make it so we can share with other people.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Voxicle Week 5</title>
      <link>https://haseebmajid.dev/posts/2025-03-24-voxicle-week-5/</link>
      <pubDate>Mon, 24 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-03-24-voxicle-week-5/</guid>
      <description>&lt;h3 id=&#34;this-week&#34;&gt;This week&lt;/h3&gt;
&lt;p&gt;On my list of tasks I had the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fix auth refresh token flow not fully working&lt;/li&gt;
&lt;li&gt;Add span information to the auth middleware&lt;/li&gt;
&lt;li&gt;Simplify templ with pop drilling&lt;/li&gt;
&lt;li&gt;Update project name&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And managed to do all of them, even starting to work the core feedback part of the application i.e. allowing
users to actually add feedback to the project. I rebranded the app from Go Feedback to Voxicle (which means voice and cycle).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="this-week">This week</h3>
<p>On my list of tasks I had the following:</p>
<ul>
<li>Fix auth refresh token flow not fully working</li>
<li>Add span information to the auth middleware</li>
<li>Simplify templ with pop drilling</li>
<li>Update project name</li>
</ul>
<p>And managed to do all of them, even starting to work the core feedback part of the application i.e. allowing
users to actually add feedback to the project. I rebranded the app from Go Feedback to Voxicle (which means voice and cycle).</p>
<p>Link to new URL: <a href="https://voxicle.app">https://voxicle.app</a></p>
<h3 id="next-week">Next Week</h3>
<p>Next week I want to;</p>
<ul>
<li>Finish core feedback page
<ul>
<li>Add new feedback</li>
<li>upvote</li>
<li>search</li>
<li>display as list or grid</li>
<li>email updates when a feature has been completed</li>
</ul>
</li>
<li>Stretch
<ul>
<li>try to implement basic RBAC (more of a PoC)</li>
<li>public/private views</li>
</ul>
</li>
</ul>
<p>Now onto the core features of the app, that users actually want to use. We can actually then start to ship the app
and share with some early users and get some feedback hopefully.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Go Feedback Week 4</title>
      <link>https://haseebmajid.dev/posts/2025-03-17-go-feedback-week-4/</link>
      <pubDate>Mon, 17 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-03-17-go-feedback-week-4/</guid>
      <description>&lt;h3 id=&#34;this-week&#34;&gt;This week&lt;/h3&gt;
&lt;p&gt;I decided to use wristband to the tenant and then organization on my side of the app. I am not fully happy with it.
During sign up the user creates a tenant but that might be confusing to the customer vs telling them its an organization.
But it does mean a new user is created and tenant on both wristband side and in the service database.&lt;/p&gt;
&lt;p&gt;Then I moved onto working on the subscription logic with paddle. Updated the pricing page to pull in data from the
backend when populating the page. Also integrating the overlay checkout, which is the simplest way to integrate
paddle with my app. I cannot style it as much as the inline one, but it involves writing less JS. So I will use that
for now. I am finishing off the subscription webhook logic, to create the data on our side.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="this-week">This week</h3>
<p>I decided to use wristband to the tenant and then organization on my side of the app. I am not fully happy with it.
During sign up the user creates a tenant but that might be confusing to the customer vs telling them its an organization.
But it does mean a new user is created and tenant on both wristband side and in the service database.</p>
<p>Then I moved onto working on the subscription logic with paddle. Updated the pricing page to pull in data from the
backend when populating the page. Also integrating the overlay checkout, which is the simplest way to integrate
paddle with my app. I cannot style it as much as the inline one, but it involves writing less JS. So I will use that
for now. I am finishing off the subscription webhook logic, to create the data on our side.</p>
<h3 id="next-week">Next week</h3>
<p>Next week I want to;</p>
<ul>
<li>Fix auth refresh token flow not fully working</li>
<li>Add span information to the auth middleware</li>
<li>Simplify templ with pop drilling</li>
<li>Update project name</li>
</ul>
<p>Go Feedback can be confused with the Go programming language. I spoke to the Go team and they said I could either change the mascot.
or the name. I wasn&rsquo;t super fond of the name much so will look to rebrand and update everywhere its called Go Feedback.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Go Feedback Week 3</title>
      <link>https://haseebmajid.dev/posts/2025-03-10-go-feedback-week-3/</link>
      <pubDate>Mon, 10 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-03-10-go-feedback-week-3/</guid>
      <description>&lt;h3 id=&#34;week-3&#34;&gt;Week 3&lt;/h3&gt;
&lt;p&gt;I finished off the auth flow this week, decided to move over to using the wristband UI instead of using my own components.
It ended up being a lot of code which was nice to actually delete and I think the wristband setup is pretty nice
and slick. I had to adjust slightly how I will create users in my own DB but it wasn&amp;rsquo;t a big deal.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h3 id="week-3">Week 3</h3>
<p>I finished off the auth flow this week, decided to move over to using the wristband UI instead of using my own components.
It ended up being a lot of code which was nice to actually delete and I think the wristband setup is pretty nice
and slick. I had to adjust slightly how I will create users in my own DB but it wasn&rsquo;t a big deal.</p>
<p>I also migrated some of my UI to use the DaisyUI library, which should simplify the amount of CSS I have to manage
on my own.</p>
<p>I also started the onboarding flow, after the user has signed up where they create a new organization. Which
should be finished early next week.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Go Feedback My New Side Project</title>
      <link>https://haseebmajid.dev/posts/2025-03-03-go-feedback-my-new-side-project/</link>
      <pubDate>Mon, 03 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://haseebmajid.dev/posts/2025-03-03-go-feedback-my-new-side-project/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;Last year, I worked on two main side projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OptiNix: CLI tool to easily find nix options.&lt;/li&gt;
&lt;li&gt;Banter Bus: My third attempt at building a browser based multi-player game.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whilst Banter Bus is not fully done, I got it to an almost working state and at some point will go back and fix a bunch
of the bugs and add some new features.&lt;/p&gt;
&lt;p&gt;However, I finally decided I wanted to try my hand at building something that I could try to make some money from.
Whilst also learning more about business, product design etc. How can I launch an actual product vs building something?
Purely for fun.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>Last year, I worked on two main side projects:</p>
<ul>
<li>OptiNix: CLI tool to easily find nix options.</li>
<li>Banter Bus: My third attempt at building a browser based multi-player game.</li>
</ul>
<p>Whilst Banter Bus is not fully done, I got it to an almost working state and at some point will go back and fix a bunch
of the bugs and add some new features.</p>
<p>However, I finally decided I wanted to try my hand at building something that I could try to make some money from.
Whilst also learning more about business, product design etc. How can I launch an actual product vs building something?
Purely for fun.</p>
<p>So I decided to build <a href="https://gofeedback.app">Go Feedback</a>, a tool which makes it easier to collect user feedback.
For once, it&rsquo;s also not open source, i.e. anyone can view my code (for now). Which is not something I&rsquo;ve really
done.</p>
<p>Either way, my thinking was, I wanted to build something and just learn. If nothing else, I can likely re-use the existing
code I write. Such as the auth, subscription logic or even the structure of the landing page.</p>
<p>Funnily enough, at the beginning of the year, I really wanted to dive into game dev. But gave myself one month to finish
Banter Bus (end of Jan). In that time, my passion of game dev died down, which is probably proved my heart wasn&rsquo;t in it.</p>
<p>Being a backend developer by working on this &ldquo;micro-saas&rdquo; there is a lot I can learn from building my own app vs
making a game which I cannot really incorporate in my day-to-day job.</p>
<h2 id="week-1">Week 1</h2>
<p>The first week I build out the landing page, I am great at frontend dev. I decided to use HTMX, alpinejs and tailwindcss.
I ended up using <a href="https://www.tailwindai.dev/">tailwindai</a> to help create the page. I also ended up choosing to use
a theme similar to the Daisy UI cupcake theme.</p>
<p>I put down all the big features/ideas I had that businesses might find useful and created mock-ups for them again
mostly using AI to help me. Due to inexperience in front-end development, and to be honest, currently my lack
of interest to learn it properly.</p>
<p>I added a fake door as a waitlist to gauge interest, which currently only me and my friends have used.</p>
<p><img
        loading="lazy"
        src="/posts/2025-03-03-go-feedback-my-new-side-project/images/hero.png"
        type=""
        alt="Hero Landing Page"
        
      /></p>
<h2 id="week-2">Week 2</h2>
<p>This week I spent mostly building out the auth part, i.e. login, signup and logout. I wanted to try to use the magic
link email flow. To make it as easy as possible for the user to sign up without needing a password. They just need
click a link in an email they are sent.</p>
<p>I was originally going to use <a href="https://clerk.com/">clerk</a>, but had issues (could just have been me) integrating cleanly with my backend
and ended up moving over to <a href="https://www.wristband.dev/">wristband.dev</a>. Whilst I have experience maintaining auth
services, I haven&rsquo;t really ever had to build it out myself for a web app. So it was a good learning experience for me.</p>
<p>I also learnt about other random things like CSRF and how to avoid that.</p>
<h2 id="building-in-public">Building in public</h2>
<p>I want to try to build in public, to collect feedback and to keep myself honest. It would also be a nice diary
to track my own progress. I don&rsquo;t really want to use Twitter, which lots of the &ldquo;indie hacker&rdquo; builder community
seem to use. So I am going to try to use:</p>
<ul>
<li>peerlist</li>
<li>bluesky</li>
</ul>
<p>And maintain weekly blog posts, maybe even YouTube or TikTok videos to show off the progress. Some weeks there will likely
be nothing to really show off.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
