Wednesday | 24 DEC 2025

next

2025-12-13
Notes on Let's Go

golang

I'm currently reading Let's Go by Alex Edwards, it's a beginner book on doing web development in Go.

It's in the vein of something like Flask Megatutorial by Miguel Grinberg or my own A Web App in Rust. You build a snippet hosting site with authentication and a database in a series of steps. It feels familiar because it ultimately is familiar.

I can see the commonalities in these backend languages and there is a pattern that I think once you recognize it, it speeds up picking up a language, at least specifically for making web apps.

I can highly recommend the book though I've just finished the 2nd chapter. It explains every step but the book also expects quite a bit. One should be comforable with the command line, using curl, understanding html templating and more so I wouldn't call this book truly for beginners.

This book would be great for someone whos already worked in something like flask or express as the ideas map pretty well.

I have some nits to pick with Go but I have a feeling that things will get cleaned up further in the book and that we're doing things the hard way to learn the tech. I hope so anyway.

Print Debugging

fmt.Println("Something: ", variable)

Syntactic Slowdowns

  1. Using := for initialization

  2. Error handling code

  3. If statements without parens

  4. Trailing commas in structs

  5. When commas are needed versus when not

  6. Spaces between the struct name and curly brace

  7. Struct elements starting with lowercase are private

Project Structure

Once of the key takeaways from the book has been the project strucutre.

.
├── cmd
│   └── web
│       ├── handlers.go
│       ├── helpers.go
│       ├── main.go
│       ├── middleware.go
│       ├── routes.go
│       └── templates.go
├── data
│   └── snippets.db
├── go.mod
├── go.sum
├── internal
│   └── models
│       ├── errors.go
│       ├── snippets.go
│       └── users.go
├── tls
│   ├── cert.pem
│   └── key.pem
└── ui
    ├── efs.go
    ├── html
    │   ├── base.tmpl
    │   ├── pages
    │   │   ├── create.tmpl
    │   │   ├── home.tmpl
    │   │   ├── login.tmpl
    │   │   ├── signup.tmpl
    │   │   └── view.tmpl
    │   └── partials
    │       └── nav.tmpl
    └── static
        ├── css
        │   └── main.css
        ├── img
        │   ├── favicon.ico
        │   └── logo.png
        └── js
            └── main.js
            
15 directories, 26 files

This looks complex but as there are a number of directories and there is significant nesting and I can see the nesting getting deeper. However I think this structure is quite clean and it's similar to what I would have ended up with in express projects.

The only go specific magic is that the internal directory is a magic keyword where files in that directory can't be used by things outside of the current project.

After getting deeper into the book, I think this mapping doesn't map to how I think and so I find that I don't quite know where things are. It also looks like go imports things automatically so just by seeing a line of code, I don't actually know where that line might be coming from.

app.sessionManager

I don't know where is app actually defined. It's in main.go but I wouldn't be able to trace that out from inside routes.go. This might just be a lack of experience but it is non-obvious.

Databases Models

One thing that probably isn't going to change is the set up of models and how the database stuff is used. I prefer the way of dynamically querying and managing the data. It's what I usually reach for in nodejs and python and I find that it's much easier to develop for.

This might be the right way where you have a model file and you have the database with a strict schema but I find that it's too much boilerplate for most hobby projects.

I'm planning to rewrite the database logic my own way once I finish the book. This project should let me create a nice little template I can quickly use to build an application.

Using SQLite

The book uses MySQL and goes through the set up. I would have preferred that it had used sqlite as I think that's a better choice for a hobbyist, especially one reading this book.

Luckily it was straightforward switch to using sqlite instead of MySQL.

The first step is to get the go sqlite package:

go get modernc.org/sqlite

The example SQL commands do need to be tailored for sqlite. This is the insert statement.

stmt := `
    INSERT INTO snippets 
(title, content, created, expires) 
    VALUES
    (?, ?, datetime('now'), datetime('now', '+' || ? || ' days'))`

