The Applied Go Weekly Newsletter logo

The Applied Go Weekly Newsletter

Subscribe
Archives
September 1, 2024

KISS. Because YAGNI. • The Applied Go Weekly Newsletter 2024-09-01

AppliedGoNewsletterHeader640.png

Your weekly source of Go news, tips, and projects

Beware of over-engineering. Sometimes, a quick hack is sufficient. This week's spotlight describes an example: Instead of selecting a CLI command library and implementing commands and subcommands in Go, you can use a quick hack to unite independent CLI tools under a parent command. But there is a catch: Such "quick" hacks can turn out to be long-lived, postponing a proper solution by months or years.

For more about over-engineering, or the avoidance of the same, read the 4-chan article in the Featured section below. Or get some tips for building a TUI app that is powerful without drowning in complexity. Or learn an effortless way of deduplicating values for reducing memory footprint.

In any case, Keep It Simple, Stupid. Because (as you'll learn in the Unrelated To Go section) You Ain't Gonna Need It.

Featured Articles

New unique package - The Go Programming Language

Attention, pun ahead: This package is truly unique. Added to Go 1.23, the unique package helps to save memory by deduplicating (comparable) values.

Michael Knyszek explains how the unique package works and presents a real-world example from the internals of the net/netip package that saves memory by deduplicating zone attributes of IPv6 addresses.

The 4-chan Go programmer

No, this is not about the (in-)famous image bulletin board 4chan. Rather, Zach Musgrave once came across a channel of channels and wondered if this concept can be stretched to another level. And another.

Tips for building Bubble Tea programs

Terminal UIs are easy to design and implement, right? Well, despite their simple visual appearance (compared to desktop GUIs or web UIs), the mechanics behind TUIs are similar to those of graphical UIs.

Louis Garman bumped into some obstacles and compiled a list of tips for anybody who wants to start writing a moderately complex TUI app.

Podcast corner

✄ To bisect or not to bisect? I guess the answer's in the middle with Jamie Tanna's step counter!

Jamie Tanna steps in for Jonathan Hall who is on vacation. Jamie and Shay discuss the proposed bisect tool, a new proposal that aims at bringing iterators to error handling (no, not the other way round), embed support in CUE, a praise of 0.x versions, and an open-source DB diagram editor.

The community of gophers

Gopher communities FTW! Host Angelica talks to gopher meetup organizers from around the world.

Spotlight: How to implement subcommands without using a CLI command package in 2 steps

CLI is king!

Your IT infrastructure wants to be managed, and while UIs are fancy and convenient, the CLI rules when quick action is required. For ultimate control over your infrastructure, you envision a tool named srv that provides numerous subcommands, like srv status, srv start, srv stop, and so forth. Just like Git does it!

Of course, you want to write all these subcommands in Go. You know that there are several CLI command packages available that support subcommands, like Cobra, urfave/cli, Kong, and many more.

But which one to choose?

"None" is an option

If you need to decide quickly, opt for none of them. Postpone this decision until you find some time to evaluate available CLI packages. You can still bundle all your tools as subcommands under a main command.

The trick? A simple shell function.

A simple Bash/Zsh/Powershell function can deliver a simple command/subcommand hierarchy in 2 steps.

Step 1: Create stand-alone commands

Start by writing all your sub-commands as stand-alone binaries. If we stick with the above example, you would name these binaries srv-status, srv-start, srv-stop, and so on.

Step 2: Add a function to your shell config file

Here is a rather simple function that can be called as srv and invokes srv-command. Sounds trivial? Maybe, but you get a few advantages from this approach:

  1. All commands have a common entrypoint. All users know that all server management commands are called as srv . No need to keep a list of commands in one's head or on stickies.
  2. All available subcommands are discoverable. If called as srv or as srv help, the script prints out all available commands.
  3. Everyone can easily contribute new commands. Adding a new binary with a srv- prefix to a directory listed in $PATH is sufficient to extend the list of subcommands for srv. The script automatically picks up the new command, no manual changes required.

So here is the script for several shell flavors.

For Bash, add this script to .bashrc and restart the shell or start a new one:

srv() {
  print_usage() {
    echo "Usage: srv "
    echo "Available subcommands:"
    printf '  %s\n' $(compgen -c srv- | sed 's/^srv-//' | sort | uniq)
    echo "  help"
  }

  if [ $# -eq 0 ] || [ "$1" = "help" ]; then
    print_usage
    return 0
  fi

  command srv-"$@"
}

Zsh users can re-use this script after swapping out the printf line for:

printf '  %s\n' ${(f)"$(print -l ${(ok)commands[(I)srv-*]} | sed 's/^srv-//')"} 

The reason for this change is that compgen is a Bash-internal function. The Zsh script uses Zsh's associative commands array instead for finding all srv subcommands.

Fish syntax is different. Create ~/.config/fish/functions/srv.fish and insert this script:

function srv
    function print_usage
        echo "Usage: srv "
        echo "Available subcommands:"
        for cmd in (string replace -r '^.*/srv-' '' (command -s srv-*))
            echo "  $cmd"
        end
        echo "  help"
    end

    if test (count $argv) -eq 0; or test "$argv[1]" = "help"
        print_usage
        return 0
    end

    command srv-$argv[1] $argv[2..-1]
end

PowerShell users can put the following script into $PROFILE:

function srv {
    function Print-Usage {
        Write-Host "Usage: srv "
        Write-Host "Available subcommands:"
        Get-Command srv-* | ForEach-Object { $_.Name -replace '^srv-' } | Sort-Object -Unique | ForEach-Object { Write-Host "  $_" }
        Write-Host "  help"
    }

    if ($args.Count -eq 0 -or $args[0] -eq "help") {
        Print-Usage
        return
    }

    $command = "srv-$($args -join ' ')"
    Invoke-Expression $command
}

Finito

With almost no effort, you now can create and use subcommands. Calling srv start --now server1 server2 translates directly to srv-start --now server1 server2, while srv and srv help list all available commands.

Feel free to play around and extend the script. For example, if srv is called without a subcommand, make it invoke a default command instead of listing subcommands. Or improve the help command to turn the call to srv help into srv- --help.

Quote of the Week: Code is like humor

Code is like humor. When you have to explain it, it’s bad.

– Cory House

More articles, videos, talks

How to Build a Multi-Workspace Slack Application in Go - Blink

Slack supports external applications that can be invoked from within a channel by typing a slash at the beginning of a line.

David Abramov has written one in Go and shares the steps to build your own Slack app.

GitHub - project-samples/go-sql-export

Exporting large volumes of data from a database can drain system resources considerably. Here are some tips for writing a performant SQL-DB-to-CSV export in Go.

Side note: Yes, this is an article, as the README states. The code is the article's PoC.

Go Beyond: Building Performant and Reliable Golang Applications

How GOMEMLIMIT helped the Zomato team cut down memory usage.

Let's stop editing go.mod manually – tpaschalis – software, systems

Do you edit go.mod manually? Paschalis Tsilias did this, too, until he realized that go mod editmakes his life much easier.

Go sync.Pool and the Mechanics Behind It

Trashing and re-creating resources, especially exernal ones like database connections, is costly and puts an extra burden on the garbage collector. A sync.Pool can save instantiated resource for re-use, especially across goroutines.

Phuong Le dives into the pool to uncover how it works.

Resilience in communication between microservices using the failsafe-go lib

Microservice communication can fail in many ways, and consequently, multiple strategies exists for preventing, mitigating, or fixing such failures. With Elton Minetto's article and sample code, you can actively inspect strategies like retry, circuit breaker, or fallback.

Standardize and Unify 7 Message Queues in GOLANG: Kafka, RabbitMQ, IBM-MQ, Active MQ, Google Pub/Sub, Amazon SQS, NATS

How to put an abstraction layer over message queue systems.

(If you'd rather use a ready-made module, Watermill might be a fit.)

Terminal Applications in Go

Cobra for command routing, Viper for configuration managment, Bubbletea for the UI—a true dream team for TUI apps, innit?

Projects

Libraries

lfmap package - github.com/Snawoot/lfmap - Go Packages

This map implmenetation achieves lock-free access by a creating shallow clone of itself on each operation. Goroutines can thus, for example, traverse a (snapshot of) the map without blocking other goroutines.

GitHub - jordyvanvorselen/go-templ-htmx-vercel-template: A template to use Golang + HTMX + Templ on Vercel 🚀

This GoETH (Go, Echo, HTMX, Templ) repository template is ready for getting deployed to Vercel. You only need to fill the template with life.

Tools and applications

GitHub - nitwhiz/omnilock: This is a locker. 🔒

Distributed lockers have a problem: To avoid stale locks, they usually imply a time to live (TTL) on each lock. What if a client is interrupted and continues after the TTL has passed?

The author of omnilock replaces TTL by TCP connections to monitor the lock status. Processes can thus keep the lock engaged without any TTL limits. Still, when a process unexpectedly exits (and thus would not reclaim the lock anyway), the OS eventually closes the stale TCP connection.

GitHub - pijng/prep: Golang comptime. Pure blasphemy

'Just a few weeks ago, I was battling with some stuff in Go and I kept saying to myself "I wish Go had comptime like Zig!"'

Now it has.

To be clear, the only function in this package, Comptime() does nothing. But that's enough for the accompanying preprocessor that can produce, well, preprocessed results from a function called by Comptime().

GitHub - willofdaedalus/yummychars

Need to kill time while waiting for a C++ project to compile? Play yummychars, a TUI game that has no other goal than steering a snake that eats all the characters in your terminal.

GitHub - byawitz/ggh: Recall your SSH sessions (also search your SSH config file)

ggh is like ssh but remembers your previous SSH sessions. Useful if you have numerous different servers to connect to.

GitHub - onurhanak/TabStop: A cross-platform app to download Guitar Pro tabs easily.

You will want to take a look at this project if you

  • want to download guitar tabs for Guitar Pro from Songsterr
  • or want to learn Fyne by looking how others do it.

GitHub - coder/wush: simplest & fastest way to transfer files between computers via wireguard

Why wush and not woosh?!

Anyway. wush was built to "transfer files and open shells over a peer-to-peer WireGuard connection".

Worth noting: wush does not require to set up a relay server to help clients behind a firewall or behind NAT find each other. Instead, it relies on Tailscale's DERP servers. (No Tailscale account required.)

Completely unrelated to Go

No, really: YAGNI

You Ain't Gonna Need It, this time with an exclamation mark.

SQL Has Problems. We Can Fix Them: Pipe Syntax In SQ

Front to back from read be must SQL. No, wait: SQL must be read from back to front.

Look at any SQL query. Its subtasks are not listed in the order a human would execute them. Or would you select columns before you even know where these columns come from? And it gets worse with nested queries.

Google Research added a pipeline operator to standard SQL in the ZetaSQL project. This operator basically works like a pipe in a Unix shell: The output of a SQL query fragment is piped to another fragment. The result is a query statement that resembles the natural data flow but hardly looks like SQL As We Know It™.

What do you think? Cool or makes-my-hair-stand-on-end?

Happy coding! ʕ◔ϖ◔ʔ

Questions or feedback? Drop me a line. I'd love to hear from you.

Best from Munich, Christoph

Not a subscriber yet?

If you read this newsletter issue online, or if someone forwarded the newsletter to you, subscribe for regular updates to get every new issue earlier than the online version, and more reliable than an occasional forwarding. 

Find the subscription form at the end of this page.

How I can help

If you're looking for more useful content around Go, here are some ways I can help you become a better Gopher (or a Gopher at all):

On AppliedGo.net, I blog about Go projects, algorithms and data structures in Go, and other fun stuff.

Or visit the AppliedGo.com blog and learn about language specifics, Go updates, and programming-related stuff. 

My AppliedGo YouTube channel hosts quick tip and crash course videos that help you get more productive and creative with Go.

Enroll in my Go course for developers that stands out for its intense use of animated graphics for explaining abstract concepts in an intuitive way. Numerous short and concise lectures allow you to schedule your learning flow as you like.

Check it out.


Christoph Berger IT Products and Services
Dachauer Straße 29
Bergkirchen
Germany

Don't miss what's next. Subscribe to The Applied Go Weekly Newsletter:
LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.