I’ve been around the block a couple times. I’ve built production services on soon-to-be dead open source projects more than once. I’ve been burned by hype and excitement in new technologies. It sucks. So if you are building a Go web app, which web framework should you pick?
Right now Go is super hot, and developers are all writing their own web frameworks in order to learn the language. Most of these will still be around in a year, but they aren’t going to be actively maintained. So I’m not giving a list or silly comparisons or benchmarks or any of that ridiculousness. Just some advice based on my remarkable history of failure.
- Choose the framework that you understand
- Choose best practices over cleverness
- Choose Age over beauty
- Ignore the active contributor count
- Ignore benchmarks
Choose The Framework You Understand
This is the most important advice. Even if your framework has a corporate backer and is popular, it may die or become orphaned. It’s a little early in Go web dev to have any one framework entrenched in the community so much that it will move on leaderless. But if you understand your framework then you can still maintain it into the future (provided you have the source).
I’ve maintained dead PHP frameworks and Python frameworks in production deployments. It can be a pain to have to build new functionality into a framework, but you can do it.
What I can’t possibly do is maintain database code. When the “NoSQL” movement got started, I chose a document database that seemed cool and just fizzled. I have no business trying to fix bugs and maintain that kind of a project so I had to migrate. I should have stuck with a working relational system until there was a clear choice in the NoSQL world.
Understand the code you are using. You may be annoyed in the future, but not in huge trouble.
Choose Best Practices Over Cleverness
As projects get larger, they come up with their own ways of doing things. This locks you into a framework and makes it difficult to add new functionality. If your framework dies, the value of an open source community disappears. You have to customize packages to fit your dead framework. So your framework better be pretty close to compatible.
This is our first actionable tip. Make sure your framework accepts Go standard library packages, and isn’t doing something custom. Let’s take a look at a standard Go http handler function:
If you are looking at a framework, you should make sure that your handlers can accept these two parameters. This does two things:
- Makes sure you will always be able to have direct access to the request and writer
- Let’s you know that any other packages expecting these arguments will work in the future.
Let’s look at some real world applications. Martini injects data into your handlers based on the arguments they specify. So you can require the HandlerFunc arguments to be passed, and Martini will give them to you.
Tiger Tonic also implements the HandlerFunc interface. However a very common use of Go is serving up JSON APIs. If you look at the Tiger Tonic Marshaler, you need to pass a specific signature which does not match the HandlerFunc interface. So you may run into additional coding challenges when implementing third party code.
As you look at all the available frameworks, you’ll notice that using the Gorilla Web Toolkit packages is pretty standard. Are you planning on adding Websockets to your app at any point in the future? Check out the GWT Websocket package. Notice that in order to upgrade a connection to a Websocket, you need to have access to the raw Request and ResponseWriter.
Choose Age Over Beauty
This may be more of a fascination with this silly expression, but I think it applies. Just like illness, software bugs only present themselves when a lot of people have been touching something. That shiny new framework is only shiny and new because nobody has used it yet. Go is new in general, so just beware. A lot of code may not have seen the horror of a production environment and the users it attracts.
I chose web.go to build a prototype application last year. It was a nice framework. I needed a lot of basic features that it didn’t have yet. I also had pretty severe time constraints so I had to maintain my own fork with my changes. As things progress, my implementations differ from those introduced into the main fork and now I’m using my own web framework or having to rewrite a lot of code.
It is always nice to have hard problems solved when you arrive. With a language as new as Go, this may not always be the case however.
Ignore the Active Contributor Count
Unless the project has already lost that new car smell, active contributors can fade pretty quick. Revel has a very healthy commit history. However looking at any specific time period you may have very few commits, or a ton of commits across several people. There is no data (that I know of, other than manual inspection anyway) to determine if these commits are features or bug fixes. Are they removing extraneous features? Or commented out code?
As the contributor count drops, you may be looking at a project with no more bugs! Or another new shiny framework may have popped up and everybody left for the new toy.
I just find this to be a hard metric to use to really quantify a projects future success.
If you have a counter example to this, then you should use the benchmarks in that counter example. But most often benchmarks are for “Hello World” cases which are best handled by a web server written in assembly.
If you are going to have users login and pull data, then you will probably be writing your own benchmarks to test sessions and database access and cache misses. You aren’t going to find any generic benchmark to help you out.
One pretty neat idea entering into the Go world is using a Trie for route parsing. I first saw this in ant0ine’s go-urlrouter package. Tiger Tonic uses it as well. This is a neat benchmark because it shows a significant speed improvement in the route lookup (over regular expressions). But remember, almost everything else out there (in any language) uses regular expressions to parse routes. While this is neat, you may be optimizing the wrong part of your program.