How to use Go as a glue language

Need to make services talk to each other? Write an adapter tool in Go, lean back, and enjoy.

What’s a glue language, again?

Over on AppliedGo.net, I recently wrote about a little command-line tool that I created to combine two completely unrelated systems: two streaming clients (for Spotify and AirPlay) on a Raspberry Pi Zero W, and a radio. Yes, a radio. I wanted to make the radio switch over to the aux-in port whenever either of the two services starts playing, and switch back to the previous source when the music stops. (If you are interested in the details, read the full article.)

While writing that article, I thought about the many aspects that make Go perfectly meet the requirements of a system glue language.

What do I mean by “system glue language”? In an ideal world, all software would be 100% interoperable. Connecting two services would be a standardized, if not fully automatable task.

In reality, the Software world is a muddle. More often than not, you will be faced with tasks like,

  • connect services that have no proper API,

  • connect services with incompatible APIs, or

  • process system events and start services or notify them.

For cases like these, and cases unlike these, you will want to have a language at your tool belt that is not only straightforward to learn (like Go) but also gives you a ton of options for connecting systems that don’t even know that they want to get connected.

What makes Go the glue language of choice

Why should you pick Go for bridging systems? Here are four good reasons.

A one-stop shop for your coding needs: the standard library

Go’s standard library is like Python’s “Batteries Included(TM)”. If you need a glue language, look for one with a feature-rich and pragmatic standard library. Every task that you can solve with help from the standard library saves you another dependency onn 3rd-party code. The next section lists a number of package from the standard library that do a great job for connecting systems.

Wherever I lay my binary (that’s my home): cross-compiling

(Apolgoies to Marvin Gaye and Paul Young.)

Go feels at home on Android, DragonFly BSD, FreeBSD, Illumos, iOS, Linux, macOS (Darwin), NetBSD, OpenBSD, Plan 9, Solaris, and Windows, and supports these architectures if the operation system does: amd64, 386, arm, arm64, ppc64le, ppc64, mips64le, mips64, mipsle, mips, s390x, and wasm.

In the aforementioned article, I cross-compiled on macOS for Linux on ARM (a Raspberry Pi), without having to install any compiler toolchain from the target system. If your code is pure Go, cross compiling is a cakewalk.

As a side note, if you need to mix Go and C using CGO, things get a little more complicated, but here is a tip: use the compiler of the Zig language for cross-compiling CGO code. This has been reported to be considerably easier than using a standard C compiler.

While we are at passing tips around, here is another one if you plan to make your tool available to others: use goreleaser. This tool makes releasing new versions a breeze and can package your binary for several package managers and other targets like Homebrew, Snapcraft, Scoop, or Docker. (I wrote a two-part how-to for goreleaser here and here.) And of course, goreleaser can cross-compile your binary to multiple target OS/architecure combinations.

Happiness comes from within: self-contained binaries

Have you ever struggled with wrong interpreter versions, missing virtual machine installations, or an incompatible dynamic library when attempting to deploy an app? The more dependencies an app has on the target environment, the more likely it will run into compatibility issues.

Go code compiles to statically linked, self-contained binaries. A Go binary needs no dynamic libraries, no pre-installed interpreter, no virtual machine. Compile the code, copy the binary to the target machine if required, and run it.

This is probably the most interesting feature for people who only look for a CLI tool to use, no matter what language it is written in. If this tool comes with none of the usual installation problems, the users will love the tool and praise the author. (You!)

You almost cannot go wrong with statically compiled, self-contained binaries (except if disk space is scarce).

Simply do it: a verb-oriented language

I know it is not politically correct to pick on other languages, but only this time, please lemmedoit!

Maybe you have heard of the famous rant from 2006, Execution in the Kingdom of Nouns by Steve Yegge. It is a long rant, so let me try to put the essence of it in my own words.

Java favors nouns over verbs. What this means is, all development in Java starts with building class hierarchies and pre-declared interfaces to create a system of objects that interact in sophisticated ways. In fact, the Java economy has a track record of building over-complex architectures based on nouns so even a simple task blows up into something needlessly complicated: “build an object factory that produces an object than can do something, then ask that object to do something.”

The equivalent task in Go is: “do something.”

Go is verb-oriented. Put a pot on the stove. Boil water. Pour the water over some tea leaves. Wait. Drink. This is the way humans think. When planning a task, you think about the necessary actions before you think about the array of objects needed for executing these actions.

Go’s “do something” approach gets your stuff done.

6+ useful packages from the standard library for connecting systems and services

Earlier I mentioned the standard library as an important part of a good glue language. Here are six packages from the standard library that are particularly useful in this context.

net/http: Bring Your Own HTTP Server

The net/http package allows building a basic HTTP server with a few lines of code. For production readiness, you can implement more features as needed, or run your app behind a production-read proxy server like Caddy or Traefik. Or build an HTTP client with ease. With net/http, REST communication, fetching HTML for things like Web scraping, and other system-connecting tasks are right at your fingertips.

encoding/json and friends: polyglot data processing

Go supports working with JSON, XML, and other data formats right out of the box. The encoding/* packages (encoding/json, encoding/xml, encoding/base64, encoding/csv, etc) bring popular data formats to Go.

io and bufio: everything is a stream

Go has quite a unique approach of abstracting away data sources and sinks as uniform data streams. An io.Reader stream, for example, can represent input from a file, a network connection, an in-memory buffer, or even a simple string. Unit-test your code with a byte buffer, then use it in production with real files.

os and io.fs: manage files and more

The os packages and its sub-packages like exec or signal, as well as the io.fs package enable you to work with files and processes. Spawn a new process, walk a file tree, read and write environment variables, or create a new directory.

database/sql: store structured information from or about your systems

Maybe you do not consider a relational database a good fit for an app that is supposed to glue a few services together, but hear me out. More often than you might think, you may find yourself in a situation where you need to collect a lot of structured data from other systems, persist that data somewhere, and retrieve it really quick on request. Flat files, whether they contain CSV or JSON or other structured data formats, soon reach a limit. Reading and writing becomes slow, let alone searching the file for particular data.

A relational database manages structured data efficiently and reliably. And embedded databases like SQLite even save you from installing a separate database management system. Some people even use SQLite as a document file format. And why not? Instead of dumping all the collected data into one giant central database, write the data out to SQLite files and enjoy a standardized file format at no extra cost. If you do not want to add a dependency to a C library (because that’s what SQLite is), have a look at modernc.org/sqlite, a Go transpilation of the original SQLite sources.

Conclusion

Go is indeed more than a pure backend systems language. It also shines as a language for connecting systems at many levels and in may different ways of communicating. Easy to learn, easy to deploy and maintain, and equipped with a rich set of options.

If you are new to Go, give it a try.


Applied Go Courses helps you getting up to speed with Go without friction. The flagship product, Master Go, is an online course with concise and intuitive lectures and many practices and exercises. Rich graphic visualizations make complex topics easy to understand and remember. Lectures are short and numerous, to help planning and saving your precious time, as well as serving as quick reference material after the course. Learn more at https://appliedgo.com.


Image by endri yana yana from Pixabay. Glue bottle by Clker-Free-Vector-Images from Pixabay.


Categories: Use cases