The diminishing half-life of knowledge

Ever been in a situation where you landed a software engineering job with a particular tech stack, mastered it, switched to another company with a different stack, nailed that too, and then found yourself in a third company that used the original stack? Now, you suddenly sense that your hard-earned acumen in that initial stack has not only atrophied over the years but also a portion, or all of it, has become irrelevant, making it a bit of a struggle to catch up with the latest changes. ...

November 12, 2023

Oh my poor business logic

Adopting existing tools that work, applying them to the business problems at hand, and quickly iterating in the business domain rather than endlessly swirling in the vortex of technobabble is woefully underrated. I’ve worked at two kinds of companies before: One that only cares about measurable business outcomes, accruing technical debt and blaming engineers when no one wants to work with their codebase, ultimately hurting the product. Another that has staff engineers spending all day on linter configurations and writing seldom-read RFCs while juniors want to ditch Celery for Kafka because the latter is hip. ...

November 5, 2023

Pesky little scripts

I like writing custom scripts to automate stuff or fix repetitive headaches. Most of them are shell scripts, and a few of them are written in Python. Over the years, I’ve accumulated quite a few of them. I use Git and GNU stow1 to manage them across different machines, and the workflow2 is quite effective. However, as the list of scripts grows larger, invoking them becomes a pain because the tab completion results get cluttered with other system commands. Plus, often I even forget the initials of a script’s name and stare at my terminal while the blinking cursor facepalms at my stupidity. ...

October 29, 2023

Footnotes for the win

