I've been writing Go for some time now, close to 7 years. But it wasn't where I originally started out in my tech career.
My first job was at a place where their core competency where backend and databases. Bootstrap, the defacto fronted library for multiple years at the time, was something completely new to them.
UI/UX was mostly an afterthought but since the clients were large accounting companies, it didn't matter. The clients were happy with the colour blue and lots square of boxes.
I also don't have a traditional background in computer science and have been self-taught all the way which probably was also why I ended up in frontend to begin with. It was the safer place to put me.
So I started learning the same way I do for everything else, typing "how to learn frontend" into Google.
React was just breaking away, in terms of popularity, from Angular at the time and was still in the good 'ol pre hook days. I eventually became proficient with React and would mainly write it (and JS/TS) for the next couple of years.
But, something kept happening. Everything seemed consistently out of date as new tools/libraries/frameworks were being shipped every day that you just had to use and keep up with.
As React grew, which it did immensely over the last ~10 years, they seem to think that every requirement known to man must be covered. The move away from classes and redux to functional components and hooks felt awesome, in the beginning. Now you need a PhD to efficiently use them and either manage a large state object being passed around or an alarming number of useState
in each component.
I, thankfully, don't write much React anymore and have gone back to the roots: plain old HTML which. And thanks some guy from Montana I still get 90% of the interactivity you get with a SPA app.
So, let me explain why I think you should use Go and why I fell in love with it.
If you just want the tl;dr: there often is only one way to do a thing and that let's you focus on building the product.
Limited choice
When writing Go, you'd often find that a solution to a problem tends to converge to a common conclusion across different teams/people/projects.
Some time ago I was frustrated that a validation library I was using didn't make it easy to do what I wanted it to. So I did what every other good engineer does and wasted a couple of days writing my own solution.
This was on my own time, after all, so fuck it.
About halfway through, I started to look into what other people had done and across multiple different projects, the solution to the problem was implemented in a very similar way.
Of course, this might be due to every library that comes after the first one copies the code. But that's not my general experience. Solutions to similar problems seem to converge due to Golang's strict feature set.
One criticism I often see is that Go doesn't allow developers to "express" themselves in code. Which, honestly, is bullshit. You're not Picasso, you get paid tons of money to sit in a cosy office and drink kombucha directly from tab.
Express yourself in system designs and elegant solutions if you'd like, but leave the code alone mate.
It's very difficult to spot an individual's contribution to a Go codebase without looking into the commit history. There is not much room to be eccentric or have your own style. Again, sounds rather boring but it ends up working extremely well when you care about building and shipping digital products.
The solution is what you focus on.
Simple syntax & easy onboarding
The syntax is very minimalistic with only 25 keywords to learn from which you probably need like, what, two-thirds?
It's incredibly fast to pick up. Whenever I have introduced Go to teams where I've worked as a consultant people generally write good Go after a week or two. Doesn't really matter if they're completely fresh out of school, work in a different way or have a lot of experience in another language.
People generally tend to pick up Go fast which, I think, comes from its readability.
To me, it's not as beautiful as you might look at a piece of Rust code you spend 8 story points writing, only to find out that it didn't match stakeholder expectations once it finishes compiling at the end of the sprint.
No, it's practical and boring and allows me to focus on what should be the more interesting part of my job, the problem. There is no need to argue over which linter style (or which linter) to use, it's decided for you and will be the same for everybody on the team.
Everything you need in one place
The standard library is fucking amazing. It brings a certain amount of calm to your projects as you can be certain that it will be maintained and won't have to make tons of rewrites to update.
Most things you want to build related to the web you can get done using just the standard library. There are other libraries that make certain parts better but they are not strictly necessary.
But, maybe just as important, the Golang toolchain ships with a linter and a standard for how to apply linting rules. This is generally something most developers have an opinion on that doesn't really add value, only take away value, if different styles are used. Just use one and get on with it.
Go is ripe for Web Development
In recent years, projects in the Go ecosystem have made it even easier to build full-stack apps only using Go.
The main thing, in my opinion, that has come out of the ecosystem that will help propel full-stack development in Go is Templ.
The creators behind it talk about (paraphrasing) that one of the "issues" we see is that lots of frontend/full-stack developers have only seen the SPA way of doing things. Hypermedia, anchor tags and forms are, in their native form, strange concepts. So, they wanted to create something that would allow them to think in components but still be completely valid HTML.
If you've ever done any Django, rails or Laravel development this way of doing frontends is most likely very familiar to you.
Use a base template to wrap the application, have some views for specific pages and abstract elements like buttons, tables etc into components for re-usability so that your API is no longer JSON-driven but hypermedia-driven.
You're creating a contract as you do with JSON-based APIs but now the consumer of the contract, the client, is much more tightly coupled to the API.
Ironically, this tigher coupling makes breaking changes less likely, lower maintenance and the system as a whole becomes easier to reason about. When developing this way, you don't have developers that's only frontend or backend. You must be both.
You can still have a preference, of course, but the two are much closer together so changes and their effects will be easier to spot.
A straightforward database layer
When I first started out in Go, the database layer was one of the most repetitive parts. Lots of "=+" for setting up query statements, scanning the rows into structures etc. You were writing sql but also not really.
Sqlc gets mentioned on forums quite often, and for good reasons, it lets you stay in the domain of SQL and solves the majority of queries I have.
It removes a lot of the boilerplate by generating type-safe code and takes care of scanning the results into what matches your database structure, validates that the fields are correct.
You'll have to reach for something else when doing dynamic queries, but adding something like squirrel to the mix is quite straightfoward. And since you can quite easily just add the generated methods from sqlc as a dependency to a database struct, having both is trivial.
Deployment is a joy
Once you've tasted the joy of single binary deployments it's hard to Go back.
You can go super simple: rent a VPS, add a service to Systemd and you're up and running.
But as you might have experienced if you've written some Go, it works very well with Docker. Given that it builds and outputs a single binary, you can get super slim docker images. Like 10mbs slim. Just getting started doing that with Python and you quickly approach 1gb. But storage is cheap I guess.
I tend to build a docker image that contains the app and some tooling, for things like running migrations, and I often end up with a docker image around 100mb.
Concurrency
It's rather hard to make a Go appreciation post without talking about concurrency. It's very good, but I honestly don't use it that often.
I usually take advantage of it whenever I have to do some data processing but in most of my work, it's not really that common. It's built into the packages you rely on and given its, comparatively, easy approach it becomes much more approachable.
It's there if I do need it.
Performant and efficient
This is always nice to throw in the mix when trying to shill Golang to other people.
Even though it's not the main reason for choosing Go it is very nice to know that your APIs are performant almost straight out of the box and don't eat up a lot of RAM. Depending on what you're doing this can have a financial benefit as you'd be using up fewer cloud resources.
I'm hosting quite a few apps on a small VM right now where the memory overhead is negligible.
Thanks for reading.
If you enjoyed this one you can check one some of these: