Apparently today is the day to post links to alternative web frameworks. Right on.
Incidentally, Revel achieves an exceptional framework overhead rating in our tests. In our i7 Fortunes test [1], Revel performs at 96.5% of its platform, Go. (See rightmost tab on the results panel.)
Wouldn't this be better described as Framework Efficiency? The framework operates at 96.5% efficiency, not with 96.5% overhead. At 96.5% efficiency, the overhead is 3.6%.
Yes. However, I want to expressly avoid the word "efficiency" because it is our intent to eventually capture some metric(s) related to developer efficiency (e.g., lines of code and so on). I don't want the word efficiency to then be ambiguous, at least within our results site.
I struggled for a while with various alternatives, but ultimately went with "overhead" even though, as you point out, a higher number is better. I'm open to other suggestions, but for the time being, I'd like to avoid efficiency.
Edit: The trouble with inverting the metric ("lower is better") is that in some cases, a framework achieves superior performance than its underlying platform due to custom components or test implementation particulars. See for instance the multiple query test in which presently Revel exceeds Go (I know at least one Go SME is researching why this is).
Call it "framework throughput". Then explain that it is the portion of the raw performance of the nominal underlying platform that is available to serve pages.
In a footnote you can point out that frameworks can do things like offload work to specialized components to get throughput above 100% on your tests.
As others have mentioned, it was really quite confusing to see "higher is better" and then "lower is better" in the same set of graphs. Stick with one convention. It works. :)
Interesting. That is actually an argument to leave the overhead chart as-is (possibly renamed) but change the latency chart to a "higher is better" chart in some way. I don't disagree that a single convention would be preferable, but it would be unusual to represent latency in a higher-is-better fashion.
In this day and age, why don't most web frameworks consider security features to be a selling point? Revel, for example, doesn't mention it once on their front page, and only 5 results in their Google Group when searching for "security" [1].
Is it just the case that speed - both of development and request/response performance - sells, and security is considered a solved problem, or something that should be implemented at the project level rather than framework level?
Lift explicitly mentions baked-in security as one of the seven things that sets it apart from most frameworks [1][2], because it's absolutely one of the most important aspects of it and any framework.
The fact that it's not shouted from the rooftops (though it is on the front page) is something I'd rather like to change when I get a chance.
Which security features are you thinking of? The only unmitigated successes I can think of are pretty standard and hard to market --- default XSS string tainting/quoting and CSRF protection.
http://cakephp.org/ (not great but at least they pay some lip service to it on the home page, implying more details can be found digging around in the docs/source)
Because even if a web framework had a multitude of security features, there's still so much a developer can do with the framework that would introduce unforeseen security gaps. This somewhat makes it a liability to advertise security. The first security breach of a system built with the framework would completely undermine one of the differentiating features of the framework.
What kills me about rich web frameworks for Golang is that the ORM problem for Golang is still open; there isn't a "mainstream" best ORM to use, and so frameworks don't have tight integration with storage; there's no "model" layer to work with.
If a framework is going to solve approximately the same set of problems for me as Sinatra, I'm not sure that's a big enough win over what Golang already ships with.
I agree, but a side effect of there being no mainstream ORMs is that the frameworks themselves don't have well-defined model layers. They're routing, filter, and template systems, and Golang's standard library does a pretty good job of that already.
I'm aware that many people don't use ORMs and feel they cause more problems than they solve. However, I'm struggling to think of a popular web framework in any language that doesn't include or suggest a specific ORM.
So out of curiosity, when you do web development, do you use an ORM-less framework, or do you simply not use the ORM that's included/recommended?
There's certainly not an Active Record -esque ORM for Go, but that doesn't prevent you from writing a very simple model layer. Write a few structs to represent your models, and use database/sql (or another DB interface) to unmarshal your response into the struct as needed.
i.e.
type User struct {
Id int
Email string
OAuthId string
Created time.Time
Active bool
}
user := &User{}
err := r.Table("heroes").Get(user_id).Run(session).One(&user)
fmt.Printf("%s", user.Email)
Obviously the example above is very minimal, but you can see how straightforward it is to describe "models" and query against them.
I'm finding that the code to do this is no more verbose (if at all) than writing out my models.py in Django.
Lets just start with: no comprehensive test suite to make sure drivers work, drivers that don't work (or work 'if you use the api in a particular way only; which defeats the purpose of having an api), no cross database support.
There are some good database apis, but using database/sql wouldn't be my advice (hint: use a c binding to a cpp orm).
I haven't heard of lots of people using database/sql; most shy around from it for mongo or similar as far as I'm aware.
I've been using Gorp in a way pretty similar to the booking[1] sample app. It's better than nothing, but still a ways from something like DBIx::Class or SQLAlchemy.
SQLAlchemy is a tough act to follow... Unfortunately. We see the same problem over in Haskell land - it's slightly more difficult there because Haskell will never have something like an "ORM", it's more like an "RRM" (RecordRelationalMapper").
Nothing! I think HaskellDB is great (I've only used it twice and I've only had a cursory study of its features - so correct me if I'm wrong anywhere); what's particularly wonderful in SQLAlchemy is the powerful relationship features. One to many, many to many, many to one relationships are auto-populated (lazily or eagerly, depending on which strategy you want to use - joins, extra queries, subqueries, etc...).
I want to replicate SQLAlchemy's relationship and ORM features in Haskell; I haven't sat down to work on it yet, that's my next free-time project.
I agree 100% that the current ORM situation is early. The existing ones [1] are already very useful for simple mapping from result set to structs, and they are still under development.
Out of curiosity, what ORM do you particularly like?
My ORM experience is mostly Hibernate and some Django, and I think that they are both too advanced -- I ended up with a deeper understanding of Hibernate than I would care to admit, but it is really a minefield for developers.
It seems to me that the sweet spot is between Gorp and Hibernate -- the ORM should be able to load collection-valued properties on my struct, but it doesn't need to have programmatic query builders, a session cache, etc. I would prefer to write plain SQL, and treat the ORM as a way to map the result set into Go (with sugar for common cases like FindById and FindAll).
[1] Gorp, Jet, QBS
===
On the topic of tight integration with ORM -- I was hoping to understand what kind of integration you had in mind?
For example, I used my own ORM on top of Play 1.x, and it didn't feel any different than using Django ORM, after adding a Play Plugin that started / committed / rolled back transactions for me.
Is that the extent of integration that you meant?
===
I'll reply to the "Why not vanilla Go" question in a separate comment, since it is raised a number of times.
It drives me nuts that new frameworks announce that they've stuffed all your app's routing into one file like Rails does as if that's a feature. I'm not sure I've ever seen a large rails app where the routing doesn't become an absolute rats nest.
MyApp::Application.routes.draw do
load 'config/routes/more_routes.rb'
end
I agree that routes files have a tendency to turn nightmarish (combination of low rate of change once set and you almost never have a reason to read them.) At the same time, if you're routes are getting intimidating it may be time to break out application into separate concerns.
> At the same time, if you're routes are getting intimidating it may be time to break out application into separate concerns.
I agree. But at the same time, not a lot of frameworks out there really help you make your app more modular. Rails certainly didn't until relatively recently (late rails 2, big improvements in rails 3).
It's certainly possible, but it largely depends on how you do it (separation of concerns and all that). You can have a rats nest of header files in C or C++ as well, but no one suggests the solution to that is to only have one header file.
Revel worked really well for a project of mine with MongoDB... a DB that doesn't really play well with traditional ORMs. The idiomatic Marshall & Unmarshall features of the language carry over to the Mgo driver. Basically, Go already has an ORM, if you use it this way. I can use the same definition to consume XML into a native struct, manipulate, and then marshal a JSON response to the browser and a BSON value to the DB.
Initially, this was just a fun project to see if I could replicate the magical Play! 1.x experience in much-less-magical Go. (Spoiler alert: yes)
Along the way, I started thinking more seriously about the right way to put together web applications and did more research into how Rails and Play 2.x are structured. Although Play 1.x was amazing, I think that there are some obvious areas for improvement -- the design of 2.x seems to be much better (although I'm personally allergic to Scala).
So the goal posts have shifted. I see this ending up as two things, conceptually:
1. A framework like Rack, in the sense of being a straightforward interface for writing and composing aspects of web applications. (Revel calls them Filters). Some components (Router, TemplateLoader) have defined interfaces so that other components can use them (but most components do not).
2. A default stack of components that work well together out of the box. No size fits all, but the two concrete use cases I have in mind are writing large business applications and REST APIs.
The goal is that the revel package shrinks to encompass just the "Rack"-like framework, and the revel command line tool can generate a project with the default components set up.
This appears to be achievable; it just requires a bit of work, and I have been quite busy with the day job. More people are using it than I anticipated, so I would like to complete the core design so that it can be properly released.
=====
APPENDIX (EDIT)
FAQ: "Why not just use vanilla Go, since it already provides X, Y, and Z":
Go certainly ships with more components in place than any language I'm aware of, but I still think there is a lot of value in providing a default stack of stuff with a fixed, conventional app organization in terms of lowering the cognitive load bar.
If you are making a single endpoint and are a Golang whiz, then Revel is not for you. But for many things it's about the concepts more than the code. For example, the code dealing with the flash cookie is not many lines; most of the value there is having it integrated, documented, and exampled. Empirically, I believe that Rails unlocked a latent desire for web development by lower the bar -- I think Revel can lower the bar for web development with Go in the same way.
Lastly, there is something to be said for having an integrated web framework instead of a franken-app composed of various libraries. Take parsing parameters as an example. With a library, how can you get close to the ease that Revel provides? [1]
You may think it's dumb, but I find it so much nicer to simply accept parameters as the type that I want them in, and let Revel figure it out instead of me having to figure out the right strconv / etc function to call. The goal is for Revel to take care of all that stuff and let me focus on the higher level stuff.
I love Revel. I won't even hide it. I completely agree with the statement that while vanilla Go does provide a lot, its worth the cost, if you're able to organize code into understandable, differentiable chunks. I understand the whole plug-n-play philosophy of node, but when it comes to finding and understanding documentation, it is so much easier to just look at the framework's docs as opposed to looking on each library's website for the docs (most of which are usually outdated).
I am new to go with a Python and C# background. Using Revel on my latest project. Not completely convinced it provides that much over roll-your-own or composition with Gorilla's libraries. I much prefer Gorilla's mux to Revel's routes. Looking at the source code has been highly beneficial to learning Go, so Thanks all the same.
Just a feedback... though we like Revel, it could have been a clone of Rails or Scooter (Java) or CakePHP -- with strict coding standards than Play 1/2 that looks more of a kludge especially when you come from Rails.
The first version of anything worthwhile is typically an experiment. I'm excited about the prospect of a great way to write Go apps in the future that is better than what we have now. One of the best ways to kill a project is by trying to be something else.
One of the places Go excels is at sharing memory between goroutines. Most web apps are built so that each request is independent of all other requests. Which isn't to say Go's a bad fit for all web apps, I just don't think most will gain anything from it.
We've used it for just another web app, and haven't gotten any value of out it...except learning Go. Then we deployed it as a piece of infrastructure, something that would have been hard to write in Node or Ruby, and it's been a real win.
Don't get me wrong, I'm always excited about Go tech but Revel has been around for a while and I fail to see why today, specifically, it is on HN's main page.
Could someone explain what problem this solves? Go already has http, templates, and (duh) goroutines. What is Revel adding?
Sure, PHP programmers are accustomed to having their syntax errors detected by surprise at runtime. But aren't we choosing Go because it can detect those errors before we've deployed? [1]
In Java it may feel natural to take a simple task like dispatching a URL ("Routing"), and turn it into an elaborate overabstracted object architecture. But aren't we choosing Go to escape that nonsense? [2]
EDIT: I've seen lots of Go applications, and none of them have spent 8432579842375 hours developing scaffolding. Actually, they saved time not having to spray their logic across a zillion source files.
"Could someone explain what problem Play/Wicket solves? Java and servlets already has http, StringBuilder, and trivially created threads. What is Play/Wicket adding?"
What it's "adding" is the 8432579842375 hours you don't have to spend writing your own routing, template-loading-and-management, and logic separation code.
EDIT: Your #2 is a strawman. "revel run" mode statically compiles all files and dependencies if the timestamp changes. It's not even remotely comparable to PHP. Just because it has a friendly error message doesn't mean revel magically dispenses with static type safety.
The point of all frameworks provide a set of idioms about how one should best solve a problem. It's not to make web apps possible, it's to establish an opinion about how webapps ought to be written in Go. Which is different than how one ought to do it in Java, and Scala, and Ruby.
It's nice to be able to leverage other people's opinions and established idioms without having to figure them out yourself.
Preface: I absolutely love Revel, and I hope to put something built on it into production very soon.
That said, I'd be hesitant to bring up Revel's session as a positive. It shoves everything into a cookie instead of using the cookie token as a key to a serverside data store. Not only are you limited to 4k of session-local storage, but you're sending the entire payload across the wire for every single request. This is absolutely stupid and needs to change. I'm sure Rob is boxed in by limited time, and this is not intended for production use yet, but holy god, it's by far the worst thing about the framework.
This is the same thing Rails does by default. I much prefer it to server side stores. What on earth are you keeping in the session that actually causes it to be a problem? If it's more than an auth token and a bit of flash scoped data, it may be time to rethink how your managing state.
Well, how hard is it to just use their session store to store a guid of some type and use that key to store session data in redis or some other storage?
That's what I do, and it's easy enough, but that's not the point. Revel is trying to be a comprehensive "ready to go" framework - and as such, it has all these other features fairly well covered, but it's falling down, relatively speaking, in this one.
I like go's anti-magic philosophy, if not simply because of how utterly hard I've being burned by magic.
routing is best left to you to write explicitly, there's no pancea, because if you use this solution and you then need to do something even slightly more complicated, then you now have two nomenclatures, and two interacting systems.
A simple library is what is needed for parameter parsing and validation, not a framework that provides it wholesale.
There are lots of session implementations, a library might intice you with its offerins, not a frameworks im pretty sure this one size fits all solution should work in some manner
Templating was already there, they offer it again? There's also competing libraries to go's templating, use those.
Caching? You mean for templates? That's not complicated to do. For databases? There are databases that need you to cache results for them? Sheash.
a testing framework? Doesn't go have one of those? Why do we need two? Go's got a good testing framework!
Internationalization is templates with file reading, right?
I know these all sound hard, they sound like you can't do them on your own, or they sound tedious. But for the love of god, down with frameworks, down with magic! Burn it all!
I think this guy just wrote his "web dev in Go" utility libraries and then decided to bundle them together under a fancy name with a funky web site.
Go has all the built-ins you need to get a simple web server started in no time, but as you progress to real-world work, you do notice your own plumbing and boilerplate code piling up. In your second project, you extract those into a number of packages. Then if you're feeling fancy or idle, "bam, look Mom, a Web Framework!"
> For databases? There are databases that need you to cache results for them? Sheash
Yeah ever heard of Memcached? Check it out. Still something you need to bind & talk to, and perhaps you want to have a little toggle for your dev environment to have RAM caching instead of full-blown Memcached. Voila, a candidate for a utility package. Sure, this is mostly trivial to write yourself, which is why a gazillion of them exist, but hey, why not have it included in a single go-get-able package that has a lot other "needed" web-ish stuff in it too.
Personally, I'm not touching frameworks like this either. I swear by the built-ins (even for templating) and the "Gorilla" libs. But why the hate, this clearly worked for the coder and users of this framework. Maybe there comes a time and project where it will work for me.
ALL frameworks are built using stuff the language provides. For me it's about how much time I want to spend setting up my project's "base" versus using a framework that already has the stuff that I want set up already.
if you anonymously embed a structure, the containing structure gets its methods. If the contained structure satisfies an interface, then the containing structure does as well.
Attempting to create inheritance features is ignoring the nature of Go's type system; it's the opposite of idiomatic.
Are you suggesting that the design would be better if instead Controller was an interface?
I agree that it seems enticing, for exactly the reason you mention. However, I think the interface solution would require setters and getters for every single field, which seems much worse to me.
>I think the interface solution would require setters and getters for every single field
not if you use struct tags. e.g.,
type MyController struct {
Id int `revel.q="id,required=true,min=0,max=200"`
}
or something like that. Then the framework just fills the controllers field on an incoming request, based on what it finds in the struct tags. Then you have an interface that allows you to override that behavior, similar to how the json package will use the methods defined in `json.Unmarshaler` if you define them, but will fall back to controlling things by itself with reflection if you don't.
Incidentally, Revel achieves an exceptional framework overhead rating in our tests. In our i7 Fortunes test [1], Revel performs at 96.5% of its platform, Go. (See rightmost tab on the results panel.)
[1] http://www.techempower.com/benchmarks/#section=data-r6&hw=i7...