Talks

Using Backbone.js with Rails: Patterns from the Wild

Using Backbone.js with Rails: Patterns from the Wild

by Sarah Mei

In her talk at Rails Conf 2012, Sarah Mei discusses the integration of Backbone.js with Rails, emphasizing its flexibility and the challenges faced by developers. Backbone.js serves as a lightweight framework that structures JavaScript in web applications, differing significantly from Rails' more opinionated structure. Mei shares insight from her experience handling several projects, illustrating the various ways Backbone can be utilized in Rails apps.

Key Points Discussed:
- Introduction to Backbone.js:
- Backbone is not a traditional MVC framework; it organizes code with models, views, and templates, but lacks a controller, which makes its terminology almost counterintuitive for Rails developers.

  • JavaScript Integration Challenges:

    • Developers often come from a Rails background with limited JavaScript experience. As web applications evolve, the need for a structured JavaScript approach becomes crucial, especially for increased interactivity and responsiveness.
  • Evolution of JavaScript in Rails Apps:

    • Mei outlines phases of JavaScript usage in Rails, from relying on simple helpers to using more complex libraries like jQuery, eventually leading to the adoption of frameworks like Backbone.
  • Development Patterns:

    • Two primary patterns emerge for using Backbone:
    • Greenfield apps: These are new applications where Rails functions as an API, with Backbone handling client-side rendering and navigation management through models and views.
    • Frosting approach: For existing server-rendered apps, Backbone allows for an incremental transformation, overlaying client-side behavior on current server-rendered elements.
    • Mei emphasizes how Backbone allows for semi-automatic model mirroring, making CRUD operations more straightforward by reducing boilerplate code.
  • Event Handling and Rendering:

    • Backbone’s event management and DOM manipulation are crucial for creating responsive user experiences, though it introduces complexity that may be difficult for less experienced developers.
  • Advice for Developers:

    • For new apps, developers might consider more structured frameworks like Ember.js or Meteor. However, Backbone is ideal for transforming existing applications to enhance client-side interactivity due to its flexibility.

Conclusion:
Sarah highlights that while Backbone.js requires more boilerplate code, it provides a necessary structure for developing modern web applications. Developers need to assess their project needs carefully to decide if Backbone aligns with their goals, especially when transitioning from purely server-rendered applications to more interactive client-side experiences.

