Building markdown-based developer docs
Retooling the Sauce Labs documentation
Recently at Sauce Labs we decided to retool our documentation system. This decision came after accumulating docs in a number of template systems and repos which were difficult to standardize and maintain. The result of this effort was a new markdown-based docs site available at docs.saucelabs.com.
Using GitHub as a Markdown CMS
We began by converting all the existing documentation to GitHub flavored markdown so that developers and non-developers alike would be comfortable editing content. Markdown is optimized for English prose but also allows for syntax-highlighted code blocks and arbitrary HTML, so it was perfectly suited to be the format for our technical documentation.
Next, we put all of the markdown docs in a dedicated open-source GitHub repo so that we could publicly discuss changes and let Sauce Labs employees as well as community members make improvements to the content. Using GitHub to manage our docs, we took advantage of a few helpful features for editing and reviewing markdown:
- Rendered prose diffs for a fast review process.
- The ability to open files directly in edit mode to let people contribute quickly. Here is an example link if you have a GitHub account.
- Zen writing mode for a less cluttered editing interface.
Building the Docs Website
Our main grunt plugin used to generate the website was grunt-pages (to see grunt-pages in action, you can install a Cabin static blogging theme and check out the config in the Gruntfile.js). Using grunt-pages along with a number of plugins from the rich Grunt ecosystem, we were able to encapsulate all the logic to build the site in a single place and focus our efforts on the content and presentation.
Testing the Website
To help us confidently deploy changes, we used grunt-link-checker to detect broken links and fragment identifiers. When deploying to production we first verified that a locally hosted version of the website had no broken relative links, deployed, then verified that the deployed version also had no broken links. Doing this allowed us to verify that all internal linking worked properly and that nothing went wrong during the deploy. You can check out an example config for this pre-deploy/post-deploy workflow here and see an example of how grunt-link-checker failed a build when a fragment identifier was broken here.
Being Sauce Labs, of course we also had to write some Selenium tests to simulate user interactions in different browsers and operating systems. To do this we used the grunt-mocha-webdriver plugin for a productive local workflow and robust testing in CI.
To continuously integrate new content and code we used Travis CI. Every commit to our website’s master branch which passed the build deployed the site to production while commits to other branches were deployed to a staging environment. One of the nice things about our CI was that changes to non-master branches were deployed immediately to staging and then tested while changes to master were tested before being deployed. This let us keep master stable while quickly deploying and testing other branches on staging.
We also added free AppVeyor CI testing to our saucelabs/docs public content repo so that we made sure our tests ran properly on Windows.
I highly recommend checking it out if you have open source projects with Windows users, and you can see an example AppVeyor config file here.
Hosting & Deployment
One of the benefits of a static site is the simplicity of hosting and deployment. We decided to host the Sauce docs on Amazon S3 fronted by Amazon’s Cloudfront CDN to have a fast and simple way to serve generated assets. If you are interested in hosting on S3, you can set it up to serve static websites with a bit of configuration.
There are a number of Grunt plugins we used to help us with deployment. I recommend grunt-aws-s3 for S3 since it is based on Amazon’s official node.js SDK and grunt-filerev with grunt-usemin to revision files for caching purposes on Cloudfront.
The new Sauce Labs docs system has allowed us to iterate quickly, ease internal and community contribution, and improve performance. I hope that shedding some light on the system will help others who are seeking to achieve similar goals. Thanks for reading and please feel free to check out the docs on GitHub!