Gateway pattern for external service calls

· 6 min

No matter which language you’re writing your service in, it’s generally a good idea to separate your external dependencies from your business-domain logic. Let’s say your order service needs to make an RPC call to an external payment service like Stripe when a customer places an order.

Usually in Go, people make a package called external or http and stash the logic of communicating with external services there. Then the business logic depends on the external package to invoke the RPC call. This is already better than directly making RPC calls inside your service functions, as that would make these two separate concerns (business logic and external-service wrangling) tightly coupled. Testing these concerns in isolation, therefore, would be a lot harder.

Writing a circuit breaker in Go

· 9 min

Besides retries, circuit breakers are probably one of the most commonly employed resilience patterns in distributed systems. While writing a retry routine is pretty simple, implementing a circuit breaker needs a little bit of work.

I realized that I usually just go for off-the-shelf libraries for circuit breaking and haven’t written one from scratch before. So, this is an attempt to create a sloppy one in Go. I picked Go instead of Python because I didn’t want to deal with sync-async idiosyncrasies or abstract things away under a soup of decorators.

Notes on building event-driven systems

· 11 min

I spent the evening watching this incredibly grokkable talk on event-driven services by James Eastham at NDC London 2024. Below is a cleaned-up version of my notes.

I highly recommend watching the full talk if you’re interested before reading this distillation.

The curse of tightly coupled microservices

Microservices often start with HTTP-based request-response communication, which seems straightforward but quickly becomes a pain as systems grow. Coupling - where one service depends on another - creates a few issues. Take the order processing service in a fictional Plant-Based Pizza company. It has to talk to the pickup service, delivery service, kitchen, and loyalty point service. They’re all tied together, so if one fails, the whole system could go down.

Strategy pattern in Go

· 6 min

These days, I don’t build hierarchical types through inheritance even when writing languages that support it. Type composition has replaced almost all of my use cases where I would’ve reached for inheritance before.

I’ve written about how to escape the template pattern hellscape and replace that with strategy pattern in Python before. While by default, Go saves you from shooting yourself in the foot by disallowing inheritance, it wasn’t obvious to me how I could apply the strategy pattern to make things more composable and testable.

Configuring options in Go

· 7 min

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:

Escaping the template pattern hellscape in Python

· 15 min

Over the years, I’ve used the template pattern across multiple OO languages with varying degrees of success. It was one of the first patterns I learned in the primordial hours of my software engineering career, and for some reason, it just feels like the natural way to tackle many real-world code-sharing problems. Yet, even before I jumped on board with the composition over inheritance camp, I couldn’t help but notice how using this particular inheritance technique spawns all sorts of design and maintenance headaches as the codebase starts to grow.

Caching connection objects in Python

· 2 min

To avoid instantiating multiple DB connections in Python apps, a common approach is to initialize the connection objects in a module once and then import them everywhere. So, you’d do this:

# src.py
import boto3  # Pip install boto3
import redis  # Pip install redis

dynamo_client = boto3.client("dynamodb")
redis_client = redis.Redis()

However, this adds import time side effects to your module and can turn out to be expensive. In search of a better solution, my first instinct was to go for functools.lru_cache(None) to immortalize the connection objects in memory. It works like this:

Implementing proxy pattern in Python

· 14 min

In Python, there’s a saying that “design patterns are anti-patterns”. Also, in the realm of dynamic languages, design patterns have the notoriety of injecting additional abstraction layers to the core logic and making the flow gratuitously obscure. Python’s dynamic nature and the treatment of functions as first-class objects often make Java-ish design patterns redundant.

Instead of littering your code with seemingly over-engineered patterns, you can almost always take the advantage of Python’s first-class objects, duck-typing, monkey-patching etc to accomplish the task at hand. However, recently there is one design pattern that I find myself using over and over again to write more maintainable code and that is the Proxy pattern. So I thought I’d document it here for future reference.