About the Author:

Continuous Integration: A View into appendTo’s Process

August 14th, 2014

Recently appendTo has been leveling up many of our internal processes, procedures, and standards. This has been a long time coming, but has been given dedicated attention recently. One of these efforts, the focus of this article, is appendTo’s most recent upgrade to our continuous integration and deployment (CI/CD) procedure and systems. This is one big piece of a larger standards and practices effort that is ongoing within our organization, and which benefits both our customers as well as our staff. If you would like to read more about our general developer workflow, standard practices, and tools you should read Aaron Bushnell’s recent article titled “The Tools We Use“!

Unique Requirements

There are some unique challenges to solve in a professional services organization when it comes to project server management. Each client, and each project for those clients, inevitably has unique software requirements. One client may have a back end in Rails which requires a specific application and testing stack while another may use Node.js with MongoDB. Furthermore, we could have one client which needs to use Node.js 0.10.28 because 0.10.29 has some change that breaks their application. These widely varying requirements result in appendTo not being able to simply deploy all client applications to a single staging server, we have to have unique environments.

Unfortunately, we don’t typically have the freedom to assign a server administrator to each project, and even if we could, that would not be the best use of our time, and thus our client’s investment. The solution that appendTo has implemented balances the needs of our clients with ease of use for our developer staff. The system is highly automated, but allows for extensive customization of the entire build process by the developers through simple JSON configuration within the project repository. We use TeamCity for version control triggers, build step management, access control, and reporting and logging. On the other side of the process we use Digital Ocean (and their HTTP API) to dynamically generate virtual machines (droplets) when needed and host and serve application files.

Bridging the Gap

The biggest problem we had with TeamCity was the ability to integrate, in the way we needed to, with the Digital Ocean API and individual droplets. We needed to create new droplets on the fly, allow each application to configure them during provisioning, deploy the application files, test them, and of course serve them up for our clients to see. TeamCity is written in Java, and while it may have been nice to integrate directly with that piece of this puzzle, we decided to create a solution which abstracts us one layer from that tool. That drove our engineers to create a bridge application which accepts commands (in this case from TeamCity) and executes code in our deployment system (Digital Ocean droplets) via a set of Node.js scripts and libraries. The benefit is that if either TeamCity or Digital Ocean need to be replaced in our stack, our unique build and deployment process can be salvaged and reused in the new stack. Of course, one of our greatest strengths at appendTo is our deep knowledge of JavaScript, which made Node a natural choice for this bridge.

So what does this all look like? The diagram below identifies the big pieces, on the left we have Github, where all of our application code resides. TeamCity, on the server in the center of the diagram, will identify code changes in a repository and initiate a build process. The build process will send various commands, in a particular order, to the Node application which in turn interacts with the Digital Ocean HTTP API and directly with the droplets over ssh. The droplets are provisioned when necessary (usually on first run) from a standard image that gives projects access to commonly needed software (like Node). Finally, the Node scripts report the results of each action to TeamCity in order to create a single source for build logs and history.

ci-cd process diagram

Wrap It Up and Put a Bow On It

This setup works very well for our needs, but we wanted to make things easier on our developers. To that end we made each step of the build process configurable within the project itself. Many of our projects use Grunt to run tests, concatenate and minify files, and generally build a project for distribution. In order allow for the simplest integration of that process, the specific actions required for each build step are specified in the “scripts” block of the project’s package.json file (a Node artifact).

Here is what that might look like:

{
    "name": "my-project",
    ...,
    "scripts": {
        "provision": "apt-get install mongodb",
        "install": "bower install",
        "test": "grunt test",
        "stop": "forever stopall",
        "deploy": "grunt deploy",
        "start": "forever server/main.js"
    }
}

Each of the lines in this “scripts” block maps to a step (or part of a step) in the larger build process. They will all be executed within the code directory, meaning developers don’t need to worry about where the code is located and can focus on what needs to be done to make it work. Additionally, since each line is executed (essentially) on the command line over ssh, the developer could specify a shell script should their process be overly complex for a one-line statement. Note that using Grunt is not at all required, but this process simplifies using tools like it with continuous integration and deployment.

