{"action":"created","comment":{"url":"https://api.github.com/repos/wework/wework.github.io/pulls/comments/46192917","id":46192917,"diff_hunk":"@@ -0,0 +1,114 @@\n+---\n+layout:       post\n+title:        Why WeWork.com uses a static generator and why you should too\n+author:       ramin_bozorgzadeh\n+summary:      Back when the web first started, things were a lot simpler. Most websites were made up of static html pages and there weren't a lot of moving parts. This is the story of how wework.com went from a monolithic \"web app\" to a statically generated site, and why ...\n+image:        //res.cloudinary.com/wework/image/upload/c_fill,f_auto,g_faces,h_800,w_1000/v1448734984/engineering/wework-com-is-going-static.jpg\n+categories:   engineering\n+---\n+\n+### What is this \"static\" business all about?\n+\n+Over the past few years, static site generators have become a hot topic, so much so that [entire sites](https://www.staticgen.com/) have emerged dedicated to tracking and rating them. Don't take my word\n+for it, check out this graph from [Google Trends for \"static site generator\"][2]:\n+\n+[![Google Trends - Static Site Generator][1]][2]\n+[1]: http://res.cloudinary.com/wework/image/upload/f_auto/v1448735896/engineering/static-site-generator.png\n+[2]: https://www.google.com/trends/explore#q=static%20site%20generator\n+\n+The idea is actually quite simple. On a traditional *dynamic* site, each page is processed and generated on the server per request (let's ignore caching strategies for the sake of argument.) A static site, on the other hand, is processed and generated just **once** on each deploy and all the server has to do is serve up the resulting generated HTML. Of course, this is an overly simplified explanation of how it all works, and this process doesn't work as well on all sites, but it does work really well for marketing and informational sites like [wework.com](https://www.wework.com). Not to mention that this very blog you are reading is a statically generated site using [Jekyll](http://jekyllrb.com/) and hosted on [Github pages](https://pages.github.com/)!\n+\n+If you consider the amount of information that changes on a daily, or even weekly basis on a site like wework.com, it is actually quite wasteful to have a server process each and every request that comes through. One of the main reasons we even have a server-side component for wework.com is to allow us to easily add, update and manage information on the site.\n+\n+\n+### Roots: The static / dynamic hybrid approach\n+\n+One key requirement for us when evaluating different static site generators was the ability to hit an API endpoint and dynamically generate static pages based on the data returned. A real-world example of this is when we add new locations and markets. With a traditional web-app setup (like a Ruby on Rails project), as soon as you add a new location or update one, the new content is up and live on your website. With a statically generated site, this is not the case. You will need to rebuild the entire site and generate new HTML. The good news is, for a site that doesn't have a ton of content, this static site generation is actually really fast. Like in the tens of seconds fast. A couple of minutes if you consider the entire proecss of minifying assets, building and deploying your site.\n+\n+We played with and evaluated quite a few options, like pure Jekyll, Yeoman, Middleman, etc. The one we decided to go with is a very well thought out static site generator called [**Roots**](http://roots.cx/), developed by the fine folks at the Brooklyn based agency, [Carrot Creative](https://carrot.is/). Besides the many nice features that Roots gives you right out of the box (folder structure, asset management and minifcation, great extension and plugin system, etc), they also have a really nifty extension called [**roots-records**](https://github.com/carrot/roots-records). Like most well built things, roots-records serves one purpose and does an amazing job at it. It allows you to hit ANY endpoint that returns a JSON collection and use that collection in your templates to iterate over or if you pass in a template, it will also generate individual static HTML pages for each item in the collection. For example, here is all we had to define in our `app.coffee` file to hit our API endpoint for our market/location:\n+\n+```coffeescript\n+extensions: [\n+  records(\n+    marketsByCountry: {\n+      url:\n+        path: \"#{process.env.API_ENDPOINT}/api/to/locations/data\"\n+    }\n+  )\n+]\n+```\n+\n+And here is how this data is referenced inside of our `jade` templates:\n+\n+```jade\n+each countries, index in records.marketsByCountry\n+  .row\n+    .col-12.col\n+      h4\n+        != countries[0]\n+```\n+\n+Once we are ready to move our individual location pages over to *static*, all we need to do is pass a `template` option to the `records` block in our app.coffee config, and Roots will generate the individual location pages when it builds the site. This would look something like this:\n+\n+```coffeescript\n+extensions: [\n+  records(\n+    marketsByCountry: {\n+      url:\n+        path: \"#{process.env.API_ENDPOINT}/api/to/locations/data\"\n+      template: \"views/_location.jade\"\n+      out: (location) -> \"/locations/#{location.market.slug}/#{location.slug}\"\n+    }\n+  )\n+]\n+```\n+\n+\n+### Netlify: The static host with the most\n+\n+One of the biggest challenges of this whole process has been to figure out a way to slowly migrate our site over from a dynamic app, over to a static one. One way to do this would be to stop all development for a few months and rebuild the entire site using this new infrastructure. But as the saying goes, \"ain't nobody got time for that.\"\n+\n+We needed a way to move things over piecemeal. One page at a time. This meant that we couldn't move wework.com to a new static host all at once, but at the same time, we needed some URL's to serve up the old content, and some URL's to serve up the new static pages. One way to do this is via a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy).\n+\n+There are many different ways to skin this cat, ranging from installing and configuring your own Apache or Ngnix server, using something like rack-reverse-proxy to a slew of other similar solutions, each with their own pros and cons. For us, being a team of web developers, we wanted to spend our time focusing on our KPI's and optimizing our pages for performance, and not on setting up and managing servers. We had a few requirements. Specifically, we were looking for a host that could:\n+\n+- House our new static site and scale with us\n+- Have an API and webhooks so we can trigger builds when content changes\n+- Ability to proxy requests to other URL's (internal and external)\n+- Global CDN to improve our international traffic and SEO\n+- Easy to use and configure\n+- Great and responsive customer support\n+\n+We found one host that met all of those requirements and more, and they are called [Netlify](https://www.netlify.com/). Never heard of them? Neither had we, but as a colleague of mine recently put it, \"Netlify is like the developer whisperer\". This team has put together an amazing service that handles SO much for you from a dev-ops persective of hosting static sites. And if there is a feature that is missing, they will bend over backwards to either implement it for you, or help you figure out a solution. I can sit here and sing their praises all day long, but its probably best if I explained a bit about how they were able to help us with our migration to a static site and make the static verions of our site *2-3x faster*.\n+\n+To get started, one of the first pages we decided to migrate over to static was our [`/locations`](https://www.wework.com/locations/) page. This page doesn't get a ton of traffic, but has dynamic content coming from our backend, so it seemed like a good place to start. Looking at the graph below you can see the downward trend (in the world of performance, downward trending graphs are usually a good thing) from the day we rolled it out.","path":"_drafts/wework-com-is-going-static.markdown","position":84,"original_position":84,"commit_id":"41fe3f46fcd5701f845f8901800dd23ee5a3548e","original_commit_id":"41fe3f46fcd5701f845f8901800dd23ee5a3548e","user":{"login":"OnFreund","id":2159693,"avatar_url":"https://avatars.githubusercontent.com/u/2159693?v=3","gravatar_id":"","url":"https://api.github.com/users/OnFreund","html_url":"https://github.com/OnFreund","followers_url":"https://api.github.com/users/OnFreund/followers","following_url":"https://api.github.com/users/OnFreund/following{/other_user}","gists_url":"https://api.github.com/users/OnFreund/gists{/gist_id}","starred_url":"https://api.github.com/users/OnFreund/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OnFreund/subscriptions","organizations_url":"https://api.github.com/users/OnFreund/orgs","repos_url":"https://api.github.com/users/OnFreund/repos","events_url":"https://api.github.com/users/OnFreund/events{/privacy}","received_events_url":"https://api.github.com/users/OnFreund/received_events","type":"User","site_admin":false},"body":"I would remove \"doesn't get a ton of traffic\"","created_at":"2015-11-30T19:46:15Z","updated_at":"2015-11-30T19:46:15Z","html_url":"https://github.com/wework/wework.github.io/pull/11#discussion_r46192917","pull_request_url":"https://api.github.com/repos/wework/wework.github.io/pulls/11","_links":{"self":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/comments/46192917"},"html":{"href":"https://github.com/wework/wework.github.io/pull/11#discussion_r46192917"},"pull_request":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/11"}}},"pull_request":{"url":"https://api.github.com/repos/wework/wework.github.io/pulls/11","id":52048402,"html_url":"https://github.com/wework/wework.github.io/pull/11","diff_url":"https://github.com/wework/wework.github.io/pull/11.diff","patch_url":"https://github.com/wework/wework.github.io/pull/11.patch","issue_url":"https://api.github.com/repos/wework/wework.github.io/issues/11","number":11,"state":"open","locked":false,"title":"blogging about static wework","user":{"login":"i8ramin","id":49030,"avatar_url":"https://avatars.githubusercontent.com/u/49030?v=3","gravatar_id":"","url":"https://api.github.com/users/i8ramin","html_url":"https://github.com/i8ramin","followers_url":"https://api.github.com/users/i8ramin/followers","following_url":"https://api.github.com/users/i8ramin/following{/other_user}","gists_url":"https://api.github.com/users/i8ramin/gists{/gist_id}","starred_url":"https://api.github.com/users/i8ramin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/i8ramin/subscriptions","organizations_url":"https://api.github.com/users/i8ramin/orgs","repos_url":"https://api.github.com/users/i8ramin/repos","events_url":"https://api.github.com/users/i8ramin/events{/privacy}","received_events_url":"https://api.github.com/users/i8ramin/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-11-28T21:05:31Z","updated_at":"2015-11-30T19:46:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7902f2af2b27810cb23fcd406f81daa8806bfe71","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/wework/wework.github.io/pulls/11/commits","review_comments_url":"https://api.github.com/repos/wework/wework.github.io/pulls/11/comments","review_comment_url":"https://api.github.com/repos/wework/wework.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/wework/wework.github.io/issues/11/comments","statuses_url":"https://api.github.com/repos/wework/wework.github.io/statuses/41fe3f46fcd5701f845f8901800dd23ee5a3548e","head":{"label":"wework:feature/wework-com-static","ref":"feature/wework-com-static","sha":"41fe3f46fcd5701f845f8901800dd23ee5a3548e","user":{"login":"wework","id":4072897,"avatar_url":"https://avatars.githubusercontent.com/u/4072897?v=3","gravatar_id":"","url":"https://api.github.com/users/wework","html_url":"https://github.com/wework","followers_url":"https://api.github.com/users/wework/followers","following_url":"https://api.github.com/users/wework/following{/other_user}","gists_url":"https://api.github.com/users/wework/gists{/gist_id}","starred_url":"https://api.github.com/users/wework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wework/subscriptions","organizations_url":"https://api.github.com/users/wework/orgs","repos_url":"https://api.github.com/users/wework/repos","events_url":"https://api.github.com/users/wework/events{/privacy}","received_events_url":"https://api.github.com/users/wework/received_events","type":"Organization","site_admin":false},"repo":{"id":32547526,"name":"wework.github.io","full_name":"wework/wework.github.io","owner":{"login":"wework","id":4072897,"avatar_url":"https://avatars.githubusercontent.com/u/4072897?v=3","gravatar_id":"","url":"https://api.github.com/users/wework","html_url":"https://github.com/wework","followers_url":"https://api.github.com/users/wework/followers","following_url":"https://api.github.com/users/wework/following{/other_user}","gists_url":"https://api.github.com/users/wework/gists{/gist_id}","starred_url":"https://api.github.com/users/wework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wework/subscriptions","organizations_url":"https://api.github.com/users/wework/orgs","repos_url":"https://api.github.com/users/wework/repos","events_url":"https://api.github.com/users/wework/events{/privacy}","received_events_url":"https://api.github.com/users/wework/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/wework/wework.github.io","description":"WeWork Engineering Blog","fork":false,"url":"https://api.github.com/repos/wework/wework.github.io","forks_url":"https://api.github.com/repos/wework/wework.github.io/forks","keys_url":"https://api.github.com/repos/wework/wework.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wework/wework.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wework/wework.github.io/teams","hooks_url":"https://api.github.com/repos/wework/wework.github.io/hooks","issue_events_url":"https://api.github.com/repos/wework/wework.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/wework/wework.github.io/events","assignees_url":"https://api.github.com/repos/wework/wework.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/wework/wework.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/wework/wework.github.io/tags","blobs_url":"https://api.github.com/repos/wework/wework.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wework/wework.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wework/wework.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/wework/wework.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wework/wework.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/wework/wework.github.io/languages","stargazers_url":"https://api.github.com/repos/wework/wework.github.io/stargazers","contributors_url":"https://api.github.com/repos/wework/wework.github.io/contributors","subscribers_url":"https://api.github.com/repos/wework/wework.github.io/subscribers","subscription_url":"https://api.github.com/repos/wework/wework.github.io/subscription","commits_url":"https://api.github.com/repos/wework/wework.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/wework/wework.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/wework/wework.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/wework/wework.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/wework/wework.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/wework/wework.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wework/wework.github.io/merges","archive_url":"https://api.github.com/repos/wework/wework.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wework/wework.github.io/downloads","issues_url":"https://api.github.com/repos/wework/wework.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/wework/wework.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/wework/wework.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/wework/wework.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wework/wework.github.io/labels{/name}","releases_url":"https://api.github.com/repos/wework/wework.github.io/releases{/id}","created_at":"2015-03-19T21:36:55Z","updated_at":"2015-10-06T03:18:09Z","pushed_at":"2015-11-30T19:07:11Z","git_url":"git://github.com/wework/wework.github.io.git","ssh_url":"git@github.com:wework/wework.github.io.git","clone_url":"https://github.com/wework/wework.github.io.git","svn_url":"https://github.com/wework/wework.github.io","homepage":null,"size":4140,"stargazers_count":3,"watchers_count":3,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2,"mirror_url":null,"open_issues_count":4,"forks":2,"open_issues":4,"watchers":3,"default_branch":"master"}},"base":{"label":"wework:master","ref":"master","sha":"af2826212cd6fef7707347f8597cb0cb9e90b7f4","user":{"login":"wework","id":4072897,"avatar_url":"https://avatars.githubusercontent.com/u/4072897?v=3","gravatar_id":"","url":"https://api.github.com/users/wework","html_url":"https://github.com/wework","followers_url":"https://api.github.com/users/wework/followers","following_url":"https://api.github.com/users/wework/following{/other_user}","gists_url":"https://api.github.com/users/wework/gists{/gist_id}","starred_url":"https://api.github.com/users/wework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wework/subscriptions","organizations_url":"https://api.github.com/users/wework/orgs","repos_url":"https://api.github.com/users/wework/repos","events_url":"https://api.github.com/users/wework/events{/privacy}","received_events_url":"https://api.github.com/users/wework/received_events","type":"Organization","site_admin":false},"repo":{"id":32547526,"name":"wework.github.io","full_name":"wework/wework.github.io","owner":{"login":"wework","id":4072897,"avatar_url":"https://avatars.githubusercontent.com/u/4072897?v=3","gravatar_id":"","url":"https://api.github.com/users/wework","html_url":"https://github.com/wework","followers_url":"https://api.github.com/users/wework/followers","following_url":"https://api.github.com/users/wework/following{/other_user}","gists_url":"https://api.github.com/users/wework/gists{/gist_id}","starred_url":"https://api.github.com/users/wework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wework/subscriptions","organizations_url":"https://api.github.com/users/wework/orgs","repos_url":"https://api.github.com/users/wework/repos","events_url":"https://api.github.com/users/wework/events{/privacy}","received_events_url":"https://api.github.com/users/wework/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/wework/wework.github.io","description":"WeWork Engineering Blog","fork":false,"url":"https://api.github.com/repos/wework/wework.github.io","forks_url":"https://api.github.com/repos/wework/wework.github.io/forks","keys_url":"https://api.github.com/repos/wework/wework.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wework/wework.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wework/wework.github.io/teams","hooks_url":"https://api.github.com/repos/wework/wework.github.io/hooks","issue_events_url":"https://api.github.com/repos/wework/wework.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/wework/wework.github.io/events","assignees_url":"https://api.github.com/repos/wework/wework.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/wework/wework.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/wework/wework.github.io/tags","blobs_url":"https://api.github.com/repos/wework/wework.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wework/wework.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wework/wework.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/wework/wework.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wework/wework.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/wework/wework.github.io/languages","stargazers_url":"https://api.github.com/repos/wework/wework.github.io/stargazers","contributors_url":"https://api.github.com/repos/wework/wework.github.io/contributors","subscribers_url":"https://api.github.com/repos/wework/wework.github.io/subscribers","subscription_url":"https://api.github.com/repos/wework/wework.github.io/subscription","commits_url":"https://api.github.com/repos/wework/wework.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/wework/wework.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/wework/wework.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/wework/wework.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/wework/wework.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/wework/wework.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wework/wework.github.io/merges","archive_url":"https://api.github.com/repos/wework/wework.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wework/wework.github.io/downloads","issues_url":"https://api.github.com/repos/wework/wework.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/wework/wework.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/wework/wework.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/wework/wework.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wework/wework.github.io/labels{/name}","releases_url":"https://api.github.com/repos/wework/wework.github.io/releases{/id}","created_at":"2015-03-19T21:36:55Z","updated_at":"2015-10-06T03:18:09Z","pushed_at":"2015-11-30T19:07:11Z","git_url":"git://github.com/wework/wework.github.io.git","ssh_url":"git@github.com:wework/wework.github.io.git","clone_url":"https://github.com/wework/wework.github.io.git","svn_url":"https://github.com/wework/wework.github.io","homepage":null,"size":4140,"stargazers_count":3,"watchers_count":3,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2,"mirror_url":null,"open_issues_count":4,"forks":2,"open_issues":4,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/11"},"html":{"href":"https://github.com/wework/wework.github.io/pull/11"},"issue":{"href":"https://api.github.com/repos/wework/wework.github.io/issues/11"},"comments":{"href":"https://api.github.com/repos/wework/wework.github.io/issues/11/comments"},"review_comments":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/11/comments"},"review_comment":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/wework/wework.github.io/pulls/11/commits"},"statuses":{"href":"https://api.github.com/repos/wework/wework.github.io/statuses/41fe3f46fcd5701f845f8901800dd23ee5a3548e"}}}}