My adventures in front-end-land

My adventures in front-end-land or two backbone apps later… Lessons learned.

I have to admit, I’ve been a secret admirer of great front end developers. I thought my brain wasn’t wired for HTML/JS/CSS.
My wife Marianne and I started a SaaS about a year ago aimed at off-loading teachers in schools teaching math for kids aged 6-12 years old. You find the service at http://nomp.se/
The service makes it a breeze to give out personalized assignments and to follow them up. All content is automatically generated, so the teacher doesn’t have to produce the assignments either. The service is free for all kids to use. If you have a subscription you can do the teacher/parental thing.

When you try to run a bootstrapped (self-funded) business you have to roll up your sleeves and just get things done. Often there are things that you haven’t done before. Marianne and I are Java developers by trade. I have a background in Unix sys-admin but haven’t done that professionally in 12 years. Instead I’ve been coding Java, with a break between 2007-2011 when I was Chief Architect for Unibet. It was a very challenging and a learning experience technically, but there is no time for coding when you’re constantly running or governing key transformation projects and trying to be a line manager at the same time.

Some of the jobs that need to be performed in our startup include:

  • Business development / requirements management
  • Back-end developer (Java/Spring)
  • Front-end developer (HTML/CSS/LESS, jQuery/Underscore/Backbone)
  • Quality assurance (testing all the different browsers etc)
  • Systems administrator (Amazon Web Services: EC2/RDS/R53/SES/SQS/Cloudfront, Apache/Tomcat perf tuning, Security patching)
  • Database administrator (Tuning, backup strategy)
  • Configuration management (Maven/Jenkins/RPM-packaging and deploy automation)
  • Sales and marketing (Social media campaigns and online ads)
  • Finance (book keeping, invoicing)
  • Customer service (email support)

As both Marianne and I still have our daytime jobs to manage, there is a lot of late nights… but also a lot of fun!

The first versions of our Math SaaS was a “traditional” server-side rendered application using Spring MVC and Freemarker for templating.
After the initial launch, we spent some time looking at rewriting the most heavily used part – the student quiz – in Javascript in order to improve user experience. We didn’t know much about jQuery, Javascript or Ajax at this point.

The first rewrite did the job. It off-loaded the server and improved the user experience in terms of response times, as eleven server round trips was reduced to two (start the quiz and submit result). However, it was unmaintainable due to a number of things:

  1. No name spacing or modules (all functions in the global space)
  2. Adding a new quiz type involved changing the core quiz logic which meant that stuff broke
  3. The DOM manipulation was spread out all over the javascript code
  4. Client side templating of the quizzes was done with custom code
  5. We had two implementations, one for the desktop and another for mobile (jQuery mobile)
  6. The markup was a mess, without good structure and class names
  7. HTML5 canvas code was too low level to be productive

So after spending some additional months doing other stuff, we increasingly felt that the solution had grown out of hand.
We then spent some time researching the front-end open source space and found some interesting contributions:

Backbone.js

Backbone is a tiny MVP (model-view-presenter) framework for developing client side applications in Javascript.
For better and for worse, its a tiny frame work, only about 7000 bytes to transfer minified/gzipped.

Twitter Bootstrap

Bootstrap is a HTML/CSS framework with standardized markup and ready-made components that can be customizable. It supports responsive design so that the same site can be used for mobile, tablet and desktop.

KineticJS

KineticJS is an HTML5 Canvas JavaScript framework that enables high performance animations, transitions, node nesting, layering, filtering, caching, event handling for desktop and mobile applications, and much more.

We then started to rewrite the quiz engine once again using Bootstrap and it was quite a struggle, being javascript rookies.
Backbone is elegant, but there is a lack of good tutorials beyond the trivial hello world examples. I learned a lot from Christophe Coenraets blog post: http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/ even though it’s very simple (in retrospect).

We also struggled with things I take for granted in other frameworks, such as a best-practice project structure and naming practices.

Never the less, we shipped a new quiz engine in backbone/bootstrap for Nomp.se in less than a month, which was pretty ok considering our skill level at the time. Was it perfect? No, but it was a huge improvement. Now we can extend the quiz engine with new quizzes quickly in a modular way.

The next evolution was a complete rewrite of the teacher/parent backoffice, which was pretty limited from a functionality point of view and a horrible user-experience to be honest.

This was a considerably bigger effort with twenty odd views and perhaps twice as many use-cases.
In this case we felt it was necessary to use a module system (and a module loader) in order to track dependencies between components.

Require.js does a pretty good job at this, but we felt the documentation is hard to follow. It comes with an optimizer to minify and combine javascript. Not too many people seem to be integrating javascript into their Maven build either, so it takes a while to find a good maven plugin that allows you to run javascript from Maven.

When it comes to data-binding, we started to use Backbone.Forms, but we quickly felt it was overly complicated and not always designed by be extended. I18n wasn’t supported in a good way either.

