2025

Dependency Lifecycle Management in Go

Introduction

Lifecycle management ensures that application components like servers, databases, and background workers are correctly initialized, started in the right order, and gracefully stopped.

Go developers typically wire dependencies manually, instead of relying on heavy frameworks or reflection based dependency injection. This approach improves readability and reduces hidden behavior, but it also shifts the burden of managing component interactions and lifecycle sequencing onto the developer.

As applications scale, the lack of built-in orchestration increases the risk of subtle bugs and inconsistent shutdown behavior.

2023

structured logging: slog

Event logging is one of the three pillars of observability (traces and metrics being the other two).

A log entry is a timestamped record about the application’s activity, which can be used for troubleshooting, monitoring or auditing purposes.

Logs may have different formats:

  • unstructured or plain text: similar to print statements, free-form text
  • semi-structured: some values have a structure and the remaining data free-form
  • structured: there is a well-defined and consistent format, such as: W3C Log Format, NCSA Log Format, Key/Value Pair, CSV or JSON, among others. Structured logs can be easily machine parsed, which simplifies analytics, filtering and aggregation.

This article is a non-exhaustive walkthrough of logging in Go.

2022

Opentelemetry - Log-Based Change Data Capture tracing

Opentelemetry provides vendor neutral standards and implementations to generate, collect, and export tracing data.

A Context is a propagation mechanism which carries execution-scoped values across API boundaries and between logically associated execution units. Cross-cutting concerns access their data in-process using the same shared Context object.

Context propagation is required when the tracing needs to cross process or service boundaries. Common ways to propagate the context is using W3C Trace Context or Zipkin B3 headers, while to inject the context is, for example, http headers or metadata fields in event messages.

2021

Go Generic Programming - Testing the waters

Go Type Parameters Proposal is expected to be implemented with Go 1.18 early 2022.

The Very high level overview section contains a nice overview of what to expect.

If you have seen generics in other languages perhaps the most curious differences (other than using square brackets), are union types:

type SignedIntegers interface {
	int | int8 | int16 | int32 | int64
}

Similar in syntax to scala3/dotty union types, although in Go is only allowed in constraints. The type set of this union element is the set {int, int8, int16, int32, int64}.

Go - On logging and dependencies

Doing code reviews, I have sometimes noticed libraries forcing dependencies on the users.

While it is perfectly acceptable if well justified and documented, it sometimes feels like adding unnecessary baggage.

A typical example would be something like the following snippet:

func (c *Client) HealthCheck(ctx context.Context) error {
    logrus.Debug("sending get request")
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url, nil)
    if err != nil {
        return err
    }

    resp, err := c.client.Do(req)
    if err != nil {
        return err
    }

    logrus.Debugf("http status code was %d", resp.StatusCode)

    defer resp.Body.Close()

    return nil
}

Using logrus, as example, picking this code, one would need to check how logrus works, which configurations are available, which version should be used and so on.

PostgreSQL - UUID vs TEXT

In PostgreSQL one can represent UUID as UUID, TEXT or VARCHAR built-in types.

Which type represents a UUID more efficiently? Mainly in the JSONB context?

My gut feeling says built-in type UUID is way better.

One trap with using TEXT is trying to compare different cases with equals:

select 'ef9f94da-98ef-49fa-8224-32f3e1f592b3' = 'EF9F94DA-98EF-49FA-8224-32F3E1F592B3' as equal;
 equal
-------
 f

Checking the relevant RFC4122 section:

Each field is treated as an integer and has its value printed as a
zero-filled hexadecimal digit string with the most significant
digit first.  The hexadecimal values "a" through "f" are output as
lower case characters and are case insensitive on input.

Which the UUID type correctly handles:

Go - Embed

Go 1.16 added a new embed package.

While the idea is not a novelty, there were already some packages with slightly different APIs fulfilling similar roles, for example:

Having an official package is always a welcome addition to the batteries-included standard library.

Usage

A possible way to embed react assets could be as simple as:

package web

import (
    "embed"
    "io/fs"
)

//go:embed build/*
var content embed.FS

And serving assets using http.FileServer:

Go - On building URL strings

Building URL strings in Go may be accomplished in a couple different ways:

  • url.Parse
  • string concatenation (using +)
  • fmt.Sprintf
  • bytes.Buffer
  • strings.Builder

You may be asking yourself: which one should I use, then? As always, the answer depends. Let us explore why.

URL.Parse

Before we start, a quick refresh on the URL structure:

url

One important fact to be aware is that when building URLs manually, it is easy to forget the percentage-encoding.

2020

Writing a client library in Go

There are multiple ways of writing client libraries in Go. In this post, I will explore what I look for in a library depending if I am developing or checking if it is suitable for a project.

Without any specific order of importance, these are my thoughts on the subject.

Usage and Examples

Like a book can be judged by its cover, a project can be judged by its README. Therefore providing examples helps getting an idea of how the library works: if it will fit a project or how good the developer usability is (this includes function naming, usage difficulty or how easy it is to misuse).

2017

A story about Go http.Client

Or how I have learned to embrace http.RoundTripper

Using a http client in Go usually starts like this:

resp, err := http.Get("http://example.com/")

Everything works, until it doesn’t: a network blip, a connection reset, a slow
response, etc.

After some research (I recommend reading The complete guide to Go net/http
timeouts
)
you may end up writing something similar to:

c := &http.Client{
    Transport: &http.Transport{
        Dial: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).Dial,
        TLSHandshakeTimeout:   10 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
}

req, _:= http.NewRequest(http.MethodGet, "https://www.google.com", nil)

resp, err := c.Do(req)

Ok! But now the question is: how does http.Client really work?