Tuesday, June 08, 2010

Seamless Web Development

Do you remember the time before 9/11 when you could arrive to the airport 10 minutes before the plane's departure and just walk on to the plane. Now, you have to arrive at least an hour before the plane departs and you have to strip to get through a security.

The security is just for show. I don't know how many times I have forgotten a bottle in my carry-on and I haven't noticed it until I unpacked the bag at home. There a number of ways to get a big bang for your bucks, that aren't checked at security

XKCD Bag Check

So what does this have to do with web development? Web development in a compiled language is like traveling after the terrorist attacks. You have to go through a lot of time consuming controls that are, mostly, for show. This dramatically slows down your development speed.

Since I did my comparison between Rails and .NET MVC I have done a lot of coding in both environments, and my conclusion is that Rails is a lot faster to develop with than .NET MVC.

The reason for this is that .NET MVC puts up a lot of seams that hinders development.

Seam #1, Runtime Development

When I'm using Rails for development I very rarely restart the server. When I make a change to the view, controller, model or database, the changes are visible on the next request. Immediate feedback!

In Visual Studio, there is something called "Edit and Continue", that you can set to allow you to make changes to the code while you are debugging. Mooah ha ha ha ha ha ha ha haaaaaaa! This sucks so bad that I cannot help bursting out in a crazy laughter. The flow disappears.

Edit and Continue

If I make changes to the view I can continue, unless I make changes to the I18N resources, because these are obviously not mutable. WTF!

Sometimes it is actually possible to make changes to the code, but when you try to save the changes "Edit and Continue"'s big brother appears and smacks you in the head, and the flow is gone again!

Edit and Continue2

There is really no good reason for this. I'm sure there are a number of technical reasons having to do with type safety and class reloading, but I don't care. Just reload everything, do it fast and unnoticeable!

Seam #2, The Javascript Impedance Mismatch

Hopefully everyone has realized that Javascript is the hottest thing in web development! A language that has been spat on, and laughed at, has become the most wide spread programming language in the world. It's available in every browser and on every programming platform.

I have been talking about the greatness of Javascript for a few years now and it is great to see when someone finally get it.

"Its great, I just change this little thing, then click reload, and it works immediately!" -- A collegue, seeing the coolness of Javascript.

Javascript is Lisp in C-clothing and Lisp is the language that God used to create the world.


OK, Javascript is cool, and we all use it, but what is this impedance mumbo jumbo? Since a large part of a modern web application will be developed in Javascript, when we move over to the server side and everything is static and compiled, we are in for a lot of frustration.

Common Development Flow

  • Make a change to the GUI, in Javascript.
  • Reload
  • Make another change
  • Reload
  • Realizing that you need to change the controller for the next change.
  • Aaaaaaaaaarrrrrrrrrrrggggghhhhhhhh, I have to restart the application.
  • What was it that I was working on again? I'll go for a cup of coffee!

This is the Javascript impedance mismatch. If you are developing in a dynamic language you expect it to work like this all the time. You get used to it. It is better!

With Rails and Ruby, you get the same flow on the backend as you do on the client. The static-dynamic mismatch is gone. (There is still a language mismatch, between Javascript and Ruby, but it is minuscule by comparison.)

Seam #3, Cruft

Compare the following two partials

<!-- BookedCustomer.aspx partial in .NET MVC-->
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Bergqvists.Models.Booking>" %>

<div class="view">
    <div class="customer title"><%= Html.ActionLink(Model.Customer.Fullname, "Details", "Customers", new { Id = Model.CustomerID}, null) %></div >
    <div class="status"><%= Html.Encode(Model.BookingStatus.Name) %></div >
    <div class="booking-date"><%=ViewRes.BookingStrings.BookedOn %>
    <%= string.Format("{0:D}", Model.BookingDate) %></div>

<% Html.RenderPartial("BookingStatusLinks", Model);%>

<!-- _booked_customer.html.erb partial -->
<div class="view">
    <div class="customer title"><%= link_to customer.fullname, customer_path(customer)</div >
    <div class="status"><%= customer.booking_status.name %></div >
    <div class="booking-date"><%=t :booked_on %>
    <%= customer.booking_date, :format => :short %> %></div>

<%= render :booking_status_links, :locals => {:customer => customer} %>

As you can see the corresponding ERB is not much different, but at least everything in the code is meaningful. And what customer value does the first line bring?

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Bergqvists.Models.Booking>" %>

Nada! Zilch! It is only there to please the compiler. Cruft!

Now, most Ruby programmers I know use HAML instead of ERB and this cleans the view up considerably.

  .customer.title= link_to customer.fullname, customer_path(customer)
  .status= customer.booking_status.name
    = t :booked_on
    = customer.booking_date, :format => :short

= render :booking_status_links, :locals => {:customer => customer}


Comparing Haml with ASP is not really fair, since it is possible to use NHaml instead of ASP in .NET MVC, but no one I know does that, maybe I hang with the wrong .Net people. NHAML is great, please switch it to the default for .NET MVC.

The example above is in the view layer. But the cruft spreads all over the stack.

Seam #4, Testing

Testing is always an interesting topic. The number one reason testing is not done is that it is to cumbersome. In .NET MVC I find it painful to test the controller and view layers, to the extent that I don't do it. With Rails on the other hand, you are setup for test from the start.

I always try to put as much code as possible into the model layer, but some code belongs in the controller. I want it to be easily tested too.

It is also worth noting that the Ruby community is driving the testing profession forward, with tools like RSpec, Capybara, Webkit and Cucumber.

Yes, I know that there are several implementation languages for Cucumber. It's possible to write your steps, in C# or Java or almost any other language. My advice is, don't do it! There is compilation, waiting, pain and suffering down that road. Use a dynamic language to test your code.


So what's my point? It's time to stop compiling and quit slacking off.

XKCD Compiling

Rails should be the default choice for web development. You should have a very good reason for not choosing Rails on your next web project.

I know it may feel tough to have invested many years in something else, but as the turks say.

No matter how far down the wrong path you've gone, turn back! --- Turkish Proverb

Don't take my word for it. Check out this interview and this presentation


VAI said...

Yes, dynamic recompilation is great. This is why i'm usually running asp.net site without debugging: I still can edit my .cs files (because there is no debugger active) and fast ctrl+shift+b after that will recompile my site.

And there is my question: did Ruby have some debugger? Or there is equivalent to you to run site wuthout debugging?

Anders Janmyr said...

@VAI Are you saying that I can get dynamic class reloading if I don't run in debug mode? I have to try that.

Yes, Ruby has a debugger, check out ruby-debug http://bashdb.sourceforge.net/ruby-debug.html

Also check out this screencast


VAI said...

Anders Janmyr, thanks for links, I will check.

No, I've not described dynamic recompilation, there is full recompilation, but without re-running of your browser :)

Dynamic recompilation described here: http://msdn.microsoft.com/en-us/library/ms366723.aspx (look for optimizeCompilations word on page).