Sustainable Process

As we discussed earlier, the goal with this bridge application written in Node is portability. Should we need to transition to a new tool on either side of that diagram above, we can reuse what we have written and our existing projects will require no alterations. This makes our developers happy, our clients happy, and results in better solutions delivered faster.

By implementing better standards and practices, and properly vetting those through our development staff, appendTo is able to accomplish two simultaneous goals: rapidly deliver better solutions to our clients and make the lives of our developers easier. The first may seem obvious, better standards (and adherence to them) directly translates into cleaner, more manageable code. The second may not be as obvious, but it is just as important to us. We value our developers’ time as well as their opinions. That’s why we vet our standards through them. Any employee is able to submit a change to our standards and practices. This change will be discussed among the entire organization and ultimately is approved by an S&P board which is composed of upper level engineers.

About the Author:

Getting Zumba’s Responsive Website into Shape

July 15th, 2014

We partnered with Zumba Fitness to help with the front-end development of their new responsive website’s home, shop, user authentication and checkout pages. They provided great looking mocks and guidelines and worked closely with our team throughout the project’s life cycle. The most notable requirement was their desire to use the Foundation responsive framework from Zurb. (more…)

About the Author:

Do you need jQuery? Can you afford the risk?

March 5th, 2014

appendTo produces a bi-weekly newsletter called the Modern Web Observer that will keep you up to date on trends with the Modern Web. Below is the introduction from Issue #30. You can sign up at https://appendtonew.wpengine.com/mwo

It seems that the issue of, “Do you need jQuery”, makes the headline rounds every few months. I always giggle when I see these posts, because they try to make the point that browsers have evolved to the place where native JavaScript methods can replace jQuery methods.

Measure by risk

Our position here at appendTo, as on many things in the Modern Web, comes down to risk.

  • Can you afford the added cost and added risk to build a production level project without jQuery?
  • Do you want to depend on Browser API’s staying consistent for the entire lifecycle of your Modern Web application?

Your answer may be different. For any serious application, we wouldn’t take the risk and we don’t think you should either.

FluentConf 2014

We’re incredibly excited to be participating in FluentConf next week. Fluent has emerged as one of our favorite Modern Web events and if you’re attending, please stop by our booth to say hi. Our own VP of Training, Ralph Whitbeck, is speaking on “Performance Tips when Developing with jQuery Mobile” on Thursday at 12:00pm. See you there!

About the Author:

Encapsulate What Varies

April 10th, 2013

Nicholas Cloud is a Software Architect at appendTo and has a real passion for deconstructing business problems, creating abstract, reusable and flexible solutions, and creating awesome software that rocks. Nicholas writes about what drives him when he is architecting software for clients, encapsulating code for ease of maintainability and readability.

I was once asked in an interview: Which object-oriented design principle do you think is most important?. The answer was an easy one. Encapsulate what varies. This principle has become my motto for development, and is the tag-line for my own blog. Whenever I evaluate my own code (or the code of others), I always look for ways to use this principle to make code more expressive and maintainable. I want to write simple, terse, bite-sized code that is easily digested. When I pay attention to what varies in my code–when I start to see where things are common yet divergent–I can refactor to enable future changes while attaining further clarity in the present. The tangible artifacts of this practice tend to be:

  • expressive methods/functions
  • concrete member names
  • more modules/classes
  • shorter modules/classes
  • fewer mortal enemies

When I return to my own code after a period away it is easier to grok and extend. And clients like that.

In this post, I will examine two common scenarios in which encapsulation can be used to deconstruct poor code and create better implementations. These examples, though contrived, are not exaggerated.

Branching statements

Branching statements often indicate variation. You have probably seen (or written) code that looks like this:

//assumes no particular view framework
function UserDetailsView (user) {
  this.user = user;
}

UserDetailsView.prototype.render = function () {
  if (this.user.role === 'teacher') {
    this.showClassRoster();
  } else if (this.user.role === 'student' && !this.user.isGraduated) {
    if (this.user.classmates.length) {
      this.showClassmates();
    }
  } else {
    this.showWelcome();
  }
};

