Let the domain guide your application structure

· 6 min

I like to make the distinction between application structure and architecture. Structure is how you organize the directories and packages in your app while architecture is how different components talk to each other. The way your app talks to other services in a fleet can also be called architecture.

While structure often influences architecture and vice versa, this distinction is important. This post is strictly about application structure and not library structure. Library structure is often driven by different design pressures than their app counterparts. There are a ton of canonical examples of good library structure in the stdlib, but it’s app structure where things get a bit more muddy.

Stacked middleware vs embedded delegation in Go

· 5 min

Middleware is usually the go-to pattern in Go HTTP servers for tweaking request behavior. Typically, you wrap your base handler with layers of middleware - one might log every request, while another intercepts specific routes like /special to serve a custom response.

However, I often find the indirections introduced by this pattern a bit hard to read and debug. I recently came across the embedded delegation pattern while browsing Gin’s HTTP router source code. Here, I explore both patterns and explain why I usually start with delegation whenever I need to modify HTTP requests in my Go services.

Link blog in a static site

· 3 min

One of my 2025 resolutions is doing things that don’t scale and doing them faster without overthinking. The idea is to focus on doing more while worrying less about scalability and sustainability in the things I do outside of work. With that in mind, I’ve been thinking for a while about tracking some of my out-of-band activities on this blog. The goal is to:

  • List the things I do, books and articles I read, and talks I grok.
  • Add some commentary or quote something I liked from the content, verbatim, for posterity.
  • Not spam people who just want to read the regular articles.
  • Not turn into a content junkie, churning out slop I wouldn’t want to read myself.

This isn’t about getting more eyeballs on what I publish. It’s about tracking what I do so I can look back at the end of the year and enjoy a nice little lull of accomplishment. Plus, having a place to post stuff regularly nudges me to read more, explore more, and do more of the things I actually want to do.

ETag and HTTP caching

· 7 min

One neat use case for the HTTP ETag header is client-side HTTP caching for GET requests. Along with the ETag header, the caching workflow requires you to fiddle with other conditional HTTP headers like If-Match or If-None-Match. However, their interaction can feel a bit confusing at times.

Every time I need to tackle this, I end up spending some time browsing through the relevant MDN docs on ETag, If-Match, and If-None-Match to jog my memory. At this point, I’ve done it enough times to justify spending the time to write this.

Crossing the CORS crossroad

· 5 min

Every once in a while, I find myself skimming through the MDN docs to jog my memory on how CORS works and which HTTP headers are associated with it. This is particularly true when a frontend app can’t talk to a backend service I manage due to a CORS error.

MDN’s CORS documentation is excellent but can be a bit verbose for someone just looking for a way to quickly troubleshoot and fix the issue at hand.

Self-hosted Google Fonts in Hugo

· 2 min

This site is built with Hugo and served via GitHub Pages. Recently, I decided to change the font here to make things more consistent across different devices. However, I didn’t want to go with Google Fonts for a few reasons:

  • CDN is another dependency.
  • Hosting static assets on GitHub Pages has served me well.
  • Google Fonts tracks users and violates GDPR in Germany. Google Analytics does that too. But since I’m using the latter anyway, this might come off a bit apocryphal.
  • I wanted to get a few extra Lighthouse points.

Turns out, it’s pretty easy to host the fonts yourself.

Bulk request Google search indexing with API

· 4 min

Recently, I purchased a domain for this blog and migrated the content from rednafi.github.io to rednafi.com. This turned out to be a much bigger hassle than I originally thought it’d be, mostly because, despite setting redirection for almost all the URLs from the previous domain to the new one and submitting the new sitemap.xml to the Search Console, Google kept indexing the older domain. To make things worse, the search engine selected the previous domain as canonical, and no amount of manual requests were changing the status in the last 30 days. Strangely, I didn’t encounter this issue with Bing, as it reindexed the new site within a week after I submitted the sitemap file via their webmaster panel.

Building a web app to display CSV file stats with ChatGPT & Observable

· 5 min

Whenever I plan to build something, I spend 90% of my time researching and figuring out the idiosyncrasies of the tools that I decide to use for the project. LLM tools like ChatGPT has helped me immensely in that regard. I’m taking on more tangential side projects because they’re no longer as time-consuming as they used to be and provide me with an immense amount of joy and learning opportunities. While LLM interfaces like ChatGPT may hallucinate, confabulate, and confidently give you misleading information, they also allow you to avoid starting from scratch when you decide to work on something. Personally, this benefits me enough to keep language models in my tool belt and use them to churn out more exploratory work at a much faster pace.

Pushing real-time updates to clients with Server-Sent Events (SSEs)

· 14 min

In multi-page web applications, a common workflow is where a user:

  • Loads a specific page or clicks on some button that triggers a long-running task.
  • On the server side, a background worker picks up the task and starts processing it asynchronously.
  • The page shouldn’t reload while the task is running.
  • The backend then communicates the status of the long-running task in real-time.
  • Once the task is finished, the client needs to display a success or an error message depending on the final status of the finished task.

The de facto tool for handling situations where real-time bidirectional communication is necessary is WebSocket. However, in the case above, you can see that the communication is mostly unidirectional where the client initiates some action in the server and then the server continuously pushes data to the client during the lifespan of the background job.