Archive for the ‘node.js’ Category You are currently browsing the archives for the node.js category.
November 21st, 2011

Front-end JavaScript bug tracking

Proper and effective error tracking is a common issue for front-end JavaScript code compared to back-end environments.

We felt this pain as well and experimented with different solutions over the past months on the SoundCloud Mobile site.

Analytics

The first approach we had was to track errors with Google Analytics. Their library permits to fire custom events and whenever an ajax error would occur, we would log it.

The biggest benefit of this tool is to monitor the stability of the site and its evolution in longer periods as you can easily go back a few weeks or months to see which events were triggered. Also, it is easy to implement – almost a one-liner!

The drawback, at least for Google Analytics, is that this tool is not meant to track bugs. There is no way to add custom data to these events to get more insight about why and how an error happened, it also doesn’t work in real-time, and you obviously want that when you debug.

So we kept Analytics in place for a long-term view, but took a look at other options for real-time and in-depth tracking.

Airbrake

In our pursuit of getting more insight, we decided to take a look at Airbrake because we were already using it to track back-end errors on our main site.

Our mobile site runs on Node.js, the first thing we did was to integrate an existing plugin for it to handle error tracking on the back-end as well.

Looking a little further we found a front-end notifier, which would catch errors that would fire on window.onerror, but there was no way to report any custom errors.

We decided to take a day to hack this on our own since their API is public and easy to implement.

The benefits of Airbrake were instant. We could see what triggered which error, how, why, in which context, which browser, etc… in real-time!


It also counts errors, which can help you prioritize and include fixes in your roadmap.

However, the lack of filtering, grouping and custom sorting made it difficult to work with. There was also no sense of time or progress, as everything just gets dumped into a single list ordered by time. We needed something a little better than that.

BugSense

That’s when our Android team showed us their BugSense implementation.
BugSense seemed to address all of these issues we had with Airbrake: grouping is more effective, searching and filtering is possible, charts of errors are drawn as well.

There is one more benefit over Airbrake… JSON. No need to convert objects to XML strings anymore!

If you are interested in our BugSense notifier you can find the source on github.

Conclusion

There is still a lot of work needed to make front-end JS debugging as easy as it is for regular back-end environments.
For example, stack traces today aren’t that useful, because of anonymous objects and minified code, but hopefully browser vendors will tackle these issues soon. Maybe Source Maps could be the first milestone in this quest.

At SoundCloud, we will continue to use a combination of these tools because of the different strengths outlined above, but there are also other tools we didn’t try out yet like getexceptional or errorception. If you have tried these, or if you have any suggestion on this subject we’d like to get your feedback in the comments below.

Happy debugging!

Yves
August 22nd, 2011

SoundCloud mobile – Proxies

The Problem

The mobile version of SoundCloud is a consumer of our own API dog food. That decision was made with the intention to deploy a self-sufficient client application that depends only on a static provider. Our early experiements showed that the attempt we made had some downsides. For example, the implementation of redirects in CORS is not behaving properly and therefore can’t be used with many of the endpoints in our API where we rely on the correct handling. Also classic XHR communication with the API is not an option due to the same origin policy implications that apply even on subdomains.

Read the rest of this entry »

alx
August 2nd, 2011

Building the SoundCloud mobile site using backbone.js

Until early this year, there was a gap. A gap between the desktop-targeted main SoundCloud site, what we call the ‘mothership’, and the native iOS (iPhone, iPod touch) and Android applications. A common and frustrating use-case was mobile Twitter: Someone would share a new favorite or upload on Twitter, you tap on it, and it tried to load the regular site on your tiny smartphone screen. Pushing the whole desktop site over a mobile connection would be a waste of precious bandwidth, if you only want to check out a track. Alternatively we could try to redirect to our native apps, but there’s no guarantee that the user has it installed and the mobile vendors don’t offer any APIs for verifying that in advance.

With that in mind, back in December 2010, we set off to build SoundCloud Mobile, targeting the mobile browsers of iOS and Android. The analytics of the existing site told us that these two platforms make up the overwhelming majority of our users, so we started there. As a mid-term goal, we decided to expand our support to devices, as long as they have a browser capable of streaming audio.

For the architecture of the site we decided to make it a SoundCloud API client, eating our own dogfood just like the native iOS and Android apps already do. With that in mind, we considered the option of building a single-page web application (vs classic serverside rendered pages). To figure out how viable that option is, we spent a week building a prototype based on jQuery Mobile. The prototype included a start page with hot tracks, a basic search, people and track pages and basic audio streaming. The lists used the theme provided by jQuery Mobile, everything else was barely styled HTML. This prototype helped a lot in making several important decisions:

  • Building a single-page app was feasible, with the client side application as the direct API client. Later we had to back away a bit from that, introducing a proxy to decorate the API (and work around WebKit bugs), but overall most of the action is still happening on the client.
  • jQuery Mobile works great for a fixed number of preloaded and infinite number of server-generated pages, but not for our usecase of generating all pages on the fly based on API results. We needed much more flexible routing with HTML5 history.pushState support, so that we could support the theme URL sets as the main site.
  • On a similar note, jQuery Mobile’s theming system allowed us to build a pretty prototype in no time, but wasn’t a good fit for the completely customized UI that we wanted.
  • Audio streaming on mobile is still very immature. Even with support for only iOS and Android, plenty of workarounds are required for a somewhat consistent experience.