Though this example uses an if/else statement, we can easily imagine a switch/case in its place. There are several things that vary in this method:

  • how the view determines which DOM elements it must render
  • what the view actually does when it renders

Notice that it is the view that varies, not the user, though the user is tested to determine which path execution must travel. It is entirely possible (and likely) that we will see these kinds of branching statements elsewhere in our view:

UserDetailsView.prototype.onContactInfoFormSubmitted = function (e) {
  var validationMethod = this.defaultValidationMethod;
  if (this.user.role === 'teacher') {
    validationMethod = this.teacherValidationMethod;
  }
  if (!validationMethod()) {
    this.showValidationError();
    e.stopPropagation();
    e.preventDefault();
  }
};

As more roles are added, these methods will be expanded to account for diverging scenarios, and the code will become complex and unmaintainable. At any given time, the state of this view should adapt to one–and only one–user role, but bugs will be introduced and the view state will become corrupted. Suddenly behavior intended for students will mysteriously execute for teachers, and we will find ourselves spending hours debugging to figure out why.

The solution is simple: encapsulate what varies. And what varies is the view itself, with respect to user roles. There are three states we need to account for: a default state, a teacher state, and a student state. We can see that behavior diverges when rendering, and during validation.

// DEFAULT VIEW
function UserDetailsView (user) {
  this.user = user;
}
UserDetailsView.prototype.showWelcome = function () {/*...*/};
UserDetailsView.prototype.showValidationError = function () {/*...*/};
UserDetailsView.prototype.render = function () {
  this.showWelcome();
};
UserDetailsView.prototype.onContactInfoFormSubmitted = function (e) {
  if (!this.validate()) {
    this.showValidationError();
    e.stopPropagation();
    e.preventDefault();
  }
};

// STUDENT VIEW
function StudentDetailsView(user) {
  UserDetailsView.call(this, user);
}
StudentDetailsView.prototype = new UserDetailsView();
StudentDetailsView.prototype.render = function () {
  if (this.user.classmates.length) {
    this.showClassmates();
  }
};

// TEACHER VIEW
function TeacherDetailsView(user) {
  UserDetailsView.call(this, user);
}
TeacherDetailsView.prototype = new UserDetailsView();
TeacherDetailsView.prototype.validate = function () {
  //teacher-specific validation
};
TeacherDetailsView.prototype.render = function () {
  this.showClassRoster();
};

Each view now manages its own role-based state, and all methods are more expressive and more terse than our original design. The problem of instantiation still remains. It may be solved any number of ways, but with encapsulation in mind, we will allow each view to determine if it should be rendered or not. We will do this by:

  1. Adding a handles method to the constructor of each view. It returns a boolean value after evaluating its arguments to determine if the view should be instantiated.
  2. Registering all views with a “static” collection on the UserDetailsView constructor via a register method.
  3. Instantiating a view by calling UserDetailsView.create() instead of newing up one directly. This method acts as a simple factory method. Its arguments will be passed to each registered constructor’s handle method, and the first view that can handle them will be instantiated.
function UserDetailsView (user) {/*...*/}
/*
 * Constructors can be registered with the base
 * constructor
 */
UserDetailsView.ctors = [];
UserDetailsView.register = function (ctor) {
  UserDetailsView.ctors.push(ctor);
};
/*
 * The `create` method will be called in lieu
 * of direct view instantiation.
 * Assume underscore.js
 */
UserDetailsView.create = function (/*arguments*/) {
  var args = arguments;

  // find the ctor that can handle arguments
  var ctor = _.first(UserDetailsView.ctors, function (ctor) {
    return ctor.handles.apply(null, args);
  });

  // if no ctor handles arguments, assume UserDetailsView
  ctor = ctor || UserDetailsView;

  // factory function can pass all args to any view ctor by
  // using `Function.apply`; this allows each view to have
  // any constructor parameters that it wishes
  function Factory() {
    return ctor.apply(this, args);
  }
  Factory.prototype = ctor.prototype;
  var instance = new Factory();
  ctor.prototype.constructor.apply(instance, args);
  return instance;
};

