Skip to content
This repository was archived by the owner on May 4, 2019. It is now read-only.

Commit a1f630b

Browse files
committed
blogging about static wework
1 parent af28262 commit a1f630b

File tree

11 files changed

+246
-72
lines changed

11 files changed

+246
-72
lines changed

Gemfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
source 'https://rubygems.org'
22

3+
gem 'jekyll'
4+
gem 'jekyll-paginate'
5+
gem 'jekyll-watch'
6+
37
gem 'github-pages'
48
gem 'sass'
9+
gem 'octopress-autoprefixer'
10+
gem 'redcarpet'
11+

Gemfile.lock

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,53 @@ GEM
22
remote: https://rubygems.org/
33
specs:
44
RedCloth (4.2.9)
5-
activesupport (4.2.1)
5+
activesupport (4.2.5)
66
i18n (~> 0.7)
77
json (~> 1.7, >= 1.7.7)
88
minitest (~> 5.1)
99
thread_safe (~> 0.3, >= 0.3.4)
1010
tzinfo (~> 1.1)
11+
addressable (2.3.8)
12+
autoprefixer-rails (2.2.0.20140804)
13+
execjs
1114
blankslate (2.1.2.4)
12-
celluloid (0.16.0)
13-
timers (~> 4.0.0)
14-
classifier-reborn (2.0.3)
15+
classifier-reborn (2.0.4)
1516
fast-stemmer (~> 1.0)
16-
coffee-script (2.3.0)
17+
coffee-script (2.4.1)
1718
coffee-script-source
1819
execjs
19-
coffee-script-source (1.9.1)
20+
coffee-script-source (1.10.0)
2021
colorator (0.1)
21-
execjs (2.4.0)
22+
ethon (0.8.0)
23+
ffi (>= 1.3.0)
24+
execjs (2.6.0)
2225
fast-stemmer (1.0.2)
23-
ffi (1.9.8)
26+
ffi (1.9.10)
2427
gemoji (2.1.0)
25-
github-pages (33)
28+
github-pages (39)
2629
RedCloth (= 4.2.9)
2730
github-pages-health-check (~> 0.2)
2831
jekyll (= 2.4.0)
2932
jekyll-coffeescript (= 1.0.1)
33+
jekyll-feed (= 0.3.1)
3034
jekyll-mentions (= 0.2.1)
31-
jekyll-redirect-from (= 0.6.2)
32-
jekyll-sass-converter (= 1.2.0)
33-
jekyll-sitemap (= 0.6.3)
34-
jemoji (= 0.4.0)
35+
jekyll-redirect-from (= 0.8.0)
36+
jekyll-sass-converter (= 1.3.0)
37+
jekyll-sitemap (= 0.8.1)
38+
jemoji (= 0.5.0)
3539
kramdown (= 1.5.0)
36-
liquid (= 2.6.1)
40+
liquid (= 2.6.2)
3741
maruku (= 0.7.0)
3842
mercenary (~> 0.3)
39-
pygments.rb (= 0.6.1)
43+
pygments.rb (= 0.6.3)
4044
rdiscount (= 2.1.7)
41-
redcarpet (= 3.1.2)
45+
redcarpet (= 3.3.2)
4246
terminal-table (~> 1.4)
43-
github-pages-health-check (0.2.2)
44-
net-dns (~> 0.6)
47+
github-pages-health-check (0.5.3)
48+
addressable (~> 2.3)
49+
net-dns (~> 0.8)
4550
public_suffix (~> 1.4)
46-
hitimes (1.2.2)
51+
typhoeus (~> 0.7)
4752
html-pipeline (1.9.0)
4853
activesupport (>= 2)
4954
nokogiri (~> 1.4)
@@ -65,56 +70,62 @@ GEM
6570
toml (~> 0.1.0)
6671
jekyll-coffeescript (1.0.1)
6772
coffee-script (~> 2.2)
68-
jekyll-gist (1.1.0)
73+
jekyll-feed (0.3.1)
74+
jekyll-gist (1.3.5)
6975
jekyll-mentions (0.2.1)
7076
html-pipeline (~> 1.9.0)
7177
jekyll (~> 2.0)
7278
jekyll-paginate (1.1.0)
73-
jekyll-redirect-from (0.6.2)
74-
jekyll (~> 2.0)
75-
jekyll-sass-converter (1.2.0)
79+
jekyll-redirect-from (0.8.0)
80+
jekyll (>= 2.0)
81+
jekyll-sass-converter (1.3.0)
7682
sass (~> 3.2)
77-
jekyll-sitemap (0.6.3)
78-
jekyll-watch (1.2.1)
79-
listen (~> 2.7)
80-
jemoji (0.4.0)
83+
jekyll-sitemap (0.8.1)
84+
jekyll-watch (1.3.0)
85+
listen (~> 3.0)
86+
jemoji (0.5.0)
8187
gemoji (~> 2.0)
8288
html-pipeline (~> 1.9)
83-
jekyll (~> 2.0)
84-
json (1.8.2)
89+
jekyll (>= 2.0)
90+
json (1.8.3)
8591
kramdown (1.5.0)
86-
liquid (2.6.1)
87-
listen (2.9.0)
88-
celluloid (>= 0.15.2)
92+
liquid (2.6.2)
93+
listen (3.0.5)
8994
rb-fsevent (>= 0.9.3)
9095
rb-inotify (>= 0.9)
9196
maruku (0.7.0)
9297
mercenary (0.3.5)
9398
mini_portile (0.6.2)
94-
minitest (5.5.1)
99+
minitest (5.8.3)
95100
net-dns (0.8.0)
96-
nokogiri (1.6.6.2)
101+
nokogiri (1.6.6.4)
97102
mini_portile (~> 0.6.0)
103+
octopress-autoprefixer (1.0.1)
104+
autoprefixer-rails (~> 2.2)
105+
jekyll (>= 2.0)
106+
octopress-hooks (~> 2.0)
107+
octopress-hooks (2.6.1)
108+
jekyll (>= 2.0)
98109
parslet (1.5.0)
99110
blankslate (~> 2.0)
100-
posix-spawn (0.3.10)
101-
public_suffix (1.4.6)
102-
pygments.rb (0.6.1)
111+
posix-spawn (0.3.11)
112+
public_suffix (1.5.2)
113+
pygments.rb (0.6.3)
103114
posix-spawn (~> 0.3.6)
104115
yajl-ruby (~> 1.2.0)
105-
rb-fsevent (0.9.4)
116+
rb-fsevent (0.9.6)
106117
rb-inotify (0.9.5)
107118
ffi (>= 0.5.0)
108119
rdiscount (2.1.7)
109-
redcarpet (3.1.2)
120+
redcarpet (3.3.2)
110121
safe_yaml (1.0.4)
111-
sass (3.4.13)
112-
terminal-table (1.4.5)
122+
sass (3.4.19)
123+
terminal-table (1.5.2)
113124
thread_safe (0.3.5)
114-
timers (4.0.1)
115-
hitimes
116125
toml (0.1.2)
117126
parslet (~> 1.5.0)
127+
typhoeus (0.8.0)
128+
ethon (>= 0.8.0)
118129
tzinfo (1.2.2)
119130
thread_safe (~> 0.1)
120131
yajl-ruby (1.2.1)
@@ -124,4 +135,12 @@ PLATFORMS
124135

