A few weeks ago we rolled out major changes to the underlying structure of Prose. We’ve now tested and fixed issues over the last couple of weeks and we’re excited to annouce that Prose has been refactored with established Backbone.js conventions.

This is exciting news that follows up on the announcements of new features and improved media support back in May. In June and July, Tristen and I embarked on a refactor of the underlying structure of Prose with the goal of building a more robust application and we’ve now completed this work.

This work builds on the mission of Prose to be a viable alternative to traditional content management systems, replacing heavy backend infrastructure and databases with text files, leveraging the power of git for version control and relying on Jekyll for quick static site generation. In addition to concrete performance gains, the revised model layer unlocks the potential for more complex functionality that could be implemented in the future, such as comparing diffs of previous versions of a file.

Community

With dozens of contributions and over 10,000 users, Prose is supported by a huge and growing community. By embracing standard Backbone.js conventions, this refactor opens up Prose to even more contributions. Developers who have worked on other Backbone.js applications should feel comfortable reading through Prose’s codebase and contributing. By lowering the barrier to community contributions, we hope to empower the users of Prose to play a greater role in the continuing development of this project.

History

Originally, Prose only used views and a router from Backbone.js, communicating with the GitHub API through a handful of functions that wrapped methods from github.js. However, the GitHub API is essentially a CRUD backend and, with a few minor tweaks, Backbone.js offers great support for syncing models between the client and server.

Structure

The first task was sketching out the relationships between objects accessible through the GitHub API, and determining how to break up the current large views into a nested structure of subviews, each with its own discrete functionality.

We built Backbone.js models and collections to mirror the API objects, and gradually broke functionality out of models.js and reimplemented it in the appropriate models. We then broke the large views into smaller pieces, each responsible for presenting a limited amount of information and only handling interaction in its immeadiate context. Throughout this process, we emphasized a cleaner separation of concerns, such that models would only be responsible for managing data, while views would only be responsible for presentation and handling UI interaction. This enabled us to use functionality like Backbone’s model.save and model.isNew rather than parsing content or inferring state from the DOM.

Patterns

We used a dependency injection pattern to provide references to model dependencies and related UI components to subviews. This helped ensure that views would not render prematurely or re-render themselves unnecessarily (both of which happened when rendering on change or sync events).

To avoid memory leaks caused by failing to unbind event handlers, we used Backbone’s listenTo method and an object on each view to keep track of all child views. Then, we extended Backbone’s view.remove method to iterate over all child views and invoke their respective remove methods.

Templates

We refactored the templates in Prose to not depend on global state, and then defined single variable names for each template to avoid the with statement, significantly improving rendering speed. We also used DocumentFragment to improve performance in views responsible for rendering many subviews.

Internationalization

During this refactor, there was an emphasis on making Prose accessible to users around the world. Tristen internationalized the UI using the Transifex platform, which has already attracted translations in German, French, Brazilian Portugese, Romanian, Russian, Vietnamese and Chinese. There are future plans to enable a non-English default language for multilingual sites.

Improvements

  • Rendering several subviews individually rather than waiting for a single large view to render reduces perceived page load time.
  • Switching from a custom XHR implementation to $.ajax seamlessly added support for conditional requests and delegated _config.yml caching and invalidation to jQuery rather than handling this manually through localStorage
  • Repo view navigation now filters and re-renders the Files subview, rather than requiring a full page reload, significantly speeding up navigation.

Prose Repo Navigation

Next

Start using Prose now at prose.io, or check out the issue queue if you’re interested in contributing!

We're a team of engineers and designers working on big projects. We believe in open source and in building for lasting impact.

Learn more