architecture, Javascript, Programming

MVC: desktop application vs web application

Here is a post, to compare and contrast the two styles of MVC I have worked with: Web application MVC and desktop application MVC. As I understand, the desktop application MVC came first and then we tried to fit that idea to Web applications.

Lets start with a quick example of the two application types: desktop and web. An example of the web application would be the ubiquitous shopping website where the user interacts with the website via a browser. An example of desktop application would be a thick client for trading stocks or even a rich client-side UI interaction in a web application using Javascript. When contrasting between web application MVC and desktop application MVC, I would be considering purely the HTTP/network communication aspect in a web application for MVC, devoid of the Javascript, Ajax or client-side templating.

To talk a bit about the architecture, the basic components of the two styles are the same: Model, View, Controller and hence the name. Sparing the details of the pattern itself, a subtle yet important difference between the two styles of MVC is that, for a desktop application all 3 components live in the same memory space on the same machine and this has some significant implications which we will talk about later. For a web application though, the controller and model live in the server memory space but the view partly lives on the server and partly on the client. The view is built on the server but interacted with on the client (through the browser).

Coming to the control flow, in a desktop application, the user interacts with the view to generate events. These events will be intercepted by a controller action which uses the model to update/retrieve data. There are multiple ways in which the view will be updated to reflect the model state change. Either the controller will directly update the view, or by employing the observer pattern. In the observer pattern, all the components interested in the model change, will register with the model. When the model changes, it informs all the observers of the change. This is the interesting bit of communication that you do not get to see in a web application. Since all 3 components, M,V and C, are objects in the same memory space, the communication between them is richer, hence a model can notify all the interested models/views about its changes. Another interesting pattern of communication is, the direct communication between the view and model on a user event. Given that the controller has bound the correct model action to a view event as a callback, the view can directly invoke the model action on the trigger of the event. Lets put this in perspective with an example. In the desktop trading application, lets say the user has the ability to change the trading currency. This currency is being used for transactions in multiple widgets/views on the same page. In MVC land, this currency change event is tied to some update action on the trading currency model. When the user changes the currency, the model is updated directly. The model then notifies all the registered observers (predominantly models) about the currency change who then subsequently update their respective views. This communication seems very natural in a desktop style MVC.

Lets look at the web application control flow. The user interacts with the view via the browser. The view is built on the server with 2 pieces of information: one, the actual view code and second, a mapping of user event to a controller action. Each user action is converted into an HTTP request by the browser. On the server side, the web application framework invokes the controller action. The controller action uses the model to retrieve/update data, builds the next view and sends it back to the browser as a HTTP response. The browser renders the view code and then the user is free to interact with the view again. In this style of communication, all the communication between the view and model has to be channeled through the controller. Going back to our example of updating trading currency, in a web application, updating the currency would mean having a currency update controller action that updates the necessary models and then builds the entire page with updates to all the necessary views and retaining the unchanged views. This seems like inelegant approach as opposed to the desktop style of MVC.

The web application seems fit for one-page-one-user-event model where you put all the information on the page, post it to the server and get back some results. It makes the web application single-tasked and slow to respond to user events. But life is rarely simple enough to warrant a single threaded communication, especially in the world of fancy UI interactions. True to the saying, “a layer of indirection can solve every problem in computer science”, it seems Javascript can solve some of these problems. It provides the rich user interaction to a web application and fetch and update selective parts of the view using Ajax and client-side templating. It is still a pull mechanism where the Javascript is pulling all the necessary information and updating the relevant bits as opposed to a desktop application where you could update the model and then it publishes its changes to interested components who update themselves as they see fit.

Standard
Agile, Programming

Refactoring: Levels and Lessons Learnt

Refactoring is a critical factor in the successful evolution of a code base. Slow and small steps of refactoring help keep the code base healthy and clean.

I would classify refactoring into 3 levels ordered by increasing scope, and consequentially, effort.

1) class level refactoring

This is the type of refactoring typically done at the end of red-green-refactor cycle. As TDD practitioners are aware, you write a failing test for the business expectation, you fix the test and then refactor the code to make it ‘better’. Here are some examples:
a) rename methods/variables to clearly express their intent
b) extract duplicate lines of code into a method
c) memoize to avoid duplicate calls to expensive pieces of code
d) write idiomatic code. I was baffled when I first heard the word ‘idiomatic’ so let me take a minute to explain it. It means using the language constructs rather than the plain old constructs, that you find in almost every language. An example in Ruby would be: lets say you have a method that initializes an array, adds some elements to the array and returns the array. As you can imagine, these three steps would each correspond to a line in your method if written in a non-idiomatic fashion. If you were to write idiomatic code, you would use tap instead. Here is how it looks.

[].tap do |fruits|
  fruits << 'apple'
  fruits << 'banana'
end


2) story level refactoring

This is the type of refactoring, you do when the story is “functionally” complete. Here are some examples:

a) convert procedural code into object oriented code. A symptom of procedural code is, manipulating the state of an object outside its own class. Here is an example:

if(input.numeric?)
  NumberFormatter.format(input)
else if(input.date?)
  DateFormatter.format(input)
end


In this case, some code outside the input class is making a decision of which formatter to apply to a input. This clearly breaks encapsulation by exposing the input data to something outside the input class. Doing this the right way would mean, when initializing the object you create the right type of class based on its input, like a NumericInput vs DateInput, write a format method on each of those classes and simply have the client call the format method on the instance of the class. The client should not care what instance it is calling the format method on, as long as the input type knows how to format itself.

Here is how it looks:

