I recently got a DevOps job that mostly involves writing a new backend system in Go completely from scratch. Here's what I learned having never actually used it in production, knowing it mostly just from personal projects.
1. You (probably) should use a web framework
At the start, we decided to try simply sticking with Go's http library and a simple routing library - mux
.
However, I quickly ran into real-life, production problems:
- Recovery middleware - for logging & silently handling panics in handler's code.
- Logging - I wanted some solution that would print information about each request, including body params, auth tokens, etc. (for debugging purposes).
- Better error handling - I wanted the errors to still be JSON responses with an error message and code.
- Other commonly-used middleware - including JWT auth and CORS.
I had two options; implement solutions to the above problems myself, use different 3rd party libraries for each problem, or pick a web framework that already does most (if not all) of these things.
I eventually decided to use the Echo web framework. With almost 20k GitHub stars, a pretty active community, and great documentation I thought it was a great tool for the job.
I also found there to be a little less boilerplate when it comes to writing the apps in echo
(mainly when parsing a json body, writing errors, and manually setting headers), leading to improved code readability.
However the real difference in productivity will be noticed when you have slightly more complex endpoints. You'll often run into cases where you need to validate certain JSON fields, and you'll want meaningful error messages describing what's wrong. If you want to do that without any library, your code will quickly become much harder to read:
2. You NEED a good code structure
Go web frameworks (or go in general) don't enforce any particular file structure. If you ever used something like ASP.NET/ASP.NET Core, you'll know what I'm talking about when I say that some frameworks are tightly structured and many things are done implicitly by convention rather than explicitly specified.
The thing about Go is that it's really easy to skip learning about structuring your code and make it a hard to read+maintain mess. If you still don't know what I'm talking about, here's an example of a (bad) Go endpoint I wrote a while back:
Do you see what I mean? It's quite likely that the in total the "better" way will contain more lines in total after adding all the CreateUser
and CreateAgency
methods, but… It will be much easier to understand, reuse, debug, and modify later on as each method will have a single purpose. If you haven't already, I highly recommend you have a look at the following resources for a good code structure:
In general, the concept is simple. You should separate the code that communicates with the database from the actual application logic itself, which should also stay separate from the transport/endpoint logic (in this example the HTTP endpoints).
3. Pick your SQL driver wisely
When I first started programming in Go, I wanted to use the least libraries possible, so of course I opted in for using the database/sql
package (with Postgres). Although the experience was OK, I came across quite a lot of boilerplate when querying data, especially having to use the Scan syntax. This lead me to the following 2 options:
sqlx
- a lightweight wrapper on top ofdatabase/sql
with some extensions that will make querying much easier.gorm
- an ORM (Object-Relational Mapping) library for Go, which generates SQL models and queries based on your Go models.
I don't think there's a clear "better" library, in the end it comes down to the use case and preference.
gorm
will probably make your life easier, especially if you often forget to add fields to queries after altering the database (because in gorm you won't have to do that at all).
sqlx
on the other hand is much more SQL centric, it's more like writing Go code to interface with SQL rather than gorm 's approach of generating SQL from Go code. It's nice if you prefer to have total control over your SQL and not having to learn new syntax of GORM.
4. Docker
One of the challenges I came across was configuring the project for production. There are always some differences between the dev and prod environments, such as what port the app should run on, the host and credentials for the database, and etc.
I've seen people configure their app variables via JSON, YAML, or even gitignored .go files. I personally found env files to work the best, especially with docker-compose:
I usually combine these with the following utility functions: Building the Docker image is also extremely easy in Go.:
5. Anything else?
There are also a few other things that came to my mind that I didn't consider worth their own sections:
from Hacker News https://ift.tt/38knrPe
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.