00:00:24.690 I'd like to introduce myself a little bit first. My name is Sarah Mei, and I am a JavaScript and Ruby developer at Pivotal Labs in San Francisco. I've been at Pivotal Labs for about two years.
00:00:31.000 Pivotal is a consulting company, which means that I see a lot of different projects coming through. Before I was at Pivotal, I could spend a year, a year and a half on one project, and now my average project lasts about two months. In the last year, I've had five different projects using Backbone, one using Ember.js, and two that didn't use anything at all.
00:00:44.170 So I've seen a lot of different ways that Backbone is used. Backbone is a lightweight framework, which is both a blessing and a curse. It makes it different from Rails, which is very opinionated; there's a right place and a wrong place to put your code. One of the advantages of that is that you, as a Rails developer, can go from one Rails app to the next and have some confidence about where the code is going to be.
00:01:08.590 However, Backbone is not that way. Every Backbone project I've been involved with has completely different code, part of which is due to the fact that Backbone is fairly new technology. We haven't established a lot of well-established patterns for using it, and part of it is that Ruby developers in general aren't very well-versed in JavaScript, myself included.
00:01:36.579 What I'm going to cover today is a bit about what Backbone is, a little about the history of the relationship between JavaScript and Rails, and then we're going to look at some differences in terminology, what Backbone offers, and I'll present two different patterns that I've seen for using Backbone in real apps. I will also be publishing these slides along with the annotated source code later this week, and you can find that on Twitter, so don't worry about taking exhaustive notes.
00:01:54.610 To start, I’d like to conduct a little poll: How many people have worked on a Rails app that used Backbone.js? (A good portion of the room raised their hands.) How many people are here just to find out what Backbone is? (Seems like a lot of the same hands got raised, which is interesting.) And how many of you are here to find out whether your patterns of Backbone usage look anything like mine? (You see the same people keep raising their hands.)
00:02:19.690 So, let's start with a bit about what Backbone is. It is not an MVC framework for JavaScript, although you will see a lot of people refer to it that way and it gets lumped in with many other frameworks that follow a more traditional MVC structure. This can be very confusing for us as Rails developers because the components of the MVC—Model, View, and Controller—are different from what we think of in Rails.
00:02:34.049 So, let's put MVC aside for a moment and think of Backbone as a set of compartments where you can organize different types of code. Unfortunately, the names of the compartments in Backbone and the names in Rails are similar but don't serve the same function, which can add to the confusion.
00:02:52.229 The first thing to know is that Backbone doesn’t have anything called controllers, which is one reason why it’s not an MVC framework—there's no 'C' in Backbone's 'MVC'. However, it does have Models, Templates, and Views. In Rails, we have Models, Views, and Controllers. Before we dive into this comparison of terminology, it's important to discuss why you would want to bridge the gap between these two frameworks and understand two different frameworks if you’re already familiar with one.
00:03:17.299 The basic idea is better organization of your JavaScript code. I want to unpack that a bit and discuss how we arrived at the point where we need a framework to sort this out. Let's look at the evolution of our relationship with JavaScript which also reflects my own experiences as a JavaScript developer working with Rails. I think many developers go through similar phases.
00:03:59.980 The first phase is the 'No JavaScript' phase. This is where you use the JavaScript helpers that Rails provides. When I first arrived at Rails in 2006 after coming from a J2EE background, I found the JavaScript helpers quite amazing and almost magical! I could write Ruby, and it would generate JavaScript seamlessly. For example, if you include a specific tag in your Rails view, you would get a piece of rendered HTML in your form, like the unobtrusive Rails 3 version that adds AJAX functionality to form submissions.
00:04:32.460 The magic really kicked in when you could create a link that would fetch data from the server and update part of your page without a full refresh. This approach works well for simple interactions in many apps and can be sufficient in many cases. However, to be honest, I tend to think of Rails helpers like scaffolding; great for quick setups but not something I'd implement in a real app. We’ve reached a point where JavaScript needs to be a first-class citizen in our web applications.
00:05:10.940 This evolution is largely driven by the 'Web 2.0' phenomenon, which, among other things, increased demands for interactivity that Rails JavaScript helpers can't easily provide. For instance, when you want to update two different DOM elements with different content upon a single request, chances are you'd need to begin writing actual JavaScript. When you reach that point, it’s often easier to start using jQuery directly.
00:05:36.990 This brings us to phase two: the 'jQuery Explosion' phase. Here, you convert your Rails helper calls into actual JavaScript. This 'explosion' creates a situation where the top of your views is filled with jQuery calls, plugin invocations, and AJAX requests, leading to a cluttered and unwieldy structure.
00:05:56.560 As you accumulate more AJAX calls, your views become messier and harder to maintain or test. This code becomes difficult to manage, especially when testing, because you’re usually forced to conduct slow integration tests to verify your JavaScript functionality.
00:06:18.750 This brings us to phase three: the evolution towards using page objects. In this pattern, instead of having all your functions scattered at the top of each view, you organize your JavaScript into reusable, unit-testable functions contained within a JavaScript object relevant to a specific page. For example, this means creating a page object that encapsulates the previously scattered JavaScript into a tidy, adaptable unit.
00:06:48.840 As your application grows, you might split the page class into subcomponents. This reorganization allows for each component to manage its own behavior and state, resulting in a much cleaner architecture—easier testing and improved readability as well.
00:07:05.090 However, as your product managers request increasingly dynamic user experiences, such as instant updates to the shopping cart or related product information displayed without refreshes, you'll realize that this pattern has its limitations. To address this, many teams begin considering client-side rendering using frameworks like Handlebars or Mustache.
00:07:34.680 Client-side rendering introduces its own complications, such as the need for renewed knowledge about when updates should occur. As your events multiply in complexity—especially in a nested structure—you may find that it becomes cumbersome to manage these interactions.
00:08:07.159 You might realize you need an event system to properly manage inter-component communications. Additionally, developers start to wish for a mechanism to synchronize model objects between the server and client as well.
00:08:14.670 Up until this point, you tend to write repetitive Ajax calls for CRUD operations against your RESTful API. If there’s an increase in interactivity requested by product owners, you’ll be pushed to look for more structure to hold everything together.
00:08:48.799 The evolution toward frameworks like Backbone arises from these complications. There are many frameworks available that attempt to solve these issues—each with its own approach. Personally, I think that as developers, we've collectively reached the same JavaScript wall, and we're finding various solutions to break through.
00:09:14.930 Backbone provides a decent balance through semi-automatic model mirroring, which we will explain more later. It accesses concepts of models and collections akin to Rails' models and gives you views that handle DOM events without traditional MVC separation.
00:09:33.960 It's essential to understand these parallels and differences, as this can get quite nuanced. While Backbone mimics some Rails functionalities, the definitions of components can diverge widely. A Backbone view is more akin to a controller—it controls a specific part of the DOM and responds to user actions.
00:09:50.810 Most of the time, when you’re implementing Backbone templates, it’s less about just rendering HTML and far more about orchestrating a full package of DOM interaction—event handling and lifecycle management.
00:10:07.230 Perhaps part of the challenge is that many Backbone docs cater to seasoned JavaScript developers who can neglect the Rails newcomers. As a Rails-oriented speaker, my aim is to bridge that divide and clarify how JavaScript frameworks function in the context of a Rails environment.
00:10:37.470 So, let’s take a moment to breathe and reset here. We've talked about how we arrived at a place where we need robust frameworks and when you might want to employ one.
00:10:48.139 Now, I want to illustrate how Rails apps I’ve witnessed successfully integrated Backbone and share two distinctive patterns of its application.
00:10:58.340 The first one pertains to Greenfield applications, which is a concept I frequently encounter in many new Rails projects. Here, Rails typically functions primarily as an API server, meaning that we often render everything client-side, relying significantly on JavaScript for the user interface.
00:11:20.640 This approach can be significantly labor-intensive compared to server-side rendering of HTML pages, as you must deliver rich interfaces entirely through API calls. Nonetheless, we've observed numerous clients who want to offer a single responsive site alongside diverse native mobile applications.
00:11:43.190 When executing this hybrid approach, it becomes clear that most of your back-end logic can remain unchanged. An interactive JavaScript client and mobile apps typically share several data requirements. Consequently, employing a RESTful API serving up JSON data is usually sufficient for both.
00:12:03.444 For these projects, the view structure often remains simplistic. You may still need a Rails layout for the head and body, but within the body, you generally only require a defined div with an ID corresponding to your JavaScript application.
00:12:27.279 When looking at the Rails views, this is often all you see—a single controller that loads an index template into a layout, rendering very little actual view logic but merely passing data through the controller methods.
00:12:48.750 Instead of rendering views and sending strings back to the browser, you may simply return a JSON object. This approach could involve additional layering, such as decorators, to further enhance the JSON output being delivered.
00:13:10.020 Moving over to the JavaScript side, we still start with an 'app' object that acts as a simple initialization tool. This resides in its own file and acts as the primary JavaScript that initiates functionality on the view.
00:13:33.319 Usually, this object will encompass model constructors and view constructors, in addition to an initialize method. This way, when loading the application on a page, you'll have one shot to prepare the state you need for the initial render.
00:14:00.150 Typically, this includes creating a model instance representing the current user and establishing a view that governs the navigation bar, which is a common pattern for managing global state in the application.
00:14:20.179 As we delve deeper, I want to provide an overview of the user model, which extends from Backbone's model and performs essential functions, like determining the URL for the corresponding RESTful API endpoints.
00:14:45.380 This encapsulation saves from much of the boilerplate, making it easier to perform the standard CRUD operations through attribute manipulation. Any changes made to the user object can automatically determine the necessary network requests without additional coding overhead.
00:15:06.360 As the data flows from the server, it initializes the model attributes directly from the JSON. This process connects the power of Rails data management with Backbone dynamic models.
00:15:29.440 Next, we’ll look at the navigation bar view, which also extends from Backbone view. Knowing this makes it straightforward to create views that manage specific areas of the user interface.
00:15:52.140 The view can monitor DOM elements, listen for events, and execute specific functionality, such as rendering when a triggering action occurs. In our example, the navigation bar preparation would typically incorporate DOM events you'd be concerned about, along with event handlers linked to model or component updates.
00:16:10.350 Within the 'initialize' section, we often bind vital events pertaining to model changes, ensuring that updates drive any necessary re-renders, which keeps everything synchronized.
00:16:29.420 As we roll through the key rendering methods, you'll find that this modular approach allows us to efficiently manage updates without cluttering the application's code, using clear encapsulation and well-defined events.
00:16:52.289 Nevertheless, we need to consider how these components interact as different events may trigger various changes throughout your application. Handle these components with care, ensuring efficient interaction.
00:17:14.360 I want to emphasize that there is no universal approach to managing these transitions, which leads to many diverse strategies used for managing state across different views in Backbone.
00:17:37.059 The ideas tend to follow a common principle where you think about the views as designated representatives of different portions in your application’s interface. Each view orchestrates user interaction, processing the subsequent output through event-driven actions.
00:18:01.480 Ultimately, you’ll want to leverage Backbone's router for potential solutions to improve navigational handling. The router provides a solid foundation to help handle page changes via hash-based URLs.
00:18:23.029 After handling various designs through Backbone, I think it's essential to explore the potential pitfalls that arise when managing elements in a multidimensional state, both for capacity and maintainability.
00:18:43.179 Up until this point, we’ve journeyed through how Rails and Backbone often come together in distinct patterns as you migrate your application towards a more dynamic user interface experience.
00:19:07.040 I'd like to contrast this with existing applications that embody a more traditional server-side rendering approach, with the hope of introducing within that structure a more fluid client-side experience.
00:19:27.850 Essentially, if you find yourself facing pressure from product managers who wish to modernize an app while managing a predominantly server-side rendered app, consider how Backbone can complement that experience.
00:19:49.360 In these cases, you can start implementing Backbone as a layer on top of existing markup—this is where its lightweight design can significantly reduce friction during the transition towards a client-sider experience.
00:20:07.440 By strategically overlaying Backbone on existing templates, you can reduce extensive rewrites. Instead of reinventing the wheel entirely, you can incrementally improve and enhance interactivity.
00:20:27.680 I’ve had great success with apps using existing markup and appending Backbone functionality onto those components, which demonstrates the framework’s adaptability and ease of integration.
00:20:51.760 As we look to manage these pieces effectively, it's often easier to set the groundwork for future development by blending frameworks instead of outright discarding previous methods entirely.
00:21:12.390 Utilizing existing templates alongside newly developed Backbone views creates a seamless transition that enhances usability without compromising any performance or user engagement expectations.
00:21:32.780 This flexible infrastructure can guide your application in a measurable, manageable way as you grow, adapting to more complex interactivity requests from users and product management.
00:21:52.490 Ultimately, careful strategic planning can lead to a successful integration phase, where both server-side rendering and client-side JavaScript coexist, enriching the application.
00:22:12.950 In summary, Backbone is a strong choice during app refactor processes and evolutionary changes, particularly if you want to gradually incorporate more client-side dynamics without losing the essence of existing structures.
00:22:31.950 While I long experienced success with Backbone, I also recognize that many other frameworks come with distinct features that may better suit specific project requirements, such as Ember or Meteor.
00:22:48.970 However, my enthusiasm for using Backbone lies in its adaptability, especially for existing applications transitioning toward improved interactivity.
00:23:03.820 JavaScript frameworks increasingly gravitate toward offering more structured coding environments. Given my Rails background, I find this trend beneficial—it reflects a push toward improved development practices in the JavaScript domain.
00:23:19.820 There is still a notable need for flexibility during implementation. When factoring in existing architectures, accommodating team structure, and collaborating with less JavaScript-savvy developers can all contribute to how a project evolves while using Backbone.
00:23:34.870 Ultimately, these efforts combine to yield a modernized application, where careful attention to architecture and component interaction leads to success in product development timelines.
00:23:50.880 With that said, I’d like to open the floor for questions! Thank you very much for your attention and participation.