125136
DEPENDENCIES
126137
github-pages
138+
jekyll
139+
jekyll-paginate
140+
jekyll-watch
141+
octopress-autoprefixer
142+
redcarpet
127143
sass
144+
145+
BUNDLED WITH
146+
1.10.6

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ $ jekyll -v
3333
Then, start the Jekyll Server. I always like to give the `--watch` option so it updates the generated HTML when I make changes.
3434

3535
```
36-
$ jekyll serve --watch --drafts
36+
$ bundle exec jekyll serve --watch --drafts
3737
```
3838

3939
Now you can navigate to `localhost:4000` in your browser to see the site.

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ description: ""
66
baseurl: ""
77
url: "http://engineering.wework.com"
88

9+
gems: [jekyll-paginate, jekyll-watch, redcarpet, octopress-autoprefixer]
910

1011
# Permalinks
1112
permalink: pretty
12-
relative_permalinks: true
1313

1414
# Syntax highlighting
1515
highlighter: pygments

_data/authors.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ramin_bozorgzadeh:
2+
name: Ramin Bozorgzadeh
3+
gravatar_email: [email protected]
4+
github: i8ramin
5+
summary: Engineering Director at WeWork and loving all things performance, front-end and design related.

_drafts/.gitkeep

Whitespace-only changes.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
layout: post
3+
title: WeWork.com is Going Static
4+
author: ramin_bozorgzadeh
5+
summary: Back when the web first started, things were a lot more simple. Most websites were made up of static html pages and not a lot of moving parts. A lot has changed since then, but ever present desire to "keep it simple" is still there. This is the story of how wework.com went from a complicated "web app" to a basic statically generated site and why ...
6+
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
7+
categories: engineering
8+
---
9+
10+
### What is this "static" business all about?
11+
12+
Static site generators have become a hot topic in the past few years. There are [whole sites](https://www.staticgen.com/) dedicated to tracking and rating them. Don't take my word
13+
for it, check out this graph from [Google Trends on "static site generator"][2]:
14+
15+
[![Google Trends - Static Site Generator][1]][2]
16+
[1]: //res.cloudinary.com/wework/image/upload/f_auto/v1448735896/engineering/static-site-generator.png
17+
[2]: https://www.google.com/trends/explore#q=static%20site%20generator
18+
19+
The idea is actually quite simple. Whereas on a traditional *dynamic* site, each page is proceed and generated on the server per request (let's ignore caching strategies for the sake of argument), a static site 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 explantion of how it all works, and this process doesn't work on all web sites, but it does work really well for marketing and informational sites like wework.com. This very blog you are reading is actually a statically generated site using [Jekyll](http://jekyllrb.com/) and hosted on [Github pages](https://pages.github.com/)!
20+
21+
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 very 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 is to allow us to easily add new information to the site, manage the content that is being served up and build administrative dashboards that allow a non-developer to update this information. The great news is, we can still achieve all of this with an API and take the load off of our application server for serving up these mostly static HTML pages.
22+
23+
24+
### The static / dynamic hybrid approach
25+
26+
One key requirement for us when evaluating different static site generators was the ability to hit an API endpoint for data, and dynamically generate static pages based on this data. 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 the 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 and deploying of the site.
27+
28+
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, 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 endpoing for our market/location:
29+
30+
```coffeescript
31+
extensions: [
32+
records(
33+
marketsByCountry: {
34+
url:
35+
path: "#{process.env.DUBS_API}/api/v1/locations/markets_by_country"
36+
headers: { Authorization: "Token token=#{process.env.DUBS_API_TOKEN}" }
37+
}
38+
)
39+
]
40+
```
41+
42+
And here is how this data is referenced inside of our `jade` templates:
43+
44+
```jade
45+
each countries, index in records.marketsByCountry
46+
.row
47+
.col-12.col
48+
h4
49+
!= countries[0]
50+
```
51+
52+
If you are interested to learn more about how it all works and a real-world example, [check out their tutorial here](http://roots.cx/articles/hybrid-static).
53+
54+
55+
### The static host with the most
56+
57+
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 all of our pages one by one over to this new process. But as the saying goes, "aint nobody got time for that!"
58+
59+
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 need 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).
60+
61+
Again, there are many different ways to skin this cat. You can install and configure your own Apache or Ngnix server. You can use something like [`rack-reverse-proxy`](https://github.com/jaswope/rack-reverse-proxy) or a slew of other similar solutions in different languages. There are pros and cons to each approach. For us, being a team of web developers, we wanted to spend our time focusing on our KPI's and optimizing our pages for best performance, and not on setting up and managing servers. We had a few requirements:
62+
63+
- Host our new static site and scale with our fast growing company
64+
- Have an API so we can trigger builds when data changes
65+
- Ability to proxy requests to other URL's
66+
- Global CDN to improve our international traffic and SEO
67+
- Easy to use and configure
68+
- Great and responsive customer support
69+
70+
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 one engineer I recently spoke with who was familiar with their services put it, "Netlify is like the web 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 *4-5x faster*.
71+
72+
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. We no longer have the old version up now, so I can't do a side by side comparison, but just looking at the graph below you can see that our server response time is **1ms** (this number is around 150-200ms on other pages), and the page is visually complete in about 2.8 seconds, which is 2-3x faster than it used to be. Of course, we've done other optimizations as well (reducing unused assets, optimizing images, etc) and there are still other things we can do to make it even more performant, but we are very pleased with the results so far.
73+
74+
![Performance graph of locations page](http://res.cloudinary.com/wework/image/upload/c_scale,f_auto,w_1000/v1448740493/engineering/locations-graph.png)
75+
76+
So how are we serving up traffic to `wework.com/locations/` from the static host and the other pages from our current host? It was as simple has modiftying some DNS settings to route all traffic through Netlify, [creating a `_redirects` file](https://www.netlify.com/docs/redirects) at the root of our very basic static site and configuring Netlify to compile and deploy our static site anytime it detects changes in our `master` branch.
77+
78+
The reality is that we are currently maintaining two versions of our site as we move things over, but this allows us to continue doing business as usual, make updates to existing pages and not have to stop our normal workflow as we migrate things over little by little. By doing this, we hope to see an improvement in direct and SEO traffic and hopefully a small uptick in our KPI numbers, as it has been proven that conversion numbers generally improve when your pages load faster and people are able to get to the information they are looking for quicker. This also allows us to expand globally without having to worry as much about traffic load and performance. One thing to keep in mind with all of this is that it is not necessarily *easier*, but it is a lot *simpler*.
79+
80+
In a future post we will discuss how we've started to also generate our React components as part of this static build process and how by doing so, we've seen a huge improvement in overall load times on pages that rely heavily on React.
81+
82+
83+
84+
85+
86+

_layouts/post.html

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
layout: default
33
---
44

5+
{% assign author = site.data.authors[page.author] %}
6+
57
{% assign minutes = content | number_of_words | divided_by: 180 %}
68
{% if minutes == 0 %}
7-
{% assign minutes = 1 %}
9+
{% assign minutes = 1 %}
810
{% endif %}
911

1012
<div class="post-header mb2">
@@ -14,8 +16,8 @@
1416
<h1>{{ page.title }}</h1>
1517
<div class="post-meta">
1618
<span class="post-date">{{ page.date | date: "%b %-d, %Y" }}</span>
17-
{% if page.author %}
18-
- <span class="post-author">{{page.author}}</span>
19+
{% if author %}
20+
- <span class="post-author">{{author.name}}</span>
1921
{% endif %}
2022
{% if page.update_date %}
2123
<div class="post-updated">Updated: {{ page.update_date | date: "%b %-d, %Y" }}</div>
@@ -26,6 +28,25 @@ <h1>{{ page.title }}</h1>
2628

2729
<article class="post-content">
2830
{{ content }}
31+
32+
{% if author %}
33+
<div class="author-block">
34+
{% if author.gravatar_email %}
35+
<div class="gravatar">
36+
<img src="{{ author.gravatar_email | gravatar }}">
37+
</div>
38+
{% endif %}
39+
<div class="author-info">
40+
<div class="author-name">Written by <strong>{{ author.name }}</strong></div>
41+
{% if author.summary %}
42+
<div class="author-summary">
43+
{{author.summary}}
44+
</div>
45+
{% endif %}
46+
</div>
47+
</div>
48+
{% endif %}
49+
2950
</article>
3051

3152
{% if site.show_sharing_icons %}

_plugins/Gravatar.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
require 'digest/md5'
2+
3+
module Jekyll
4+
module GravatarFilter
5+
6+
# Add our new liquid filter.
7+
def gravatar(input)
8+
"//www.gravatar.com/avatar/#{hash(input)}"
9+
end
10+
11+
private :hash
12+
13+
# Clean up the email address and return hashed version.
14+
def hash(email)
15+
email_address = email ? email.downcase.strip : ''
16+
Digest::MD5.hexdigest(email_address)
17+
end
18+
end
19+
end
20+
21+
Liquid::Template.register_filter(Jekyll::GravatarFilter)

0 commit comments

Comments
 (0)