EDIT: Someone on Twitter got upset about my “or an inferior IoC tool” comment. Most of you probably know that I’m also the author of StructureMap, and I did mean that as a joke. In a later post about IoC integration I’ll happily admit that making FubuMVC too StructureMap-centric was a problem with adoption (we did support Autofac and had Windsor support all but done as well). To be blunt, I think that the IoC tools in .Net are largely an interchangeable commodity item and any of the mainstream ones will get the job done just fine.
I’m still up for doing this series of technical lessons learned about FubuMVC to wring some value out of the whole process, but I feel like I’ve said enough mea culpas about our failure on documentation. From now on, instead of dog piling on me in the comments, could you just say “Docs sucked++” and then get on with whatever else it is you want to say?
Today’s blogpost is partially about the danger of deviating from .Net orthodoxy, but mostly a lamentation on my part on missed opportunities and unrealized potential. I’m actually quite proud and happy with most of what I’m describing in this post, but it was too late to matter much and might not have ever been widely accepted.
Quickstart from the Command Line
I’ve always admired elements of Ruby on Rails, especially the almost magical “rails new” project skeleton creation that sets you up with an entire code tree with a standard build script that exposes common build tasks for testing, database migrations, and even deployment recipes — but the word “standard” in that last sentence is a very key concept. Much of the value of “rails new” is enabled by standardizing the layout and naming conventions of a Rails codebase to make it cheaper to write reusable command line tooling.
We knew from the very beginning that we’d eventually want our very own “fubu new” analogue. Our community built a simple one initially that would just clone a pre-canned Git repository and do some simple string replacement for your project name. Great, and it added value immediately. However, the FubuMVC ecosystem got bigger as we built:
- Ripple as a better command line manager for Nuget packages in continuous integration
- Bottles as our modularization strategy, including a command line bottles tool to package up web content files as a pre-compile step in your build script
- FubuDocs as a command line tool to author and publish technical documentation
- fubu run as a Katana based development server that’s more efficient in development than anything IIS or VS based
- FubuTransportation as our FubuMVC based message bus
- A slew of command line testing tools
To unlock the usefulness of all those tools above to new users, not to mention just getting a simple application codebase up and running fast, we embarked on a new effort to create a vastly improved “fubu new” story that would allow you to mix and match options, different project types, and integrate many of those tools I listed above.
At the time of this post, you can stand up a brand new FubuMVC application using the Spark view engine from scratch to do grown up development by following these steps at the command line assuming that you already have Ruby 1.9.3+ installed:
- gem install fubu
- fubu new MyApp –options spark
If you do this, you’ll see a flurry of activity as it:
- Builds a new VS.Net solution file
- Csproj files for the main application and a matching test library
- Creates the necessary classes to bootstrap and run a minimal FubuMVC application
- Invokes gems to install Rake, FubuRake, Ripple, FubuDocs, and the bottles command line tool
- Invokes Ripple’s equivalent to Nuget Package Restore to bring down all the necessary Nugets
- Opens the new Visual Studio solution
The result is a code tree that’s completely ready to do grown up development with a build script containing these tasks:
rake ci # Target used for the CI server
rake clean # Prepares the working directory for a new build
rake compile # Compiles the solution src/MyApp.sln
rake compile:debug # Compiles the solution in Debug mode
rake compile:release # Compiles the solution in Release mode
rake default # **Default**, compiles and runs tests
rake docs:bottle # 'Bottles' up a single project in the solution with...
rake docs:run # Runs a documentation project hosted in FubuWorld
rake docs:run_chrome # Runs the documentation projects in this solution i...
rake docs:run_firefox # Runs the documentation projects in this solution i...
rake docs:snippets # Gathers up code snippets from the solution into th...
rake myapp:alias # Add the alias for src/MyApp
rake myapp:chrome # run the application with Katana hosting and 'watch...
rake myapp:firefox # run the application with Katana hosting and 'watch...
rake myapp:restart # touch the web.config file to force ASP.Net hosting...
rake myapp:run # run the application with Katana hosting
rake ripple:history # creates a history file for nuget dependencies
rake ripple:package # packages the nuget files from the nuspec files in ...
rake ripple:restore # Restores nuget package files and updates all float...
rake ripple:update # Updates nuget package files to the latest
rake sln # Open solution src/MyApp.sln
rake unit_test # Runs unit tests for MyApp.Testing
rake version # Update the version information for the build
This entire rake script is (I’ve added some explanatory comments for this blog post):
require 'fuburake'
@solution = FubuRake::Solution.new do |sln|
# This is unnecessary if there's only one sln file in the code
sln.compile = {
:solutionfile => 'src/MyApp.sln'
}
# This feeds the CommonAssemblyInfo.cs file we use
# to embed version information into compiled assemblies
# on the build server
sln.assembly_info = {
:product_name => "MyApp",
:copyright => 'Copyright 2013. All rights reserved.'
}
# These are defaults now, but I still left it in the
# template
sln.ripple_enabled = true
sln.fubudocs_enabled = true
end
# This one line of code below creates rake tasks as a convenience
# to run our development server in various modes
FubuRake::MvcApp.new({:directory => 'src/MyApp', :name => 'myapp'})
The terseness of the rake script above relies very heavily on a standardized code tree layout and naming conventions. As long as you could accept the FubuMVC idiomatic code tree layout, I feel like we succeeded in making our ragbag collection of command line tools easy to setup and use. Great, awesome, it’s done wonders for the Rails community and I’ve now seen and used working analogues with Scala’s Play framework and Mimosa.js’s “mimosa new” command. A couple problems though:
- There’s never been a successful effort in .Net to identify and codify idioms for laying out a code repository and we’ve found that many folks are loathe to change their own preferences (“src” versus “source”, “lib” vs “bin”, and so on). It might be easier if TreeSurgeon had succeeded way back when.
- I think it was just too little, too late. I think that OSS projects, once announced, have a short window of opportunity to be awesome or else. I felt good about the improved “fubu new” experience just in time for NDC London this past December — but that was almost 4 years after FubuMVC started.
- VS.Net templating was a lot of work and dealing with different project types, VS versions, and versions of .Net added overhead
- Nuget and our own Ripple tool are still problematic
- While common in other development communities, this is not a common approach for .Net developers
Why not Visual Studio Templates or Project Template Nugets?
We did support Visual Studio project templates, but I always felt that would only be for folks that just want to play with the framework a little bit. The feedback we got from other OSS projects that did invest heavily in VS templates was uniformly negative and I wasn’t enthusiastic about doing much with them. It was a tremendous amount of work to build our templating engine (FubuCsProjFile), but at least what we got was something easy to author and possible to cover with automated testing (a never-ending shortcoming of so many tools originating from Redmond).
We supported a project template with Nuget very early on and even finagled out a “install nuget, press F5” experience, but I always found it to be very limited and problematic. Again, testability was an issue. If I had it to all do over again, I’d still choose our command line approach with the mix and match selection of options (Spark or Razor? StructureMap or an inferior IoC tool? Want RavenDb support? Bootstrap? Html Conventions?).
The Polyglot Thing is a Double Edged Sword
There’s always some sort of meme running around in developer circles that’s meant to make the cool edgy kids feel superior to the unwashed masses. A couple years ago there was a lot of hype about polyglot programming where you would happily mix and match different programming languages and paradigms in a single solution based on their relative strengths.
For better or worse, the FubuMVC projects were a mild example of polyglot programming and that repeatedly scared people off. We used Ruby’s Rake tool for our project automation, we partially replaced Nuget with gems for distributing binaries that we used in the build scripts. For 2.0, we’re happily ditching our original asset pipeline in favor of the Node.js based Mimosa.js tool. At other times we also used the Python-based Sphinx for some early documentation efforts.
While I think that Rake is an outstanding tool and very easy to use, the simple need for a Ruby tool drove many people away — which is a shame because many other .Net OSS tools besides FubuMVC prefer Rake-based build scripts over the other alternatives.
It’s not hard to understand people being hesitant about having to learn non-mainstream .Net tools, it’s unfortunate. One of the big problems with trying to do ambitious OSS work on .Net is that .Net simply isn’t the home of the best tools. We repeatedly found Nuget to be lacking (a blog post for a later day) and MSBuild is, in my opinion, a complete non-starter for build automation.
As an aside, I’ve frequently seen members of the ASP.Net team complain on Twitter about having to install Ruby just to build some OSS project when they turn right around and require you to install all kinds of Visual Studio.Net add ons and templates just to build their code.
Aborted Plans for FubuMVC 2.0:
For 2.0 I wanted us to push forward with more templating options and introduce a new standalone web application that you could run from some sort of “fubu new –interactive” mode to visually discover and select the options you wanted to use in your own solution. I also wanted to stop bundling the template library into the fubu.exe tool itself in favor of using a git repository that could be easily auto-updated by fubu.exe as we extended or improved our templates.
I thought we had a strong concept for the bootstrapping, but after getting one hard look at Typesafe Activator in Scala, it’s pretty obvious that we would never have been able to match that level of polish.
I also wanted to upgrade our standard testing tools from whatever old version of NUnit we’ve been using for years to something based on the excellent Fixie tool.
Conclusion
In a lot of ways, I think the .Net ecosystem — even though it’s over a decade old — is immature compared to other development platforms. I feel like we had to do way too much bespoke infrastructure (Bottles for packaging web content, Ripple for a more usable Nuget story, FubuCsProjFile for csproj file manipulation) to pull off the “fubu new” story. I wonder a bit if what we did might be easier in a couple years when Nuget matures and the new OneGet package manager gains traction.
I feel like Visual Studio.Net was a significant hurdle in everything we tried to do with our bootstrapping story. I think .Net would be able to innovate much faster if our community would be much more accepting of lighter weight command line tools instead of demanding much more time intensive Visual Studio integration.
My colleagues at work and I are likely moving to the Play/Akka stack on Scala and a very common refrain around the office this week is excitement over being able to use lighter weight tools like Sublime and SBT instead of being forced to work with a heavyweight IDE like VS.