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
Run
to execute - press
Share
to 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 $GOPATH
set up required environmentcd 03-go-run/
(code)- run
go run hello_world.go
to get the"hello go world"
output - run
go fmt hello_world.go
to 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 fmt
both 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-deps
to install development dependencies - run
make re-test
and watch the test fail - fix the failing test and see it
PASS
immediately!
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
hello
function has been renamed toHello
to be exported and accessible from outside the package helloworld
is now a package (notepackage helloworld
insteadpackage main
insrc/helloworld/*.go
)- there are now separate "entry points" (or executables) source of which located under
src/cmd
Makefile
now has abuild
rule that builds the entry points/executables tobin
directory- Gb is our new development dependency
Exercise:
cd 06-PackagesAndProjectLayout
(code)- run
make dev-deps
to install new dependencies - run
make re-test
, notice failing test - fix the failing test
- run
make build
to get binaries built - run
bin/helloworld
ensure it printshello go world
- run
bin/helloworld-server
and 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
index
function 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
Makefile
rule that builds and runs the server - add a
Makefile
rule that re-builds server and runs on a file change - check-in
vendor/manifest
file to your SCM - write
Makefile
rule 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
!