/*
 * Set up `handles` method for teacher view
 * and register it with the base constructor
 */
function TeacherDetailsView(user) {/*...*/}
TeacherDetailsView.handles = function (user) {
  return !!user &&
    user.role === 'teacher';
};
UserDetailsView.register(TeacherDetailsView);

/*
 * Set up `handles` method for student view
 * and register it with the base constructor
 */
function StudentDetailsView(user) {/*...*/}
StudentDetailsView.handles = function (user) {
  return !!user &&
    user.role === 'student' &&
    !user.isGraduated;
};
UserDetailsView.register(StudentDetailsView);

/*
 * Get a view instance from the factory method
 */
var user = {
  role: 'teacher',
  name: 'Obi Wan Kenobi'
};

var view = UserDetailsView.create(user);
// (view instanceof UserDetailsView) === true
// (view instanceof TeacherDetailsView) === true

This becomes even more practical when using a loader like require.js. Sub-views can be loaded and registered with the generic, default view. Other modules only need to require the default view to gain access to its factory method and all views it subsumes.

define([
  'underscore',
  'views/StudentDetailView',
  'views/TeacherDetailView'
], function (_) {

  function UserDetailView(user) {/*...*/}
  UserDetailView.register = function (ctor) {/*...*/};

  var ctors = Array.prototype.slice.call(arguments, 1);

  _.each(ctors, function (ctor) {
    UserDetailView.register(ctor);
  });

  return UserDetailView;
});

// elsewhere...

define([
  'views/UserDetailView'
], function (UserDetailView) {
  var user = {
    role: 'student',
    name: 'Anikan "Cranky Pants" Skywalker'
  };

  var view = UserDetailsView.create(user);
  view.render();
});

Each view is now responsible for its own state, and for rendering only the DOM elements with which it should be concerned. Errors are less likely to be introduced because each view does a limited number of well understood operations, and state manipulation is uncomplicated. Code that interacts with these views, via the factory method, will not care that the views are actually different implementations, only that they share a uniform interface and may be used in identical ways.

External state checks

Encapsulation can also be a powerful tool for eliminating external state checks (checking an object’s state from “the outside”). I see this kind of code often:

var library = {
  checkoutBook: function (customer, book) {
    if (customer &&
      customer.fine <= 0.0 &&
      customer.card &&
      customer.card.expiration === null &&
      book &&
      !book.isCheckedOut &&
      (!book.reserveDate || book.reserveDate.getTime() > (new Date()).getTime())) {
      customer.books.push(book);
      book.isCheckedOut = true;
    }
    return customer;
  }
};

This code has several problems:

  1. You have to read, and understand, every condition in this function to know what it actually does.
  2. The criteria for determining if a customer can check out a book, and if a book can itself be checked out, is external to those objects. This means that this code must be duplicated if this check needs to be performed in another location.
  3. New team members, ignorant that it already exists in the code base, might try to re-implement this algorithm elsewhere. They will likely get it wrong because they are unaware of every condition involved (unless this has been studiously documented somewhere, which is a laughable).

The variations in this method are, of course, the conditions that need to be checked. We can eliminate the ambiguity by reducing this method to a few lines and encapsulating the conditions into their respective objects:

var library = {
  checkoutBook: function (customer, book) {
    if (customer.canCheckoutBook() && book.isAvailable()) {
      customer.checkout(book);
    }
    return customer;
  }
};

var customer = {
  canCheckoutBook: function () {
    return !this.hasFine() &&
      this.hasActiveLibraryCard();
  },
  hasFine: function () {
    return this.fine > 0.0;
  },
  hasActiveLibraryCard: function () {
    return this.card !== null &&
      this.card.expiration === null;
  },
  checkout: function (book) {
    //implementation
  }
};