After throwing away the first prototype, we moved on to create our own basic framework. It described the domain classes like ‘track’ and ‘user’ as global singleton objects. Our ‘router’ object was responsible of passing on the model data onto the responsible controller method. Soon we could see that the approach wouldn’t scale that well, especially when simultaneous instances of a class were required on the same page.

After dismissing a few bigger client-side MVC frameworks, we’ve stumbled upon Backbone.js, which was compact, easily extendable and depended only on Underscore.js. Backbone sets up only the application structure plus it offers a multitude of convenient methods that can be used while building your app. It doesn’t dictate how the application UX works nor describes how the templates have to be structured. While that still left a lot of open questions for us to answer, it also didn’t impose too much unwanted structure.

Backbone.js let’s you choose your own templating engine, and we went with the jquery-tmpl plugin. We restricted our template usage to output and iteration within the template, both to give us the option of switching to another template engine (e.g. handlebars.js) and to keep our sanity. To implement the remaining presentation logic, we used the route suggested by Backbone.js, preparing the data for output in the Model’s toJSON method. This also has the advantage of keeping the model itself clean, making it easy to update the model and send it back to the server. In addition to that we added a decoration step, modifying the template output before inserting it into the DOM. This includes adding additional classes or removing empty nodes.

When we started using Backbone.js, it supported only hash-based history (what Twitter does today when it redirects twitter.com/ericw to twitter.com/#!/ericw). We wanted support for history.pushState to map URLs from soundcloud.com to m.soundcloud.com by only prepending the ‘m.’. We extended Backbone.history for that, while also triggering a custom event. The latter can be used by the Google Analytics tracker or any other component that has to get an update on the current page state.

We also extended regular Backbone.sync method, used by all Models and Collections to exchange data with the server, to add a client side cache, backed by the HTML5 sessionStorage. That way we didn’t have to keep any pages in memory, but can instead rerender them from scratch in milliseconds, as the underlying data is still available in the cache.

With those components in place, a click (or rather, tap) on any internal link caused the following actions:

  • Handling the click/tap event, preventing the default browser action, and using history.pushState instead to update the current address. At some point telling the Backbone.router that the page changed.
  • Backbone.router maps the URL to a controller method, which creates the model for that URL, e.g. initializing the User model with the username parsed from the URL. It then creates the view and passes the model to that view.
  • The view tells the model to fetch its data. Once done, with data loaded from the server or from the client side cache, it passes the model to a template, decorates the result and inserts it into the DOM.
  • The view also initializes event handlers (via event delegation) to handle all interactions within that view, e.g. a click event on the ‘Play’ button to start streaming audio.

This turned out to be a very solid application architecture which we continued to fine-tune after the first public launch of the mobile site in March, when we redirected iOS and Android traffic from the main site. Since then we continued to add features and improve the site, watching the traffic almost doubling every month.

Along with this new client side architecture we also experimented with alternatives for development and production. The node.js-based development and production server, including the API-proxy is covered in detail by our node ninja Alexander Simmerl. In the upcoming post we’ll also talk about our approach to testing with QUnit and PhantomJS.

matas
August 31st, 2010

node.js knockout — August 28th & 29th

A few days ago a small team of SoundCloud developers (@goldjunge, @jberkel, @purzelrakete and @sohm) participated in the first node.js knockout competition. Aptly named “Team SoundCloud”, we set out to explore the current state of server side Javascript using node.js and the real-time web.

(For those unfamliar with Node.js, head over to http://nodejs.org/ or the project wiki at http://github.com/ry/node/wiki for an introduction.)

The node.js Knockout is a competition inspired by the popular Rails Rumble. The rules are simple: Teams of up to 4 people have 48 hours to build a web app that is awesome enough to woo the judges and results in as many votes from the audience as possible. To level the playing field, each team was required to deploy their application to a dedicated hosting environment provided by either heroku.com or joyent.com. The competition started on Saturday at 2:00AM and ended on Monday 2:00 AM. Yes, that’s a weekend right there.

We had done a minimal amount of planning beforehand and agreed that we’d try to visualize traffic on soundcloud.com by hooking into the varnish HTTP accelerator that’s part of our infrastructure. We basically set out to build a pimp HTML5 + WebSockets based version of varnishtop (with a sprinkle of varnishlog, because that’s just so exciting!).

@purzelrakete, @goldjunge and @jberkel hard at work.

Being confined to the Joyent environment, the first challenge to overcome was not having access to a varnish instance that serves any real traffic. To solve that, we hooked into the Twitter Streaming API and filtered all links to the popular URL shortening service bit.ly. For each bit.ly link a request is made against the local varnish instance to generate traffic. Of course, all of these requests result in (eventually cached) 404s, but oh well. The rest of Saturday saw us create a layout, start working on the C++ extension required to access a running varnish instance’s shared memory to read logs & statistics and generate events in node, culminating in having the basic functionality and deployment configuration done at around 3AM on Sunday.

After a few hours of sleep, Sunday was a blur of getting the C++ extension to emit more statistics and log details, writing the client-side javascript that updates the DOM and wrestling with Solaris on the Joyent SmartMachine. We deployed the final version about 30 minutes before the competition deadline.

Lessons learned (in no particular order):

  • node.js is fun, building realtime stuff with it is incredibly easy
  • deploying early and continuously == win
  • Solaris is a pain
  • Twitter is full of spam and porn
  • Server side javascript!!

You can check out the app here (using Chrome or Safari): http://team-soundcloud.no.de/
Vote for us here: http://nodeknockout.com/teams/team-soundcloud

sebastian