Using CSS with Gin
This is the seventh in a series of articles about writing a small reading list app in Go for personal use.
So far we’ve built a sort-of usable app with Gin. We can see the list of books we have in the system, and we can add new books. But there are some serious usability problems:
- there’s no link to the “New Book” page, or to the “Book List” page – you have to know the URLs and type them in directly
- both the input form and the list suffer from poor layout, almost to the point of being unreadable
In this article we will fix those issues and set up a foundation so that future work will have established patterns for styling.
When we’re done you’ll have:
- external CSS and a way to reference it from the pages in the app
- a navbar to be able to easily click around the app
- some options for managing that external CSS with the app’s deployment
Fair warning: I’m not a CSS expert, so don’t assume that anything I’m doing with the actual CSS is “the right way”. I’m more interested in demonstrating the mechanisms and patterns that we can use for integrating these pieces. When I need stuff to actually look nice and be maintainable and scalable I talk to real designers.
Validating Forms With Gin
This is the sixth in a series of articles about writing a small reading list app in Go for personal use.
When we created a form to add new books, there were a couple of data-validation issues that I said I would handle in a future article. The future is now!
As we saw in that article, Gin has support for easily binding form data to a struct. It also has a fairly rich set of form validation capabilities, provided by go-playground/validator. Let’s explore those now.
At the end of this article, you will:
- have better data validation for the form
- have better error reporting for data validation errors
Dependency Management Is More Than Just Package Management
Dependency Management is a Big Deal, it’s important, and yet a lot of projects – and some entire ecosystems – do a terrible job at it. Often it seems that people either pretend it doesn’t matter or they just ignore it. (Or are unaware of the need for dependency management.)
Go does a great job at helping developers with package management, but dependency management is more than just package management.
Here are some strategies to follow to do a better job of dependency management.

What’s the weakest link in your dependency chain? Photo by Alan Levine CC-BY
Improve CI with Static Analysis
This is the fifth in a series of articles about writing a small reading list app in Go for personal use.
A big part of developing quality software, especially in larger projects, is to make sure that your process, tools, and workflows can scale with the size of the project. The project we’re working on is tiny in the grand scheme of things, but let’s take this week to “sharpen the saw” a little bit and improve our process before diving back into functionality next week.
Earlier this week I shared some rules for semgrep, a static analyzer that we can use to find defects in Go web apps. Today we will integrate that into aklatan’s Makefile and CI pipeline.
At the end of this article you’ll:
- have semgrep with custom rules integrated into the CI pipeline
- understand the pros and cons of maintaining the rules alongside the application code (as opposed to keeping them in a separate repo)

A water pipeline in the Tagus-Segura Water Transfer in Spain. Photo by flicker user M.Peinado CC-BY
11 Semgrep Rules for Go Web Projects
I’ve mentioned semgrep a few times in recent articles, and I thought it would be good to introduce this new(ish) tool and demonstrate a few rules that you can use to find problems in your Go web apps.
At the end of this article you will:
- understand what semgrep is and what it can do
- have some idea of the limits of semgrep’s power
- have some rules that you can immediately apply to your own projects
Fair warning: I am not a semgrep expert by any stretch of the imagination. If you are, and you think these rules can be improved, please drop a note to brian at universalglue.dev.

Semgrep isn’t named for semaphore flags… but it does offer a pretty good signal.
How to Handle Forms with Gin
This is the fourth in a series of articles about writing a small reading list app in Go for personal use.
(Don’t be scared off by the length – there’s a lot of test code that’s shown multiple times due to enhancements.)
This article builds the C of our CRUD app:
- add a template with a form to enter books
- add routes to GET and POST that template
By the end of this article you’ll have:
- a page with a form that you can use to add books to the database
- tests around form handling
- a quick workflow hack to speed up testing when writing a test

This is for posting a completely different kind of form. (Photo by RuthAS, CC-BY.)
Prioritizing Tests with the Feedback Pyramid
A fair bit has been written about the idea of a “Testing Pyramid”.
I tweeted an idle thought about reshaping this concept into a “Feedback Pyramid”.
I wonder if "Feedback Pyramid" is a better way to think about the testing pyramid? pic.twitter.com/4GUr2an1Ez
— Brian St. Pierre (@bstpierre) March 2, 2022
In this post I will:
- provide a more complete picture of the feedback pyramid, including some things that aren’t typically included in the testing pyramid,
- show how to prioritize tests and other checks based on how they provide feedback, and
- show how to set up your tests and other checks so that they provide you the right kind of feedback, at the right time.
How to Test Gin Web Handlers
This is the third in a series of articles about writing a small reading list app in Go for personal use.
So far our app can show a page with a list of books that are in our database. It even has a unit test… but not a very good one. Today we’ll look at what it means to do a decent job testing a Gin handler function.
When we’re done you’ll have a test suite that thoroughly exercises the book list handler. You’ll have 100% test coverage of that handler, and you’ll also understand why that’s only a starting point.
Plus a free bonus that neatens up our CI pipeline.

A different kind of test.
Tighten Up Your Feedback Loops
Integrating Gorm with Gin
This is the second in a series of articles about writing a small reading list app in Go for personal use.

The British Museum Reading Room. Image by Wikipedia User:Diliff (CC-BY).
Today we are going to make part of the R of our CRUD app:
- add a model (maps to a db table)
- add a template to display books
- add a route that displays that template
- change our default route to redirect
By the end of this article we will be able to load a page that displays the books in the database. (But we’ll still have to manually add books to the database.)