Sweet, sweet vindication
It’s been awhile since I have written an eye-poker post. I’ve been trying be more reserved, professional, and politically correct. But that basically had a cooling effect on my writing. So it’s time we turn up the heat and let the rage flow.
The State of Web Frameworks Sucks
The state of big web frameworks sucks. Not the least sucky among these is Rails. There, I said it. “Bah, he’s just a bitter .NET developer stuck in the stone ages”. I’m tired of getting made fun of and beat up by former PHP scripters telling me how lame and “enterprisey” .NET is and how wonderful and magical Rails is and how it solves every problem. Tell it all to this guy:
ActiveRecord (and Rails) Considered Harmful
Especially with this wonderful quote:
It is practically impossible to teach OO design to students that have had a prior exposure to Rails: as potential programmers they are mentally mutilated beyond hope of regeneration.
- Edsger W. Dijkstra (paraphrased)
My thoughts exactly, on all counts. The whole “Controller” mindset in most MVC frameworks is “pants-on-head retarded”. Worse yet, most frameworks use inheritance. I can’t say this loud enough, so I’m going to BOLDFACE it, ALLCAPS, BLOCKQUOTE it so maybe you’ll see it:
INHERITANCE IS FAIL FOR MOST DESIGNS, BUT NEVER MORE SO THAN IN WEB/MVC FRAMEWORKS!
Rails is one of the biggest violators of this. I don’t care about your stupid dynamic typing and all your excuses about how it “just doesn’t matter” — it matters. The laws of good design don’t just fly out the window when you’re using Ruby or any other language.
More Pants On Head: ActiveRecord
To call ActiveRecord an ORM is to insult the already sullied and notorious name of ORM. I won’t even say that AR is the Linq2SQL of Rails dev because it’s worse than that. AR is the equivalent of dragging and dropping your database to your design surface in Northwind/Designer-driven developing in Visual Studio.
I don’t care what you’re doing with your data store, the model should be the core. This principle is so fundamental that not even silly dynamic typing can overcome it. The model flows out into everything, not your database driving your model. AR represents everything that’s wrong with data-driven development as much as designer-driven EF does in .NET. It’s just wrong, always has been wrong, and always will be wrong. The model is the key, not the persistence mechanisms. AR flagrantly and flamboyantly violates this principle and anyone with a clue who has used AR seriously on any project of length will tell you how it grows into a big mess.
And then you have the whole “Unit test against your database” phenomenon. I’ve had Rails guys chide me for this unmentionable and detestable practice and say that I’m too stuck in my .NET ways, but I’m sorry. You’re an idiot. Unit testing against the database is always, and in every case, wrong. Attempts to explain otherwise sound awfully like arguments I once heard from someone explaining why cannibalism isn’t as bad as people make it out to be. It’s just wrong (unit testing against your database and eating people). Just because you wear thick rimmed glasses and drink lattes with your scarf flipped casually around your neck doesn’t mean you get to throw fundamental principles out of the window. So stop it. Just because it may be quick or easy (which I don’t believe) doesn’t make it right.
Just when I thought I couldn’t say anything worse about AR, I’ve got one more — the coup de grace –it uses inheritance! ARGH! Add “maliciously” to the adverb list (“flagrantly, flamboyantly”).
ActionController – Crack Den of Inheritance
It seems almost every web framework has this wrong: Rails, ASP.NET MVC, Struts, Django, Spring, and MonoRail from my limited research.
Let me put it another way in case I haven’t been clear enough: Inheritance is antithetical to web app design.
One class — that derives from some gawdawful ginormous base class — that has many “action” methods on it is a pile of fail. Why? Because each of those methods has, by definition, a different responsibility and you’re breaking the Single Responsibility Principle. Principles matter! No matter what language (dynamic, static, etc), principles matter! Just because you’re a Rails hipster doesn’t mean you don’t have to obey the laws of physics. With sufficient thrust, pigs can fly for a short while. Likewise, with sufficient ignoring of principles, a rails dev can be successful for awhile but the legacy maintenance cluestick will eventually come crashing down on him/her.
The Perfect Web Framework
Rails is the whipping boy of this post, but most of the other frameworks deserve all the same vitriol because they have all the same fatal flaws (primarily inheritance). So what would the perfect web framework look like? Let me show you:
- Total adherence to principles: The framework should itself abide by the fundamental principles of good software design and it should require you to do so as well. It should make it as difficult as possible to flout them and, preferably, punish you for doing so by making everything harder.
- Completely compositional: It should itself be based entirely off of composition and either not use inheritance at all, or only in very minor circumstances and certainly should not require you to derive from any framework base classes if you don’t want to. Convenience base classes are fine, but should not be imposed upon the framework consumer. The framework should encourage and reward compositional design and frustrate and punish inheritance mis-design.
- Model based: It should encourage and reward you for having a centrally defined model (or models) and be based around one itself. The framework should itself use one or more models to represent its own internal structures (configuration, routing, etc) and expose these for manipulation by the consumer. It should facilitate you using your own models in your actions and views.
- Not have any notion of controllers: The framework itself has one controller that processes the routes and this satisfies the “C” in MVC. The words “controller” should not appear anywhere else in the framework or outside. It should completely break you of the notion that you need a class called “controller” because this is just wrong and leads you to do all sorts of bad, nasty, and dumb things.
- Statically typed: In line with the “Model based” requirement, it should facilitate inspection, reflection, and convention-based development where everything (and I mean everything) flows OUT from the model (not into the model, like ActiveRecord foolishly does). All your validation rules, authorization rules, binding rules, display rules, input rules, etc. should all flow from decorations and conventions attached to your model. Which leads to the next requirement….
- Conventional top-to-bottom: The framework itself should expose all of its own conventions and allow them to be replaced. It should encourage and reward conventional development and frustrate and punish you for doing one-off stuff except as a last resort. And I don’t mean Rails’ conventions where it’s my-(lame)-way-or-the-highway, I mean real conventions – YOUR conventions where you can define them and reuse them. This is only possible with static typing because you can’t get the level of introspection and reflection necessary to pull this off with dynamic typing and string-only reflection.
- Based on IoC: No more IoC as an afterthought. I’m not going to have this debate. If you disagree with this you are wrong. If you disagree then you simply haven’t seen the power of IoC done right in a framework and in an app. Try playing with FubuMVC for a few weeks and look at some of our samples or come hang out at Dovetail for a day or two and I guarantee you will no longer disagree with me. The power this provides is so overwhelming and fundamental that is perhaps the requirement of requirements.
- Mostly model-based conventional routing: Your routes should flow out, conventionally, from the model as much as possible allowing proper REST scenarios. This is extremely important in a CRUD app. Specific actions (for service actions like “send email” that aren’t based on your main domain model) should be easily created, defined, and conventionally routed.
- Action-based: In line with the whole “no controllers” and “completely compositional” requirements, it should encourage each action to be tiny (a few lines at most). All things like model binding, validation, object/persistence retrieval, should be moved into composable “behaviors” that are outside of the action since they are not the concern of the action. Each action should be a single class with a single public method that does that one thing. No more multiple-50-plus-line methods in your dripping-with-inheritance “Controller” class. NO!
- Minimized code-in-view: This one fits with the “conventional” and “compositional” requirements. The framework should provide ways of micro-HTML generation that flows from the model and through conventions. I’m not talking about lame loosely-typed “HTML helpers”, I mean real meaty, conventional, model-based micro-HTML generators. The framework should also make using partials a natural, normal, and preferred way of compositing screens. It should support the ability to render simple HTML no-code partials, or to execute a partial action through the framework to render a chunk of HTML, JSON, etc.
- One Model In, One Model Out: Every action method should take in one model (not primitives) in and return one model out (no ActionResult nonsense or framework-dependent inheritance silly classes). The action should not tell the framework which view to render or where to go next unless the action’s purpose is specifically for that (such as a post-successful-login routing action). The framework shall conventionally determine how to bind the models going in and render views from the outgoing model
- Zero touch: My actions should have ZERO knowledge of the web framework. I shouldn’t have to “import” or “using” or “require” any of your framework stuff. My actions should be simple POO’s like this (note no “using FubuMVC”)
- Independent of other frameworks: I shouldn’t have to use your stupid “ORM” or your favorite JavaScript framework which is 2 weeks old and already outdated. This is made easier, if not entirely possible, by the “IoC” requirement, by the way.
- Testing as a first class citizen: I debated whether or not I should even put this in here because, for me, this is like putting “Step 1: Breathe” at the top of any list of instructions. Generally, if you care about any of the other requirements, this one is essentially a “Step 0” requirement kinda like “Use a computer” or “Run on x86 processors”.
It should come as no surprise that we, at Dovetail, came to these conclusions (many of them solely by Jeremy Miller) after evaluating all the other major frameworks, suffering much ridicule and derision for noting the inherent and fundamental flaws in them and then building our own that follows all the principles: FubuMVC.
In many ways, FubuMVC is better than Rails and I’ll put my framework up against yours any day of the week. In some ways, it has a long distance to go yet. These are, namely, approach-ability for newbies and community. Currently you have to be disgusted with your current web framework and have tried to replace large portions of it before you can truly appreciate FubuMVC. As far as community, it’s still pretty small at this point, and community contributions are growing, but still not anywhere near Rails level, for sure.