Besides creating videos for Treehouse, I got to work on the Code Challenge engine which is used to test your knowledge and skill by writing real code. We decided it would be best to build this application independent of the main Treehouse rails application.
I got to choose the tools for the Code Challenges, and now I get to share with you what tools I used, why I chose them, and how they worked out. The code challenge is built as two small, independent applications using NodeJS, Dojo, CoffeeScript, RequireJS, CouchDB, and the Ace Editor.
There are two major components involved in the Code Challenge tool. One component is the editor interface that you interact with. This is the only component you really see and touch. The other is the component that actually evaluates the code you write to see if it is correct.
I decided to make these two components completely separate applications. The only coupling between them is the editor component calls the test evaluator via a web service. This turned out to be a very good decision, because the two applications have very different resource needs.
The editor application pretty much entirely runs in the web browser. The server for the editor does a very small amount of work. It was able run on a single Heroku dyno and survive the spike of traffic generated when Treehouse launched.
The code testing application, on the other hand, does a lot of heavy lifting on the server. Because of this, it isn’t able to run on Heroku, so instead we ended up running it on Amazon EC2 instances. Since the testing application is entirely stateless, we are able to fire up multiple instances of the server behind a load balancer. This allowed us to keep challenges up an running as thousands of people began taking challenges.
In this article, I will mainly be talking about what powers the code editor component.
NodeJS powers the application and web servers that run our Treehouse Code Challenges. The servers are actually very thin. Besides serving the static assets, the servers mainly act as a proxy to the database and other services.
Node is particularly suited for the task of proxying data because it can handle a lot concurrent connections, since the server is only busy while actually moving the information through the server. The time the connections are idle and waiting can be used to serve other clients.
After some prototyping with YUI and Dojo, I decided to use Dojo. It had a larger variety UI Widgets, and seemed like a pretty solid choice. There are a lot of great options I could have chosen, but I chose Dojo because it was new to me, seemed to have a lot of support, and integrated well with the AMD module pattern that I was using via RequireJS.
The main drawback is that Dojo’s documentation can be a bit tricky to navigate, and sometimes I just had to dive into the Dojo source to figure out what was happening. Ultimately I feel confident building with Dojo, but there is definitely a learning curve when you get started.
I chose CoffeeScript because I liked CoffeeScript, and wanted to find out if using it in a large project would make me love it or hate it. Well, now I love it. My code is both readable and maintainable and a significant amount of boilerplate code has been removed. The way CoffeeScript handles classes and inheritance made it super convenient to subclass Dojo widgets for the UI. I hope to delve deeper into this in an upcoming article.
RequireJS does a phenomenal job at allowing you to split your code in such a way you can have just one class or module per file. RequireJS also includes an optimizer that will combine all your files into a single, minified file for use in production.
RequireJS uses the Asynchronous Module Definition (AMD) pattern for organizing your code. Dojo also uses such AMD to define it’s own modules, so you can require specific Dojo dependencies in your project, just like you require any of your own files. This is a huge win since I could keep my code organized in a consistent way.
You can read more about why RequireJS is awesome in my previous article.
When it came to choosing a datastore for the application, I wanted something that would be easy to work with from the front end. I narrowed it down to MongoDB and CouchDB. I actually built the first prototype with MongoDB, and ended up switching out to Couch.
It turns out all I really needed in a database was something that could store arbitrary documents, and retrieve them by ID. The querying functionality in MongoDB is very cool and useful, but I just didn’t need it in this project. I always had the ID of the document I needed, and only need to have proper queries in the admin tools (to a very small degree). This made CouchDB’s strengths outweigh MongoDB’s for this project, and that is why I made the switch.
CouchDB is queried over HTTP, which is great because I was able to effectively do the querying from the front-end code, and just use the server to proxy the query to CouchDB relatively untouched. The server does have some logic for security, but ultimately the front end code is directly querying the database.
One of the most important parts of the Code Challenge is code editor, since it’s really all about the code. A simple HTML textarea just won’t do. An editor should have good indentation support, code highlighting, and the keyboard shortcuts you would expect.
The Ace project provided a great code editor with pluggable themes, language support, and a good amount of extensibility. Overall I am pretty happy with Ace, but we might end up swapping it out for a similar component.
Since the application is so modular, it’s very easy to swap out components like Ace if an alternative has better support for a certain feature we may want.
I am extremely pleased with each of these tools, both how they work in isolation, but also in how they work together. There were several points in development when I couldn’t work on this code for several weeks at a time. Normally I am disoriented when coming back to code after such a break, but in this project it was so well organized that I was able to pick up fairly quickly from where I left off.
I was also able to do a full rewrite of the code about halfway through because I was able to simply write new modules, and switch them out one by one. This allowed me to rewrite some sloppy code with my new knowledge I had gained about my tools. This rewrite only took a few hours, and didn’t introduce any regressions in functionality.
Dojo turned out to be particularly awesome because the widgets are supported and tested thoroughly in most browsers. Everything pretty much “just worked” from one browser to another, and I didn’t spend much time at all on browser bugs.
The launch of Treehouse and Code Challenges was a complete success. The code challenges had only a few minor bugs pop up during launch, and most of these were small style bugs. The servers kept pace with no problems since it was easy to add more servers when traffic picked up.
This combination of tools allowed me to build both prototype and a real application very quickly. It has very few bugs, is easy to maintain, and ultimately is a joy to work with. I hope to share some specific tips and tricks I discovered with these tools, both here on Think Vitamin, and on Treehouse.