5. You’re driving a brand new car
It’s moving really slowly
You can smell smoke
People are giving you looks
6. You’re driving a brand new car
It’s moving really slowly Return the car
You can smell smoke Rebuild the car
People are giving you looks Disengage the hand brake
11. Yahoo!
34 “Best Practices” for Exceptional Performance
1. Minimize HTTP 9. Reduce DNS Lookups 19. Reduce the Number of 27. Choose <link> over
Requests DOM Elements @import
10. Minify JavaScript and
2. Use a Content CSS 20. Split Components 28. Avoid Filters
Delivery Network Across Domains
11. Avoid Redirects 29. Optimize Images
3. Add an Expires or a 21. Minimize the Number
Cache-Control 12. Remove Duplicate of iframes 30. Optimize CSS Sprites
Header Scripts
22. No 404s 31. Don’t Scale Images in
4. Gzip Components 13. Configure ETags HTML
23. Reduce Cookie Size
5. Put Stylesheets at the 14. Make Ajax Cacheable 32. Make favicon.ico
Top 24. Use Cookie-free Small and Cacheable
15. Flush the Buffer Early Domains for
6. Put Scripts at the Components 33. Keep Components
16. Use GET for AJAX under 25K (mobile)
Bottom Requests 25. Minimize DOM
7. Avoid CSS Access 34. Pack Components
17. Post-load into a Multipart
Expressions Components 26. Develop Smart Event Document (mobile)
8. Make Javascript and 18. Preload Components Handlers
CSS External
13. YSlow
Tests Your Site on 13 Rules
Minimize HTTP Put Stylesheets at Reduce DNS
Requests the Top Lookups
Use a Content Put Scripts at the Minify JavaScript
Delivery Network Bottom and CSS
Add an Expires Avoid CSS Avoid Redirects
or a Cache- Expressions
Control Header Remove
Make Javascript Duplicate Scripts
Gzip and CSS External
Components Configure ETags
15. Minimize HTTP Requests
HTTP Requests are Expensive!
Net work latency + server latency + net work latency + download time.
This rule is fundamental, the Catch-All. Everything else is based on it.
Asset packaging reduces overall number of requests.
Compression & minification reduce the size of responses.
Reordering includes lets important stuff go first.
ETags and Last-Modified checks reduce the content of an HTTP Request.
Browser caching completely eliminates subsequent requests. Major win.
17. Make Javascript
and CSS External
Easier to maintain, less HTML to send
Browser caching prevents later requests
Help order requests by priority
Rails makes this pretty easy,
stylesheet_link_tag
javascript_include_tag
21. Stylesheets at the top
Scripts at the bottom
<!DOCTYPE html>
Javascript shouldn’t block <html>
<head>
more important rendering
<title>Your Awesome Site</title>
<%= stylesheet_link_tag 'application' %>
<%= yield(:head) %>
</head>
<body>
Pass blocks to the head and <div id="page">
<header>
foot from within your views: <h1>Awesome Site</h1>
</header>
<article>
<%= yield %>
<% content_for(:foot) do %> </article>
<%= javascript_tag "…" %> </div>
<%= javascript_include_tag 'application' %>
<% end %> <%= yield(:foot) %>
</body>
</html>
29. Apache gives this for free for most of your
assets.
Rails gives this for free for all of the
responses it sends back. It also has some
helpers that will let you potentially skip db
calls and expensive view rendering.
CONFIGURE ETAGS
30. Configure ETags
Web servers likely give you a lot for free
Rails gives you some ETagging for free
Rails ETag and Last-Modified helper
def show
@article = Article.find(params[:id])
if stale?(:etag => @article, :last_modified => @article.created_at.utc)
@statistics = @article.really_expensive_call
respond_to do |format|
# etc
end
end
end
32. Add an Expires Header
Check out mod_expires for Apache
Rails already does “soft” cache busting
Hard cache busting (for CDNs)…
config.action_controller.asset_host =
"http://assets.example.com/#{REVISION}"
34. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
35. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
36. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
37. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
38. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
39. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
40. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
41. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: set the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end
42. #!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/environment'
require 'aws/s3'
require 'progressbar'
# Set up the S3 configuration and connection
config = YAML.load(File.open(File.join(RAILS_ROOT, 'config/amazon_s3.yml')))[RAILS_ENV]
bucket = config['bucket_name']
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
# Grab everything in the public directory
files = Dir.glob(File.join(RAILS_ROOT, 'public/**/*/**/*.{css,js,gif,jpg,png,…}'))
# Set up the progressbar
progressbar = ProgressBar.new("Uploading...", files.size)
# Iterate through the files
files.each do |file|
# Skip directories themselves - we're only interested in files
unless File.directory?(file)
# Create an object key from the file name and revision
key = file.gsub(/.*public//,"#{REVISION}/")
# Upload the object to S3
# TODO: make sure we're setting the right headers for gzipped objects
AWS::S3::S3Object.store(
key, open(file), bucket,
:access => :public_read, :expires => 1.year.from_now
)
# Increment the progressbar
progressbar.inc
end
end