To minimize duplication, we ended up rolling our own minimal data binding solution that consists of a Backbone model mixin, a view mixin and a few JS helpers.

Here’s how the resulting code looks when using the mixins. The code implements a form with client-side and server side validation. Both types of errors are displayed in the form,

Backbone Model and View

Backbone.Model.extend({
 urlRoot: "/api/profile",
 initialize: function() {
 var mixin = new Util.ErrorHandlingMixin(); // <--- the pixie dust
 _.extend(this, mixin);
 },
 validate: function(attributes, options) {
 var errors = new Array();
 if (this.get('firstName') == null || this.get('firstName').length < 2) {
 errors.push({ attr: 'firstName', error: 'You need to provide at least two characters' });
 }
 if (this.get('lastName') == null || this.get('lastName').length < 2) {
 errors.push({ attr: 'lastName', error: 'You need to provide at least two characters' });
 }
 if (this.get('rewardIcon') == null) {
 errors.push({ attr: 'rewardIcon', error: 'Mandatory field' });
 }
 if (errors.length > 0) {
 return errors;
 } else {
 return null;
 }
 },
 getAllRewardIcons: function() {
 return ["BLUE_STAR", "YELLOW_STAR", "GREEN_STAR", "ORANGE_STAR",
 "BLUE_HEART", "YELLOW_HEART", "GREEN_HEART", "ORANGE_HEART",
 "BLUE_SMILEY", "YELLOW_SMILEY", "GREEN_SMILEY", "ORANGE_SMILEY",
 "BLUE_CANDY", "YELLOW_CANDY", "GREEN_CANDY", "ORANGE_CANDY"];
 },
 getAllLocales: function() {
 return["en_GB", "sv_SE"];
 }
 });
 BaseView.extend({
 initialize: function () {
 var mixin = new Util.ErrorHandlingViewMixin(); // <--- the pixie dust
 _.extend(this, mixin);
 this.model.on("invalid", this._showErrors, this);
 },
 events: {
 "click .save-form": "saveForm",
 "focus input": "validateForm"
 },
 template: _.template(tpl),
 render: function () {
 $(this.el).html(this.template(_.defaults(this.model.toJSON(),
 {rewardIcons: this.model.getAllRewardIcons(), locales: this.model.getAllLocales()})));
 return this;
 }
 });
 });

Markup with helper functions

<div>
 <form class="form-horizontal">
 <fieldset>
 <legend><%= i18n.t('Personal information') %></legend>
 <div class="control-group"><label class="control-label"><%= i18n.t('First name') %></label>
 <div class="controls"><input name="firstName" type="text" value="<%- firstName %>">
 <div class="help-inline"></div>
 </div>
 </div>
 <div class="control-group"><label class="control-label"><%= i18n.t('Last name') %></label>
 <div class="controls"><input name="lastName" type="text" value="<%- lastName %>">
 <div class="help-inline"></div>
 </div>
 </div>
 <div class="control-group"><label class="control-label"><%= i18n.t('Description') %></label>
 <div class="controls"><input name="description" type="text" value="<%- description %>">
 <div class="help-inline"></div>
 <div class="help-block"><%= i18n.t('The description is shown to students that would like to become your mentee') %></div>
 </div>
 </div>
 </fieldset>
 <fieldset>
 <legend><%= i18n.t('Settings') %></legend>
 <div class="control-group"><label class="control-label"><%= i18n.t('Language') %></label>
 <div class="controls">
 <%= forms.select("locale", locale, "Locale", locales) %>
 </div>
 <div class="help-inline"></div>
 </div>
 <div class="control-group">
 <div class="controls">
 <%= forms.checkbox("subscribedToNewsletters", subscribedToNewsletters, i18n.t('Send me news about Nomp (about two times per month)')) %>
 </div>
 </div>
 <div class="control-group">
 <div class="controls">
 <%= forms.checkbox("subscribedToNotifications", subscribedToNotifications, i18n.t('Send me quest notification emails')) %>
 </div>
 </div>
 <div class="control-group"><label class="control-label"><%= i18n.t('Reward symbol') %></label>
 <div class="controls">
 <%= forms.select("rewardIcon", rewardIcon, "RewardIcon", rewardIcons) %>
 <span><img class="reward-icon" src="/static/img/rewardicon/<%=rewardIcon%>.png"></span>
 <div class="help-block"><%= i18n.t('The reward symbol is used in quests that you give out.') %>
 </div>
 </div>
 </div>
 </fieldset>
 <div class="form-actions">
 <button class="btn btn-primary save-form"><%= i18n.t('Save') %></button> <span class="server-error error"/>
 </div>
 </form>
 </div>

We’d love your feedback on the binding issue. If people find it useful, we’ll probably open source the small data-binding framework when we’ve used it a bit more. We think it minimizes boiler-plate code and avoids duplication without violating Backbone principles such as using Model.validate() for validation.

Let us know what you think!

Advertisements