Go – from playing to production
Proposition
- There are so many new languages popping up that it's hard to keep up with all of them.
- It's nice to be able to quickly evaluate a language by prototyping a small project or two without investing too much time learning it.
- Accustomisation to a certain setup/workflow; for example, being able to describe and install dependencies (like Ruby's Gemfile for Bundler)
- Start simple, evolve to production.
If the above holds for you then here's a practical guide on how to get started with Go.
Warning: this is an opinionated approach.
1. Play
Play web interface offers the fastest way to experiment with Go. It lets you run commands and save your gist for sharing and later use. Useful for both beginners and experienced users.
Exercises
- go to the Play web interface
- press
Runto execute - press
Shareto save your code
2. Tour
Tour walks you through exercises describing facets of Go. Offers solid starting point for a learner.
Exercises
- complete Tour (at your convenience)
3. go run
Getting the language running on your machine is the next step once the above options are no longer satisfying.
Given the content of hello_world.go:
// hello_world.go
package main
import "fmt"
func main() {
fmt.Println("Hello go world!")
}
Continue with exercises, but before that make sure to clone the example repo
Exercises
- install Go
export GOPATH=~/golang && export PATH=$PATH:$GOPATH/bin && mkdir $GOPATHset up required environmentcd 03-go-run/(code)- run
go run hello_world.goto get the"hello go world"output - run
go fmt hello_world.goto auto-format the code
4. go test
Tests help evolve code and ensure it works.
So in order to test that the hello string is generated properly let's refactor the example and add a test.
package main
import "fmt"
func main() {
fmt.Println(hello("go"))
}
func hello(one string) string {
return fmt.Sprintf("hello %s world", one)
}
and test
package main
import "testing"
func TestHello(t *testing.T) {
exp := "hello go world"
got := hello("Go")
if exp != got {
t.Errorf("\nExp: %s\nGot: %s", exp, got)
}
}
Exercises
cd 04-go-test(code)go fmtboth files- run
go test, notice the failing test - do not fix the test yet
Failing test output:
--- FAIL: TestHello (0.00s)
hello_world_test.go:11:
Exp: hello go world
Got: hello Go world
FAIL
5. Continuous testing and Makefile
Makefile lets you describe automation for routine tasks and save some typing.
In this case we want to have project rebuilt every time a file changes.
test:
go test
re-test:
reflex -r '\.go' $(MAKE) test
dev-deps:
go get github.com/cespare/reflex
Exercise:
cd 05-ContinuousTestingAndMakefile(code)- run
make dev-depsto install development dependencies - run
make re-testand watch the test fail - fix the failing test and see it
PASSimmediately!
6. Packages, Project Layout
To demonstrate use of packages and basic project layout, our helloworld will be converted into a library.
NOTE: Currently there's no official and convenient way to manage dependencies. Gb was chosen as a tool to govern project layout, building and handling dependencies.
New layout now looks like this:
tree
.
├── Makefile
├── bin
│ ├── helloworld
│ └── helloworld-server
└── src
├── cmd
│ ├── helloworld
│ │ └── main.go
│ └── helloworld-server
│ └── main.go
└── helloworld
├── hello_world.go
└── hello_world_test.go
Changes:
- the
hellofunction has been renamed toHelloto be exported and accessible from outside the package helloworldis now a package (notepackage helloworldinsteadpackage maininsrc/helloworld/*.go)- there are now separate "entry points" (or executables) source of which located under
src/cmd Makefilenow has abuildrule that builds the entry points/executables tobindirectory- Gb is our new development dependency
Exercise:
cd 06-PackagesAndProjectLayout(code)- run
make dev-depsto install new dependencies - run
make re-test, notice failing test - fix the failing test
- run
make buildto get binaries built - run
bin/helloworldensure it printshello go world - run
bin/helloworld-serverand point your browser to http://localhost:8080/?name=Go to get a web "hello Go world"
7. Dependencies
Let's now add a dependency and describe the process. In this example we're going to use httprouter to add a nicer route for our web server hello example.
Adding a dependency with Gb is running a command:
gb vendor fetch github.com/julienschmidt/httprouter
As result we now have vendor/ directory that looks like this:
tree -L 4 vendor/
vendor/
├── manifest
└── src
└── github.com
└── julienschmidt
└── httprouter
where vendor/manifest is the file which tracks dependencies added with gb vendor fetch command.
Check vendor/manifest into your SCM to be able to retrieve dependencies later with gb vendor restore (without having to check them in to SCM).
Now that that dependency is ready to be used let's update the server code to look like:
package main
import (
"fmt"
"helloworld"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
router.GET("/", index)
router.GET("/:name", index)
http.ListenAndServe(":8080", router)
}
func index(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
name := ps.ByName("name")
if name = "" {
name = "Unknown"
}
fmt.Fprint(w, helloworld.Hello(name))
}
Changes:
- import section now includes vendored dependency
"github.com/julienschmidt/httprouter" - new router
router := httprouter.New() - new route with named parameter
/:name indexfunction signature change(as required by the router API)
Exercise:
- re-build and run
bin/helloworld-server - point your browser to http://localhost:8080/Go and notice "Hello Go World" response
- add a
Makefilerule that builds and runs the server - add a
Makefilerule that re-builds server and runs on a file change - check-in
vendor/manifestfile to your SCM - write
Makefilerule to restore dependencies
8. Deployment
Deploys are just copying the binary to a server and launching it once it's built for the platform it's going to be running on. Building could be done through cross-compilation or on a build machine.
But it's left as an exercise to the reader ;)
References
- repo with example code
- Official GOPATH description
- Official Getting Started Guide
- Official How to Write Go Code
- testing package documentation
- "an official package vendoring mechanism" should be announced with Go 1.6
- more docs
Thanks for reading and happy birthday to Go!