I believe it is important for engineers to care about code quality. Some teams and companies make specific and targeted efforts to keep the quality of their codebases high. The existence of activities like “spring cleaning”, “test Fridays”, “Fixit week” and others assert the importance of code maintenance, giving engineers the breathing room to fix complex, hairy issues that take more than a day or two of time and focus to solve.
Unix commands are great for manipulating data and files. They get even better when used in shell pipelines. The following are a few of my go-tos – I’ll list the commands with an example or two. While many of the commands can be used standalone, I’ll provide examples that assume the input is piped in because that’s how you’d used these commands in a pipeline. Lastly, most of these commands are pretty simple and that is by design – the Unix philosophy focuses of simple, modular code, which can be composed to perform more complex operations.
I ran into an odd UNIX filename issue while writing Go code the other day.
Here’s a simplified example:
Let’s read a JSON file and unmarshal its contents into a struct
in go. First, let’s set an environment variable with our file name to avoid hardcoded constants in our program.
export MY_FILE="/Users/dancorin/Desktop/test.json "
Now, let’s read the file into our struct:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
// Stuff struct holds the json contents
type Stuff struct {
Test string `json:"test"`
}
func main() {
stuff := Stuff{}
place := os.Getenv("MY_FILE")
data, err := ioutil.ReadFile(place)
if err != nil {
panic(err)
}
json.Unmarshal(data, &stuff)
fmt.Printf("%+v\n", stuff)
}
❯ go run program.go
panic: open /Users/dancorin/Desktop/test.json : no such file or directory
goroutine 1 [running]:
main.main()
/Users/dancorin/Desktop/program.go:20 +0x156
exit status 2
Looks like Go couldn’t find my file.
Delve is a debugger for the Go programming language. The goal of the project is to provide a simple, full featured debugging tool for Go.
If we run our go service using a Makefile
, with a command like make run
, it can hard to find where to hook in and call dlv debug
. We can get around this issue by attach
ing the delve debugger to our running service instead. First set a breakpoint in the code, on the code path you intend to trigger by adding the statement runtime.Breakpoint()
. Don’t forget to import
the runtime
package.
Scoping in Go is built around the notion of code blocks. You can find several good explanations of how variable scoping work in Go on Google. I’d like to highlight one slightly unintuitive consequence of Go’s block scoping if you’re used to a language like Python, keeping in mind, this example does not break with Go’s notion of block scoping:
Let’s start with a common pattern in Python:
class Data(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return('Data({})'.format(self.val))
li = [Data(2), Data(3), Data(5)]
print(li)
for d in li:
d.val += 1
print(li)
Output:
The use of context
in Go can help you pass metadata through your program with helpful, related information about a call.
Let’s build an example where we set a context key, “stack”, which keeps a history of the function names called over the lifetime of the context.
As we pass the context object through a few layers of functions, we’ll append the name of the function to the value of the context key "stack"
.
Go uses goroutines to execute multiple bits of code at the same time. Channels allow for the aggregation of the results of these concurrent calls after they have finished.
Consider a case where we want to make several GET
requests to a server. The server takes some time to process each request, in many cases can handle many simultaneous connections. In a language like Python, we might do the following to make several requests:
Say we need a map to store various versions of a configuration in Go. Here is a simple example of the structure:
envs := map[string]string{
"dev": "1",
"prod": "2",
}
Given this config map, we need to create an additional map that uses the same strings as the keys, but has functions for values. The catch is that the body of each function needs to make use of the value from its corresponding key. For example, functions["prod"]
should have a value of type func() string
and the body of that function should make use of the value, envs["prod"]
. Here’s a concrete example:
Markdown is useful tool – these blog posts are written in it. I like Markdown because once you learn it, it feels invisible. It is minimal and intuitive. However, sometimes you need it to do things a little differently.
I ran into an issue where I had content which had to be written in only Markdown (no HTML) and later needed to be rendered as HTML and inserted onto a webpage, but I needed to add attributes to the HTML tags that were generated. The content itself needed to look like this:
Creating a Go module
We’re going to create a CLI tool for sending a message to a channel in Slack using the command line. This post is similar to my earlier post: Creating an Elixir Module. We’ll be using the chat.postMessage Slack API endpoint. Also, make sure you have a Slack API token.
Our CLI syntax will be:
$ ./slack -message 'hello world!' -channel @slackbot
First, make sure you have your $GOPATH
set properly.