Go has been an important language in my personal career. I built a solid audio adServer and SSP with it when I was CTO at Soundcast. Go makes networking and parallelism so easy it feels like cheating. All that with really good performance, which was perfect for the job we had to do.
In part 1, we unveiled the fundamentals of the building phase of software. In this article, we will make this more relevant with the help of a language that has a special place in my heart. Indeed, while the fundamentals of building software are similar across languages, each language ecosystem has its own unique tools and practices.
Without further ado, let's explore these, with a particular focus on Go.
Go's Approach to Building
Go, designed by Google, takes a unique approach to building that emphasizes simplicity and speed.
A Command
Go comes with a build command. This command compiles the packages named by the import paths, along with their dependencies, but doesn't install the results. It generates an executable file.
Example:
go build main.go
Cross-Compilation in Go
One of the most useful features of the Go compiler is the ability to perform cross-compilation remarkably easily. All you have to do is set the GOOS
and GOARCH
environment variables to target the desired platforms.
Example:
GOOS=windows GOARCH=amd64 go build main.go
This will create an executable for Windows under an amd64 architecture.
Go Modules
Go mod
came a bit late because it was introduced with Go 1.11, but it's a built-in dependency management system. Before that, we had to place all our Go projects into a dedicated directory on our machine called {$GOPATH}
. While it worked, Go mod
gives us more flexibility now, which is great!
Usage is pretty straightforward, like everything in Go.
-
go mod init
initializes a new module. -
go.mod
file tracks dependencies and their versions.
You can add conditional compilation by using build tags, but the most interesting feature, in my opinion, is that Go automatically caches build output to speed up subsequent builds (of course, you can clean the cache if needed).
Advanced Go Building Techniques
Embedding Resources
If you have static files that you want to include in your binary, you can use the //go:embed
directive.
Race Detection
Go is awesome when it comes to parallelism. With its system of goroutines and channels to communicate between them, developers can easily split the workload of the software on different cores. But it's also easy to mess things up and create "races."
A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. See the The Go Memory Model for details.
Go compiler can help you find them!
Use the race
flag to detect race conditions:
go build -race main.go
Profiling and Optimization
One of the main things I like in Go is its performance. This language gives solid results with not much effort. But if you want to go the extra mile, the Go ecosystem also gives you some really great tools to measure your performance and optimize your code.
I am not going to go into the details, but if you want to dive into the subject, I invite you to read this gem: https://www.practical-go-lessons.com/chap-34-benchmarks
Custom Build Modes
Sometimes, you are not building software but a plugin or a library, for example. The Go team has got your back. In Go, you can define the buildmode to get the result you want!
- Build shared libraries:
go build -buildmode=c-shared
- Build plugins:
go build -buildmode=plugin
- Get the full list at https://pkg.go.dev/cmd/go#hdr-Build_modes
As you can see, Go provides a lot of features in its compiler and ecosystem to allow developers to get the most out of the Go technology.
And that's it for today. I'll be releasing another article this week showcasing the building techniques for TypeScript, so subscribe or follow if you want to make sure not to miss it!
Until then, happy coding!
Top comments (0)