Get ready to read the word Go a lot!
Go is a new programming language built by Rob Pike, Robert Griesemer and Ken Thompson. It's a statically typed language with a gargabe collector and a kickass native concurrent-style programming model.
Go is mainly about three things:
- Go is about composition. It is kind of objected oriented just not in the usual way. There are no classes(but methods can be defined on any type), no subtype inheritance and interfaces are satisfied implicitly(we have structural typing). This results in simple pieces connected by small interfaces.
- Go is about concurrency. Go provides CSP-like concurrency primitives, it has lightweight threads called goroutines and typed thread-safe communication and synchronization with channels. This results in comprehensible concurrent code.
- Go is about gophers!!
In fact, we even have our own website where we can create our own Gophers. Thanks to Mat Ryer and Ashley McNamara for making this.
First download and install Go, you can follow this video tutorial or you can head over to Go's website and follow the installation section.
Now you need to setup the development environment. Go organizes the code in what it's called a workspace
. Here is a tutorial from Go's website on how to set it up.
As for what to use you have lots of options. All the mostly used editors have very good support for Go, VisualStudioCode has a very good and highly maintained Go plugin. If are a vim user you are in luck, we have a kickass vim-go plugin as well. Atom has very good support too.
If you don't already know git then head over to this tutorials to learn a bit. It will be necessary for this course but also along your career as a developer.
Here is a very good introductory talk given by Andrew Gerrand, one of the core Go team members.
Before each code example you will find a link to a website called GoPlay space were you can run the code and look at the documentation of packages all in the same place.
Ok, head over to Golang fundamentals to get started!
INFO: Along the course you will encounter a few challenges that will ask you to implement a solution that it's "idiomatic go". It might be hard to pin-point what idiomatic go looks like, to help you with this here is a guide from the Go team called Effective Go. This guide highlight most of Go's good practices.
Implement an in-memory DB.
The requested in-memory DB is a persistence tool that allow us to store data in memory by responding to basic CRUD operations:
- Create
- Retrieve
- Update
- Delete
You are going to be implementing this in-memory database with persistence on files too, later. You'll be working on this database through out the week, the excercise is divided into a few sections that will require you to implement the requested functionality before going to the next section.
Inside your GOPATH you will have the src directory, this directory may or may not have the github.com folder, if it doesn't go ahead and create it. Inside the github.com folder create a folder that matches your github username. Finally inside that folder create a new one called db
When developing in Go often times you will start by defining an interface that will expose the methods of whatever it is you are implementing. In this case you are implementing a database, so think throughly of what methods you will need to implement, think what operations are tipically done on a database, do you need to open it or close it? Once you have the interface well defined you can start to code.
Here is a very good talk by Francesc Campoy explaining how interfaces work in Go and how they worked: Understanding Go interfaces
This is an in-memory database which means that all the data will be kept in some data structure during program execution. Since what you are storing is key
-value
pairs you could use golang's builtin map
structure. If you don't remember how they worked and what operations you could do head back to the Fundaments section to do a little recap.
Here are a few articles that delve into how maps are implemented in Go and explains just a bit about Go's memory model
- How the Go runtime implements maps efficiently
- If a map isn't a reference variable then what is it?
- There is no pass by reference in Go
Go has a builting library called testing this library provides us with primitives to perform tests. Although this library does not give us any type of assert or mocks or anything like that.
To perform unit tests in Go you will tipically create a file called ggg_test.go
where ggg
is the name of the file you are testing. Inside that file you will create all your test functions, the convetion for the signature of a test function is like this: func TestNameOfFuncBeingTested(t *testing.T)
. Once you have that done you can run go test
in the command line and see the results of your tests. Here is a simple example of testing a Sum
function using what it's called table testing:
sum.go
-----
package sum
func Sum(a, b int) int {
return a + b
}
-----
sum_test.go
-----
package sum
func TestSum(t *testing.T) {
cases := []struct{
name string
a, b int
expected int
}{
{name: "equals zero", a: 1, b: -1, expected: 0},
{name: "equals 2", a: 1, b: 1, expected: 2},
}
for c, _ := range cases {
t.Run(c.name, func(tt *testing.T){
if res := Sum(c.a, c.b); res != c.expected {
tt.Errorf("expected %d but got %d", c.expected, res)
}
})
}
}
Try to run the tests of that code locally and see what happens.
Here are a couple of useful articles on how to perform testing in Go:
- Writing unit tests in Go
- Tips and tricks for writing unit tests in Go
- Advanced testing techniques in Go
What happens when you stop the program you just wrote? Does the data persist on disk?
Right know we are storing everything in-memory so as soon as the program exits we loose all that data. How would you fix this? A simple approach would be to simply use files and dump all the data into a particular file as soon as the program finishes. You would also have to load all the data that a given file has when you are initializing your DB right?
Go packages that might be useful for this:
Note: it's not a need to dump data to file while DB session is open
Say you want to use this database in an HTTP server that you have running in production, this server is probably serving more than one request at a time. What happens if both requests try to use the DB to write some data into the same section of the map? You'll have a race condition which will cause your http server to panic. Panics are not good, that's why they are called panic. To avoid this you need to guard the operations that are done on the map using whatever Go provides for this. We talked about something that does this in the Fundamentals section.
Once you've implemented that you should write a bit of code that would perform multiple operations on the DB at the same time(look into how the testing.T.Run()
method works). Remember to run your tests with the race
flag this will allow the go runtime to inspect whether your code has any race conditions.
Useful reading and libraries:
If you are not familiar with REST, it would be necessary to get introduced into it:
In order to proceed you should have a good understanding of HTTP, and what RESTFul architectures are.
The exercise in this section it design a RESTFul API which exposes web services for managing a Shopping Cart. The features needed are:
- Create a new cart
- Adding items to a cart
- List all items of a specific cart
- Changing the quantity of an existent item in a cart
- Removing an item from a cart
- Clear a specific cart (remove all items).
Note: Available articles to be added to the shopping cart are provided by the following third party web service.
Method | URL | Desc |
---|---|---|
GET | http://challenge.getsandbox.com/articles | To get all available articles |
GET | http://challenge.getsandbox.com/articles/{articleId} | To get an specific artible by id. It returns 404 if the articleId is not found |
Important: In order to consider an API well defined, you should consider a way to describe all the endpoints as much as possible, i.e.:
- URIs with methods allowed
- headers, if needed
- parameters (path and query params)
- request & response bodys required with format type and expected values (payloads)
- http statuses responses
Before you start writing the code for your API head over to our HTTP Services in Go section to learn the basics of writing servers in Go.
Once the API is defined, we are ready to implement it. There are quite a lot of go libraries for creating HTTP routers and handlers. But to learn more about how all of it works use one of the following two options:
- Go Native: https://godoc.org/net/http
- Gorilla mux: https://godoc.org/github.com/gorilla/mux
You should also consider adding some integration testing for the implemented API, using https://godoc.org/net/http/httptest. Consider also taking a look at:
At this point the RESTFul API is already implemented and working fine, so, the next challenge is to change the persistence engine. So, you need to change the in-memory DB used with a SQL and NoSQL local DBs. You have to connect you application to a local MongoDB and MySQL
- MongoDB: https://godoc.org/github.com/mongodb/mongo-go-driver/mongo
- MySQL: https://godoc.org/github.com/go-sql-driver/mysql
If you have enough time, it would be nice to containerise your application and the DB using Docker and docker-compose.
Remember that Go is a compiled language, this allows us to build a single binary that we can just drop in whatever container/vm/machine we want. Take a look at the docker scratch
image and see how it works. Also remember that with the Go compiler you can do cross compilation, say you are working on macOS and you want to compile something that would work on a linux machine that has the same processor architecture, you could then do something like this: GOOS=linux go build
. To learn more about cross compilation you can red this blog post.
Once you have your application containerised you need to connect it to the DB. There are availabile images for both mongo and MySQL databases. If you are not familiar with docker-compose
you can follow this tutorial that explains a bit what it is and how to use it.