I also did need to change the open function to use a file rather than a connection string:

	db, err := sql.Open("sqlite", "./data/snippets.db")
	if err != nil {
		logger.Error(err.Error())
		os.Exit(1)
	}

	err = db.Ping()
	if err != nil {
		db.Close()
		logger.Error(err.Error())
		os.Exit(1)
	}

	defer db.Close()

Templating Language

The templating language looks weak. You need to define all the template files a page uses in the code instead of templates being parsed to find out what they reference.

Templates also look to use a different language structure, specifically a stack machine for doing things so it looks to be cumbersome to write.

I also don't have automatic indentations but it does use the same double curly brace that I have used for my own templating language.

The book builds a render function that simplifies the code but it still seems a bit much that there isn't a simple render function already built in. I can see people copy and pasting the same render function into every new project.

Forms

The forms logic looks to be very verbose and the book shows you how to do it once and then starts to use a library to simplify things. The book does create a validator helper function but I skipped this step for now as I don't like the indirection.

It might be a good idea to find a good form library to simplify this stuff or to figure out a better pattern. Currently there is too much boilerplate that gets repeated for all the forms.

Sessions

Sessions are done using the package scs, the examples in the book use mysql to back the sessions but I used the in memory option to simplify things.

Chaining the middleware is starting to get unwieldy and I can see why you would want to pull in a library to handle this.

Server Security

The book uses a go tool to generate ssl certificates. After using curl and the command line it would have been better to give the openssl commands in my opinion.

It's also not clear if using go to do the SSL is a good idea versus using nginx as the ssl terminator.

I wonder what the best deployment strategy is for a go web application.

User Authentication

I did the user authentication myself as given everything covered so far, I thought it would be a good test.

I'm happy to report that it was easy enough to get everything working. I didn't do the password hashing but I didn't know which library the book would use and I didn't set up the middleware as I'm not completely comfortable with it yet.

It looks like my version matches pretty well to the book and adding in the hashing and middleware was trivial.

The route chaining is now annoying and I can see that using a library for this is very much needed.

The CSRF protection is also useful to know, I didn't implement it but things seem to be spread out too much across many files.

Request Context

I don't see why using the request context is better than adding to the session manager. Them example of checking the database for the user to make sure the user hasn't been deleted is a good one but you could put the logic in right into the requireAuthenication middleware rather than creating a new one.

Now there is an authenticate middleware which will check if the user has been deleted and then there is a middleware that requires authentication to access the routes.

Laid out this way it actually does make sense, I've explained my way into the books suggestions.

The reason is because the requireAuthentication middleware runs later and may not run at all. Each page displays things based on if the user is authenticated or not and so we want to check every request to see if the authentication is still valid.

I still don't understand why it can't be part of the session rather than the context but I do see why it's split now.

Embedding Files

This chapter sounds pretty cool. and indeed it was. Embedding the static and templates was straightforward and building the project was also easy.

Now I have a single binary that has everything it needs to just work.

The filesize is also quite small at 18mb.

go build -o /tmp/web ./cmd/web
cp -r ./tls /tmp/
cp -r ./data /tmp/

I needed to copy over the tls and data directories as those are not embedded in the binary.

Testing

I skipped this chapter as it got into the weeds of testing and that is far less fun.

In the future if I want to build something production grade, I can see this coming in handy. The testing chapter looks thorough and goes over unit tests, mocking, integration tests, end to end testing and code coverage.

Closing Thoughts

I had a good time with the book and it reinforced my backend knowledge quite a bit. This is another tick in the idea that the language ultimately doesn't matter, the core ideas of serving data are is the same across languages.

The book is written well and it fits my brain in the way its structured. I do wish there was more effort on keeping things in a minimal number of directories and files. I like things to be co-located and this spreading of code is a bit hard to keep in my head.

I also bought the second book with the discount code as the first book was quite worth it. I think I could probably bumble my way into writing some real go code for a project that I have in mind but an API server as the next project fits the bill quite nicely for my future plans.