class NumericInput
  def format
  end
end

class DateInput
  def format
  end
end


client code would be:

input.format

Believe it or not, a lot of your decision making statements will go away, if you create the right type of objects. To create the right type of objects, it is very important to identify the right abstractions for “state holders” and then push the behavior on to those abstractions(classes in object-oriented world).

b) Remove duplicate code from multiple classes and create common abstractions.

c) Watch out for performance-related refactorings like avoiding n+1 query problem, removing/merging duplicate database/service calls, caching expensive service/database calls, avoiding fetching more data than necessary from external services (example being fetching all the search results and counting them as opposed to directly getting count of results or getting database results and sorting the results as opposed to fetching sorted results), graceful handling of service/database error conditions.

3) big bang refactoring

This is the type of refactoring where you change a large part of your code, purely for technical reasons. The drivers for this type of refactoring are the exorbitant cost of changing a relatively small piece of code, performance being unacceptable, the code being too buggy and rather be rewritten than applying band-aid solutions or the code breaking in production and it is hard to tell what is going on. The effort is too big to be handled as a part of a ‘feature’ story and hence has to be done independently as one or more stories.

Big bang refactoring stories are generally hard to sell because they don’t deliver any client-facing functionality, the impact and to some extent the scope being unclear(example being refactoring Javascript code for a page to follow MVC pattern) and the fear of breaking something already working. These stories have to be drafted with a clear end goal in mind to give the developers a good idea of the scope and assess the impact. The story should enunciate the pain points being addressed as a part of acceptance criteria and the rough amount of effort required. Such stories should be more actively monitored to avoid losing focus. The stories with high level of pain and low level of effort should be prioritized first. It is possible that such stories might never get prioritized. In such a scenario, the work should be broken down into smaller chunks and handled as type 2 story-level refactoring with a related feature card.

Lessons learnt:

* Make separate unit tests for testing different intentions of a method. For an example, lets say a method returns a hash of attributes about a person object. So you would have a separate test for each item in the hash or at least a logical grouping of items in the hash. Here is an example

it "should format the name" do
  person.to_hash[:name].should == 'Todkar, Praful'
end

it "should calculate the age based on the birth date" do
  person.to_hash[:age].should == '29'
end


This way it makes it easier to understand the behavior being tested. Also if some of the logic changes or goes away it is easy to change or delete the test. Also, you could test the complicated parts more rigorously without testing everything that the method returns. You could put the test setup for the method in a before block to avoid duplication.

* Avoid unit testing of private methods of a class. Testing private methods is a smell and indicates that there is too much going on in the class being tested. The fact that you want to test the private method without going through the public interface means that you could easily create an abstraction for that code and inject it as a dependency. You could test the dependency separately and rigorously. For example, there is a method that finds the median age for a group of people stored in a database. Now as you can see, there are two separate pieces of logic here. The part where you read from the database is fairly easy to test where as testing the median age is more complicated and hence can be tested easily and thoroughly if it were its own abstraction. Also for some reason if your logic changed to find the mean age instead of the median age, your database test should remain the same.

* Try to do as much unit-level testing as possible to isolate problems during test failures. The other advantage of unit testing is that it helps reduce the combinations of integration/functional tests. Say you have to write a functional test to test a form that accepts age as one of it fields. If you have properly unit tested all the edge cases for the field accepting age as an input, then the form submission test does not have to deal with various combinations of age input. Almost always integration tests are an order of magnitude slower than unit tests. Unit tests give you faster feedback at relatively low cost.

* It is very important to have clearly defined outcomes for refactoring stories. I was on a project that had the first phase of the project dedicated to refactoring. Quickly we realized, we weren’t making much progress as it was hard to tell when a refactoring story was done. The boundary lines of the refactoring stories were blurry, hence causing confusion. I recommended that we tie refactoring effort to new feature stories, originally planned to be developed in phase 2. The approach we took was refactoring code in and around the feature we were working on. This way we had a better sense of story boundary and knew when to stop refactoring. Once the patterns were in place, it was easy to replicate those across the code base. The business also liked it as they started seeing features delivered sooner than they had anticipated.

* If you find too much mocking/stubbing of dependencies when testing a method in a class, it might be a good time to consolidate the dependencies into fewer abstractions, and delegate some of the testing to the new abstractions. Too much mocking/stubbing can be a result of a chatty interaction between multiple dependencies. It happens when you have data being passed around from one object to the other. It helps to have a owner of the data and push the behavior on to the data owner to avoid chatty interaction.

* One of the tips that I have found useful when naming classes is having the name be a noun as opposed to a verb. For example, when picking a name for a class that compares hotels, it should be called HotelComparison as opposed to HotelComparer since it feels more natural for a HotelComparison class to hold state like date of comparison, result of the comparison, requesting user. This helps in writing more object-oriented code.

* Always test the public interface. One of the biggest advantages is that unit tests don’t break when internal implementation changes. Also it strengthens the idea of having a public interface for a class.

* Functional/UI tests can be very useful when doing story level or big bang refactoring to make sure that the functionality is working as expected.

* When refactoring a large piece of code, change one thing at a time. When refactoring a complicated piece of Javascript code, we decided not to change the structure of HTML, so that we do not break the CSS and keep it out of the equation.

* When refactoring a large piece of code, follow a top-down refactoring approach. Make sure that you create the right abstractions first and establish the interactions between them in such a way that data lives as close as possible to the class manipulating it. Then move on to refactoring individual classes. If unit tests exist, it is easier to ignore them until you have stabilized on the classes and their methods.

Standard