var book = {
  isAvailable: function () {
    return !this.isCheckedOut &&
      !this.isReserved();
  },
  isReserved: function () {
    return this.reserveDate !== null &&
      !this.isFutureReserve();
  },
  isFutureReserve: function () {
    return this.reserveDate.getTime() > 
      (new Date()).getTime();
  }
};

And, of course, when checkout conditions change–as we know they will–we can update our code in customer and book without touching library, or any other module or object that depends on these entities. Since no other code makes external state checks, our objects are well encapsulated.

Conclusion

In the real world, every person has different tastes, preferences, goals and values. People who live in close communities tend to share a homogeneous culture, and so their values align more often than not. As society grows and new members are introduced, the likelihood of cultural divergence grows. Values begin to differ. People make different choices about how to use their time, energy, resources, labor.

Direct exchange of goods and services becomes difficult because of specialization, and so a common exchange medium is needed for society to scale. This medium–this tool of encapsulation–is currency. When I hand a few dollars to a barista for my coffee, I avoid the necessity of exchanging my particular skills or or time for hers. Indeed, I would most likely be long in the throes of caffeine withdrawal before we could agree on an exchange rate for “hours of programming” to “cups of coffee”. Instead we use currency to encapsulate our labor (I want to spend my time programming; the barista, crafting fine coffee). That the person in line behind me writes books, and the person behind her fixes automobiles, and the person behind him runs a non-profit is irrelevant; they will all use the same currency to buy coffee. The currency encapsulates what varies.

Our code is not unlike the real world. As complexity increases, as our code base grows, things will vary greatly as new business cases are introduced and old ones are refined. The reason we encapsulate code is so that we can isolate those differences behind common, unambiguous protocols, though the implementation of each variation may in fact be complicated.

Now go forth and encapsulate what varies.

appendTo offers JavaScript Training for Teams.

About the Author:

appendTo Congratulates Backbone.js on 1.0 Release

March 21st, 2013

appendTo would like to congratulate the Backbone.js project on their 1.0 release. We’ve been using Backbone.js in projects for our clients and internally for quite some time.

backbone.js

As experienced JavaScript programmers we’ve found that our applications can become unmaintainable pretty quickly. When developing jQuery applications, for instance, the piece that is missing is a way to organize your code. It’s all too easy to create JavaScript applications that end up as tangled piles of jQuery selectors and callbacks, all trying frantically to keep data in sync between the HTML UI, your JavaScript logic, and calls to the API for the data. Backbone.js provides a way to abstract your applications into separate smaller pieces that are then easy to maintain.

We’ve used Backbone.js in the following types of projects:

  • HTML5 Web Apps
  • Mobile/Cordova Phonegap apps
  • jQuery Mobile apps
  • Custom API/Frameworks

What our Developers are saying about Backbone.js

“The part of Backbone.js that I’ve always enjoyed the most is the Views. While some may see it as a thin wrapper, I see its power in the simplicity and conventions that it establishes, giving each piece of presentation code a distinct place to exist. Having organization improve maintainability is nothing that should be taken lightly and I think Backbone.js nails it.”

Eli Perelman
Director of Training

“I like backbone because it’s very easy to customize and has great extension points. Plus the community support around it makes it easy to learn.”

Jonathan Creamer
JavaScript Engineer

“Backbone plays a critical role in one of our large-scale client projects. We replaced Backbone.Sync with a uniform interface that spans several transports (amplify, websockets, http, etc.). Our models are now transport-agnostic, which makes feats like multi-browser updates fairly simple. We also created customized base views that can take advantage of postal.js to subscribe to, and publicize events on UI message bus channels. We do this in a declarative fashion, taking advantage of the Backbone view lifecycle to “auto-wire” bus operations to view events that are specified in View.extend(). Overall our experience with Backbone has been positive. It is a good foundation that is stable but not rigid and extensible but not messy.”

Nicholas Cloud
Architect

Let us build your next application with Backbone.js

At appendTo we get excited by a challenge. Let us develop your next web application and there is a good chance we will use Backbone.js to help keep it all organized. We even offer Backbone.js training so that your developers can take the code we deliver and maintain it. Contact us if you’d like to discuss your next project.