Does golang Make a Good Web Development Framework? Here Are Some Things to Consider

Sometime between selling citizens’ sensitive data and buying out fledgling startups, Google decided to create it’s own programming language: go (or “golang” if you want search results to be relevant). Golang is rapidly joining Python, Node, and Ruby as a substantial language for designing web applications.

But is it worth it? I’ve been a part of go-based projects as well as been the principal designer for go-based full stack apps. Here’s my own take, as I give my professional observation on the pros, cons, ins and outs of golang as a web development framework.

I’m sure many people will say, “Well, yeah, that’s a golang feature!” with a lot of these points, but they’re still good to consider when deciding to build your application with it.

Package Source Management Is Exact…

Have you ever spent an afternoon trying to get project packages to resolve only to find out a project author used a specific version from GitHub and didn’t include it in the documentation?

Golang gets around this by letting (rather, requiring) you specify the source of the packages. This makes installation an breeze:

package mypackage

import (
"github.com/gin-gonic/gin"
)

…But Doesn’t Allow Local Imports

However, you even have to do this for local packages. So you CANNOT do this…

package mypackage

// THIS IS BAD GO CODE -- DO NOT USE
import (
"..models/basic"
)

Instead, you must first name your module, then reference the FULL MODULE PATH as if it’s already remote.

// Within go.mod
module github.com/my-organization/my-project

// Within your project
package mypackage
import (
"github.com/my-organization/my-project/models"
)

Structures Allow for Database Schema Description

type Employee struct {
Id int64 `db:"id" goqu:"skipinsert"`
Name string `db:"name"`
DepartmentId int64 `db:"departmentid"`
CreatedVal int64 `db:"created"`
ModifiedVal int64 `db:"modified"`
}

Depending on the library you’re using for the database, you may be able to add metadata to each struct. Gone are the days of needing to make sure database schema matches with the struct!

This allows for “scanning” the database row into a structure:

employee := Employee{}
rows.Scan(&employee.Id, &employee.Name, &employee.DepartmentId, &employee.CreatedVal, &employee.ModifiedVal)

Universal Database Connection Is Still Not Fully Realized

Currently database connections are kind of wild west. For those used to frameworks like Django, Flask, or Knex, often you’ll need to do some extra work to ensure database migrations work.

Primarily this is due to the fact that migrations still do not have a universal connector. The migrate package is the most widely-adopted migration package for SQL, but there’s no agnostic way to perform a database migration. You must specify raw SQL code to perform a migration.

Continually Checking for Errors is Annoying

I guess not having exceptions is memory efficient, or cool, or something like that (any go fanboy has their own reasoning), but you have to continually check for errors, just like in C. So in your code you’ll be doing a lot of this:

err, val := fetchValue()
if err != nil {
return err, nil
}
err, otherVal := fetchValue()
if err != nil {
return err, nil
}

Like I said, I’m sure some go fanboys are typing in the comments right now defending the use of this go feature. But for those used to try/catch it can be very awkward.

There’s no “Reference Frame” for Paths

Typically in an application, you’ll want a “frame of reference” for constructing full paths.

In Python, using __path__ can provide a reference point. In JavaScript, you can use __filename or __dirname .

This is especially useful for unit testing, because you can make all relative paths as absolutes, eliminating any ambiguity.

But this is not the case for golang. There are ways of determining the current execution path, but there’s no way to find out myfile.go ’s parent directory.

I found this frustrating when I was running unit tests on sqlite. I was specifying the sqlite database file like this:

DB="sqlite:///../test.db"

However, .. was changing throughout the course of the application, so any migrations were being applied to another test db, either one directory above, or one directory below.

Yes, Python or JavaScript would have this issue as well, but there’s no way in the code to “resolve” this path before passing it to the database connector.

I eventually had to resort to setting it to an absolute path. Not ideal, but since this is stored in a .env file (that’s we all add to .gitignore, right?) it works alright.

There’s No Implicit Arguments

When you define a view/model method/etc., you must specify ALL arguments in a function.

So with TypeScript you can do this:

export function doSomething(request : Request, id : number = null) {
if (id) {
return [User.get(id)];
} else {
return User.all();
}
}

// later in the program...
const meUser = doSomething(32)[0];
const allUsers = doSomething();

However, with go, you can’t do this. Either you must pass in an nill-like value (like 0 ) for id, or define 2 functions:

func getUser(request *Request, id int) *User {
return DB.Query("SELECT * FROM user WHERE id=%d", id)
}
func getUsers(request *Request) []*User {
return DB.Query("SELECT * FROM user")
}

As you can imagine this can get quite overwhelming when you’re naming views for your paths. While this does constitute a separation of concerns, it does violate the D.R.Y (Don’t Repeat Yourself) principle of development.

Should You Choose Go?

I mean, really, that’s up to you. You’re the developer. Is your team comfortable with go? Are you comfortable with it?

This article, I might come across as critical of go. My professional opinion is that go (as a language) isn’t that bad. Any language you use will have quirks, strengths, and drawbacks. Any new language touts that it fixes issues from another, older, language, but will have it’s own flaws that will be fixed by yet another language.

Most of the major programming languages (JavaScript, Python, Java…heck — even PHP!) don’t seem to be going anywhere fast. So I don’t think your business is going to break if you choose Python over Go or Go over Python.

What matters more is how you structure your application. Are you testing everything? Did you include documentation? Are you using a linter? These things are what matters. I’d trust a C++-based website developed overnight that includes best programming practices over a TypeScript application that is 5 years into development with no git repo, documentation, or unit testing.

If your tech stack is in the latter category, it’s okay. I can get you on track. I’m a freelance tech lead and can get your project on track to a solid application your team can be proud of.

I have over 10 years development experience and would love to have a conversation with you.

👉‍‍ Head on over to https://damngood.tech to schedule a free consultation.

❤️ Send some love my way. If you’d like to support my work on these articles, please donate over at Kofi: https://ko-fi.com/damngood. Get articles 3 days early and get shout-outs on social media!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store