I‘m excited to walk through this in-depth guide on Go with you!
Go (or Golang) is an open source programming language that I‘ve come to love after using it extensively for the past few years. In this comprehensive guide, we‘ll cover everything you need to go from beginner to building real-world projects.
Why I Fell in Love With Go
I initially learned Go after getting frustrated with some of the shortcomings of other languages like Python and JavaScript. At my job, we build large distributed backend systems, and Go has proven incredibly effective for systems programming while also being simple and fun!
Here are the key reasons I‘ve come to appreciate Go:
-
Blazing fast performance – Go compiles down to standalone native binaries that start and run quickly. The performance really blows dynamically typed scripting languages out of the water.
-
Built-in concurrency – I love how easy Go makes writing concurrent programs with goroutines and channels. This unlocks huge performance gains.
-
Simplicity – There are only 25 keywords in Go and the syntax is straightforward. Coming from languages like Java, Go is a breath of fresh air.
-
Garbage collected – Having garbage collection built-in frees me from worrying about manual memory management. Huge productivity booster!
-
Powerful standard library – So many useful data structures, algorithms and networking/IO functionality comes out of the box.
-
Excellent community – The shared wisdom across blogs, talks and open source Go projects accelerates learning.
According to the annual StackOverflow developer survey, Go has ranked as the #1 most loved language for 4 years straight! After learning Go myself, I can certainly see why experienced developers rave about it.
Next, let‘s get set up with Go on your machine.
Installing Go – A Quick and Simple Process
Getting setup with Go is straightforward since pre-compiled binaries are available for all major operating systems.
Here‘s how to install Go:
- Download the latest binary release from golang.org. For most users the 64-bit x86 version is best.
- Follow the installation instructions on the website for your OS. This will set up the Go tools and add Go to your path.
- Make sure you set the
GOPATH
environment variable, which tells Go where to install dependencies and binaries. For example, you can add the following to your.bashrc
or.zshrc
:
export GOPATH=$HOME/go-workspace
- Verify Go is installed properly by running
go version
. You should see the version number printed out!
And that‘s it! The standard library source code and docs are downloaded the first time you run a go
command.
Let‘s write our first program to print out a friendly greeting.
Hello World Program – Your First Taste of Go
Starting with a simple Hello World program is a good way to verify our Go development environment is functioning properly.
Here is a simple Hello World program in Go:
// main.go
package main // Declares package name
import "fmt" // Imports fmt package
func main() {
fmt.Println("Hello World!") // Prints output
}
To run this:
go run main.go
You should see Hello World!
printed out!
Let‘s quickly break down what each part is doing:
-
package main
declares the main application package. All programs need at least one package. -
import "fmt"
brings in thefmt
package for formatted I/O operations. -
func main()
is the entry point when executing Go code. -
fmt.Println("Hello World")
prints the string to the console.
That covers the basics of printing output in Go! Now let‘s go a bit deeper into the language.
Go Environment and Workspaces
Go code is organized into workspaces containing packages located under your GOPATH
.
The standard library packages come pre-installed with Go itself. Any additional external dependencies you use will live inside your workspace.
A typical Go workspace contains 3 directories:
src
– All Go source files organized as packagespkg
– Contains compiled Go package objectsbin
– Executable binaries get installed here
For example, my Go workspace is ~/go-projects
and contains:
~/go-projects/ (GOPATH)
└── src
└── github.com
└── callicoder
└── avg-service
└── main.go
I cloned my avg-service
repo from GitHub under the src
folder.
When I compile my project, the avg-service
executable gets put in ~/go-projects/bin
so I can easily run it from anywhere!
Go Syntax Basics – An Easy Language To Pick Up
The Go language syntax is relatively small, so it didn‘t take me long to get comfortable when I first started.
Here are some key aspects of writing Go code:
-
Packages – Every file belongs to a package. Used to organize code and provide scope.
-
Imports – Import packages from the standard library or external dependencies.
-
Functions – Declared with
func
keyword and can take zero or more arguments. -
Variables – Declared with
var
keyword. Typed and default to package scope. -
Basic Types – Numbers, strings, booleans, etc. Nice type safety with static typing.
-
Control Flow – Standard
if
,else
,for
,switch
anddefer
statements. -
Structs – Custom data types you define to group related data together.
-
Interfaces – Allow different concrete types to satisfy a common interface.
-
Pointers – Provides full access to memory addresses and manipulation.
One aspect of Go that felt unusual coming from scripting languages is that code is formatted in a very specific style. Luckily the gofmt
tool automatically handles this formatting for you!
An Example Go Program For You
The best way to demonstrate some core Go principles is with a simple example program:
// avg.go
package main
import (
"fmt"
"math"
)
// Function to calculate average
func calculateAvg(grades [3]float64) float64 {
var total float64 = 0
for _, grade := range grades {
total += grade
}
return total / float64(len(grades))
}
func main() {
mathGrades := [3]float64{ 98.5, 91, 89}
// Call our function
avg := calculateAvg(mathGrades)
fmt.Printf("Average: %.2f", avg)
}
This program calculates the average of a few grades. To run it:
go run avg.go
This will print out the calculated average grade. Now let‘s discuss some key concepts demonstrated:
-
Packages –
main
and"fmt"
packages imported. -
Functions – Two functions declared,
calculateAvg
andmain
. -
Variables – Typed variable
grades
declared. -
Arrays – Fixed length array
grades
holds values. -
For loop – Loops through array to calculate sum.
-
Printing –
fmt.Printf
formats and prints output.
This example covers some essential language mechanics like functions, packages, variables, arrays and printing.
Testing Go Code Using The Built-in Tools
One feature I appreciate about Go is built-in support for writing tests. Go includes the testing
package for unit testing code.
The convention is to write test files named xxx_test.go
that contain test functions starting with TestXXX
. For example, we could test the above calculateAvg
function:
// avg_test.go
import "testing"
func TestCalculateAvg(t *testing.T) {
grades := [3]float64{98, 92, 95}
expected := 95
actual := calculateAvg(grades)
if actual != expected {
t.Errorf("CalculateAvg() got %f, want %f", actual, expected)
}
}
This tests calculateAvg()
by checking the actual result versus the expected result.
When you run go test
, it will automatically run any tests in files ending with _test.go
. This encourages adding tests alongside production code!
Concurrency Made Easy with Goroutines and Channels
One paradigm shift Go enabled for me was writing concurrent programs.
Go provides two incredibly powerful tools for concurrency:
Goroutines – Lightweight threads of execution. Just add go
in front of a function to run it concurrently.
Channels – Typed conduits allowing goroutines to communicate.
For example, say we wanted to fetch two web pages concurrently:
func main() {
// Make channel to hold results
results := make(chan string)
// Launch goroutine for each fetch
go fetchPage("http://example1.com", results)
go fetchPage("http://example2.com", results)
// Print results as they become available
for result := range results {
fmt.Println(result)
}
}
func fetchPage(url string, results chan string) {
resp, _ := http.Get(url)
content, _ := ioutil.ReadAll(resp.Body)
// Send result to channel
results <- string(content)
}
Here we launch two goroutines running fetchPage()
concurrently. We provide each goroutine a channel to send their result on when done.
Our main thread just reads off the channel results and prints them. This concurrent approach could fetch 2x as fast with two goroutines!
Channels in Go provide a safe, synchronous way to communicate state between goroutines. They are immensely powerful.
There are also useful patterns like worker pools that make use of goroutines and channels for parallel processing. Concurrency is a huge strength of Go.
Packages and Dependency Management
Code reuse is achieved in Go through packages. The standard library provides packages for things like I/O, JSON, HTTP, etc. There are also thousands of external packages provided by the community.
For example, here we import a useful external package:
import (
"github.com/joho/godotenv"
)
func main() {
// Load config from .env file
godotenv.Load()
}
The github.com/joho/godotenv
package lets us load config from a .env
file easily.
External packages are managed using dependency management tools. My go-to choices are:
-
Go Modules – Native dependency management built into Go.
-
Dep – Third party dependency management tool.
These tools let you declare and version the external packages your project needs. They fetch and vendor these dependencies into your project.
I like how Go forces you to be intentional about dependencies. It avoids dependency hell seen in other ecosystems.
Exploring packages on Go Walker and Go Doc helps me discover useful libraries for projects.
Go Best Practices for Building Maintainable Code
Some best practices I follow when writing Go code:
-
Format code – Always
gofmt
! No arguing about style. -
Group code – Logical components should be packaged together.
-
Name things well – Use intention-revealing names like
accountRepository
. -
Error handling – Check errors diligently instead of ignoring them.
-
Use interfaces – Program to interfaces rather than concrete types.
-
Simplify design – Avoid unnecessary indirection and over-engineering.
-
Write tests – Achieve appropriate test coverage for confidence.
-
Comment well – Use comments appropriately to explain intention.
-
Limit dependencies – Don‘t just import packages willy-nilly.
-
Log carefully – Use a logger like zap to output useful diagnostics.
Many best practices map to well-established principles like DRY, SOLID and package cohesion. Following them leads to maintainable, well-architected codebases over time.
Common Pitfalls to Avoid When Starting Go
Like any language, Go does have some nuance beginners should be aware of. Here are a few common pitfalls to avoid:
-
Not using
go fmt
– Get used to formatting code using Go tools. -
Parsing JSON without error handling – Use proper error handling instead of ignoring failures.
-
Using goroutines haphazardly – Make sure they don‘t leak resources.
-
Forgetting to declare variable types –
var name string
, not justname := "foo"
. -
Importing packages you don‘t actually need – Avoid cluttering your project.
-
Not writing enough tests – Take advantage of Go‘s great testing support.
Many of these issues will get caught quickly if you make use of golint
and vet
regularly. These static analysis tools help enforce best practices.
Go Resources to Advance Your Skills
Part of what makes Go so enjoyable is the sheer depth of high quality resources available:
-
Official Documentation – Well-written guides and references. The place to start.
-
JustForFunc YouTube – Super helpful video tutorials.
-
The Go Blog – Regular posts on language updates and projects.
-
Books – Clean Code, Concurrency in Go, Go Systems Design.
-
Talks – Excellent community conference talks on YouTube.
-
Playground – Test snippets in the browser.
-
Subreddits – /r/golang and /r/golangcode for news and help.
-
Twitter – I follow #golang hashtag.
-
Gophers Slack – Chat and discuss projects.
Having these high quality resources available makes ramping up on Go very enjoyable.
Fun Beginner Projects to Apply Your Skills
Once you grasp the basics, some useful beginner projects include:
-
A CLI tool to improve your workflow
-
Web scraper to gather data into an API
-
Basic CRUD app with HTTP API and datastore
-
Realtime chat server and client
-
Automated scripts for testing or devops tasks
-
Twitter bot that searches and responds to tweets
Starting with well-scoped projects helps cement new knowledge and apply it practically. And they can be quite fun!
My Concluding Thoughts on Learning Go
Few languages have enthralled me like Go. It strikes a wonderful balance between simplicity, power and performance.
Go shines for building distributed systems, microservices, command line tools, web apps, and more. Startups like Docker, Hashicorp, InfluxData and DigitalOcean use Go extensively for its capabilities.
Learning Go not only improves your resume, but makes you a more effective developer through exposure to concurrent systems design.
My advice is to take it slow, continually practice, and don‘t be afraid to ask questions to the community. We love helping newcomers get hooked on Go!
I sincerely hope you‘ve enjoyed walking through this guide together. Let me know if you have any other questions! I‘m always happy to help fellow gophers.
Now it‘s time to start writing some real Go code. Happy coding my friend!