Sunday, February 24, 2013

Server-side view or client-side MV*?

Recently I came across a video in which David Heinemeier Hansson(a.k.a DHH, creator of Rails) talked about client-side MVC vs the traditional Rails way of developing web applications. In the video DHH claimed that the development experience of client-side MVC is inferior compared to traditional Rails development. He didn't give a lot more specific reasoning except:
  1. you can't write ruby at client-side.
  2. client-side MVC is "extra complexity" that is not worth the effort most of the time.
Personally I like CoffeeScript better, especially on the functional programming side, but I am not going to argue about it in this article. This article is about why the second reason he gave is wrong.
I'll first show some code comparison between the two paradigms to demonstrate the software design drive for client-side MV* and then I will talk about the business drive for client-side MVC(*) - interactivity.

Is it more complexity or more elegance?

DHH claimed that client-side MVC is more complex compared to server-side views, but is it? Or is it just more elegant? Let's look at some code. How about we write a simple todo list in both client-side MVC and traditional Rails way.
Rails way
erb view:
<ul>
  <% @todos.each do |todo| %>
    <li> <%= todo.title %> </li>
  <% end %>
</ul>
controller:
class TodosController < ApplicationController
  def index 
    @todos = Todo.all
  end
end  

Client-side MVC
service controller:
class TodosController < ApplicationController
  def index 
    render json: Todo.all
  end
end  
HTML view:
<ul>
  <li> {{title}} </li>
</ul>
client-side MVC coffeescript:
$ ->
  todos = new Collection(url: '/todos')
  new CollectionView(collection: todos, el: $('ul')) 
  todos.fetch()
  #The Collection and CollectionView is not standard Backbone class. We extended it a little bit in our internal Backbone library. 

The client-side MVC version does need some client-side MVC code, but whether it's resentful complexity or elegance with clearer view logic is arguable. Now let's add a dropdown filter so that user can filter the list by todos' status: 'completed' and 'overdue'.
Rails way
erb view:
<%= form_tag("/todos", :method => "get") do %>
  <%= select_field_tag('filter', 
     options_for_select(['completed','overdue'], @filter)) %>
<% end %>
You also need a little bit help from the client-side so that user doesn't need to click a submit button to submit the form.
$ -> $('select').on 'change', $('form').submit
Controller:
class TodosController < ApplicationController
  def index 
    @filter = params[:filter]
    @todos = @filter.present? ? Todo.where(status: @filter) :
                                Todo.all
  end
end  
Client-side MVC
HTML view
<select name='filter'>
  <option>completed</option>
  <option>overdue</option>
</select>
A new view is needed at the client-side MVC:
class FilterView extends Backbone.View
  events: 'change' : 'refresh'
  refresh: => @collection.fetch data: { filter: @$el.val()}
Add this view to the page load initialization
$ ->
  ...other initializations mentioned above...  
  new FilterView(collection: todos, el: $('select')) 
Controller:
class TodosController < ApplicationController
  def index 
    @todos = params[:filter] ? Todo.where(status: params[:filter]) :
                               Todo.all
  end
end  
This time the effort required in the client-side MVC solution is arguably at the same level. You need to write a new view class and initialize it but in exchange your HTML view is much cleaner than the erb template. The extra CoffeeScript in the Rails solution perfectly exposed its inelegance - why do you need a HTML form? Also, you might notice that the server-side controller is slightly cleaner when it doesn't have to remember the filter state at the client-side (I know you can access params directly from the view but that's frowned upon).

From a software design/architecture point of view, where to place the view logic is not a very tricky question. We don't need philosophical debate to sort it out. View logic needs both to access server-side data and to interact with client-side HTML DOM. Clearly it's more convenient/efficient/logical to place view logic close to where it has the most interaction. In the old days, the only user interaction is simple back and forth HTML form request and HTML page response, the view logic relied mostly on the server-side data. Nowadays web app UI has become a lot more dynamic and complex, there is way more dependency towards client-side DOM and states then server-side data. That's why placing view logic at the client-side is more efficient at this age.

It's the interactivity, stupid.

DHH admitted that certain level of user interactivity justifies such 'extra' effort for client MV*. He was close but did not get to the point. Interactivity is the essential for better UX. When your page is simple, yes, you don't need that much interactivity. But the market will demand more and more functionality on that page. The only way to keep the UX clean and intuitive is through creative interactivity.
DHH told the story that he chose Backbone to do a calendar because it's a highly interactive component and he used traditional server-side view for other "information" page. This hybrid approach is not that bad an idea when you can foresee how interactive each part of your app is. What DHH failed to see is that it is not the case for a website reasonably ambitious to be modern.

A good example is the todo list mentioned In DHH's story about his development of Basecame Next. He couldn't decide which way to go, client-side mvc or the Rails way. He couldn't tell if the amount of the interactivity in a todo list demands client-side MVC. So he wrote both a rails+ad-hoc javascript version and a Backbone version. He ended up with the rails+ad-hoc javascript version because it's less code and more 'elegant' and can also serve the interactivity needed for that todo list. But what if the market one day says we want more functionality in that todo list? The ad-hoc javascript and view logic in rails would become more and more messy until it would have to be rewritten in client-side MVC.

Actually, all the "information" pages he mentioned could be designed differently with more interactivity if the developer was not in the traditional Rails html form and view mindset. For example, in the comments page, how about we allow user to reply to any comment? The user clicks the reply button, a text input shows up below, the user types in the reply and it gets attached right after the original comment? True this maybe achieved by refreshing the whole comments container but what if he is also in the middle of replying to another comment? What if we want that new reply to show up in some sort of animation? (If you ask why animation, I suggest some with how animation can provide visual feedback and enhance UX)?

Embrace a new paradigm not because it's "shiny", but because it can free us from our old mindsets

One of the DHH's frustrations over the current client-side MVC trend was that he thought other people are adopting this new paradigm simply because it's new. I have to say that he misunderstood them. It's the trend not because it looks new, nor is it only because it naturally provides fluent UX. It is because it opens our eyes to rethink HTML and explore the many new possibilities on how to design a better web application (interactivity is one of the keys).

DHH showed us an good example of such confinements from one's old mindset when he talked about how awesome the form_for helper is. HTML form is simply a broken piece of HTML, it made user interaction rigid and boring and it couldn't even do the HTTP method correctly. And the form_for helper was partly to help server side keep client side state (so that a form submitted can be rendered again the way it is submitted, e.g. when server side validation failed), which shows how unnatural the server side view is - letting stateless server side maintain the state at the client side.

When World Wide Web started, the interaction between client and server was designed to be get/form request and HTML response. It worked fine for over a decade, but we have started (for years) the transition to a new highly interactive WWW. Views logic resides at the client-side powered by much stronger JS engine and communication between client and server is cut down to it's bare bones - simple json data. Both make perfect philosophical and practical sense. If our client-side MVC framework isn't perfect and introduces boilerplates, then we should improve it. What we should not do is to insist on keeping view logic at the server-side simply because we have been doing that for decades.