There are a few ways you can add URLs to your Markdown documents: Inline links [inline link](https://example.com) This will render as inline link. Reference links [reference link] Define the link destination elsewhere in the document like this: [reference link]: https://example.com This will render the same way as before, reference link. Footnote style reference links footnote style reference link[^1] Define the link destination using a footnote reference: [^1]: https://example.com This will render a bit differently with a clickable number beside the origin text that refers to the backref at the bottom of the document. Like this: footnote style reference link1. ...

October 7, 2023

Dotfile stewardship for the indolent

I’m one of those people who will sit in front of a computer for hours, fiddling with algorithms or debugging performance issues, yet won’t spend 10 minutes to improve their workflows. While I usually get away with this, every now and then, my inertia slithers back to bite me. The latest episode was me realizing how tedious it is to move config files across multiple devices when I was configuring a new MacBook Air and Mac Mini at the same time. ...

September 27, 2023

An ode to the neo-grotesque web

Every once in a while, I love browsing the Wayback Machine1 to catch a glimpse of the early internet. I enjoy the waves of nostalgic indie hacker vibes that wash over me as I type a URL into the search box and click to see an old snapshot of the site frozen in time. Being a kid of the early ’00s, I missed the spectacular cosmic genesis of the ’90s internet in its entire nascent glory. However, I did briefly get a coup d’œil of the raw, untainted web 1.0 right before SEO firms, ads, pop-ups, modals, autoplays, and heavy frontend frameworks started prowling and gentrifying the whole space into an anxiety-inducing corporate circus. ...

September 18, 2023

Self-hosted Google Fonts in Hugo

This site1 is built with Hugo2 and served via GitHub pages3. 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 violates4 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. ...

September 14, 2023

Configuring options in Go

Suppose, you have a function that takes an option struct and a message as input. Then it stylizes the message according to the option fields and prints it. What’s the most sensible API you can offer for users to configure your function? Observe: // app/src package src // Option struct type Style struct { Fg string // ANSI escape codes for foreground color Bg string // Background color } // Display the message according to Style func Display(s *Style, msg string) {} In the src package, the function Display takes a pointer to a Style instance and a msg string as parameters. Then it decorates the msg and prints it according to the style specified in the option struct. In the wild, I’ve seen 3 main ways to write APIs that let users configure options: ...

September 5, 2023

Dummy load balancer in a single Go script

I was curious to see if I could prototype a simple load balancer in a single Go script. Go’s standard library and goroutines make this trivial. Here’s what the script needs to do: Spin up two backend servers that’ll handle the incoming requests. Run a reverse proxy load balancer in the foreground. The load balancer will accept client connections and round-robin them to one of the backend servers; balancing the inbound load. Once a backend responds, the load balancer will relay the response back to the client. For simplicity, we’ll only handle client’s GET requests. Obviously, this won’t have SSL termination, advanced balancing algorithms, or session persistence like you’d get with Nginx1 or Caddy2. The point is to understand the basic workflow and show how Go makes it easy to write this sort of stuff. ...

August 30, 2023

Limit goroutines with buffered channels

I was cobbling together a long-running Go script to send webhook messages to a system when some events occur. The initial script would continuously poll a Kafka topic for events and spawn new goroutines to make HTTP requests to the destination. This had two problems: It could create unlimited goroutines if many events arrived quickly It might overload the destination system by making many concurrent requests In Python, I’d use just asyncio.Semaphore to limit concurrency. I’ve previously written about this here1. Turns out, in Go, you could do the same with a buffered channel. Here’s how the naive version looks: ...

August 23, 2023

Writing a TOTP client in Go

A TOTP1 based 2FA system has two parts. One is a client that generates the TOTP code. The other part is a server. The server verifies the code. If the client and the server-generated codes match, the server allows the inbound user to access the target system. The code usually expires after 30 seconds and then, you’ll have to regenerate it to be able to authenticate. As per RFC-62382, the server shares a base-32 encoded secret key with the client. Using this shared secret and the current UNIX timestamp, the client generates a 6-digit code. Independently, the server also generates a 6-digit code using the same secret string and its own current timestamp. If the user-entered client code matches the server-generated code, the auth succeeds. Otherwise, it fails. The client’s and the server’s current timestamp wouldn’t be an exact match. So the algorithm usually adjusts it for ~30 seconds duration. ...

August 20, 2023

Interface guards in Go

I love Go’s implicit interfaces. While convenient, they can also introduce subtle bugs unless you’re careful. Types expected to conform to certain interfaces can fluidly add or remove methods. The compiler will only complain if an identifier anticipates an interface, but is passed a type that doesn’t implement that interface. This can be problematic if you need to export types that are required to implement specific interfaces as part of their API contract. ...

August 18, 2023

Writing on well-trodden topics

I enjoy writing about software—the things I learn, the tools I use, and the work I do. Owing to the constraints of the corporate software world, more often than not, you can’t showcase your work or talk about them. At least that’s how it always has been throughout my career. At the same time, as you grow older and start having a life outside of the computer screen, you realize that working on OSS at the tail of a 40+ hour workweek is hard, and maintaining consistency is even harder. On that front, how do you keep track of your progress without losing your sense of purpose as the years fly by? ...

August 14, 2023

Go structured logging with slog

Before the release of version 1.21, you couldn’t set levels for your log messages in Go without either using third-party libraries or writing your own boilerplates. Coming from Python, I’ve always found this odd, considering that this capability has been in the Python standard library forever. However, it seems like the new log/slog subpackage in Go allows you to do that and a whole lot more. Apart from being able to add levels to log messages, slog also allows you to emit JSON-structured log messages and group them by certain attributes. The ability to do all this in-house is quite neat and I wanted to take it for a spin. The official documentation1 on this is on the terser side but still comprehensive. So, here, instead of repeating the same information, I wanted to write something for me that mainly highlights the most common cases. ...

August 10, 2023

Notes on exit interviews

If you’re a manager, then there’s no shortage of information for you on how to conduct exit interviews. But there aren’t many resources that focus on how to handle them from an employee’s perspective. I’ve been meaning to write a quick piece that isn’t biased by anyone else’s experience and is short enough so that I can quickly jog my memory in the future should the need arise. While I’ve participated in a few of them over the past five years, this text doesn’t attempt to combat the inexorable recency bias that may have seeped into the writing. ...

August 7, 2023

Taming conditionals with bitmasks

The 100k context window of Claude 21 has been a huge boon for me since now I can paste a moderately complex problem to the chat window and ask questions about it. In that spirit, it recently refactored some pretty gnarly conditional logic for me in such an elegant manner that it absolutely blew me away. Now, I know how bitmasks2 work and am aware of the existence of enum.Flag3 in Python. However, it never crossed my mind that flags can be leveraged to trim conditional branches in such a clever manner that Claude illustrated. But once I looked at the proposed solution, the whole thing immediately clicked for me. ...

July 29, 2023

Using DNS record to share text data

This morning, while browsing Hacker News, I came across a neat trick1 that allows you to share textual data by leveraging DNS TXT records. It can be useful for sharing a small amount of data in environments that restrict IP but allow DNS queries, or to bypass censorship. To test this out, I opened my domain registrar’s panel and created a new TXT type DNS entry with a base64 encoded message containing the poem A Poison Tree by William Blake. The message can now be queried and decoded with the following shell command: ...

July 17, 2023

Memory leakage in Python descriptors

Unless I’m hand rolling my own ORM-like feature or validation logic, I rarely need to write custom descriptors in Python. The built-in descriptor magics like @classmethod, @property, @staticmethod, and vanilla instance methods usually get the job done. However, every time I need to dig my teeth into descriptors, I reach for this fantastic how to1 guide by Raymond Hettinger. You should definitely set aside the time to read it if you haven’t already. It has helped me immensely to deepen my understanding of how many of the fundamental language constructs are wired together underneath. ...

July 16, 2023

Unix-style pipelining with Python's subprocess module

Python offers a ton of ways like os.system or os.spawn* to create new processes and run arbitrary commands in your system. However, the documentation usually encourages you to use the subprocess1 module for creating and managing child processes. The subprocess module exposes a high-level run() function that provides a simple interface for running a subprocess and waiting for it to complete. It accepts the command to run as a list of strings, starts the subprocess, waits for it to finish, and then returns a CompletedProcess object with information about the result. For example: ...

July 14, 2023

Enabling repeatable lazy iterations in Python

The current title of this post is probably incorrect and may even be misleading. I had a hard time coming up with a suitable name for it. But the idea goes like this: sometimes you might find yourself in a situation where you need to iterate through a generator more than once. Sure, you can use an iterable like a tuple or list to allow multiple iterations, but if the number of elements is large, that’ll cause an OOM error. On the other hand, once you’ve already consumed a generator, you’ll need to restart it if you want to go through it again. This behavior is common in pretty much every programming language that supports the generator construct. ...

July 13, 2023