But At What Cost?

But At What Cost?

Building a template processor for Rails.

F29327647a9cff5c69618bae420792ea?s=128

Aaron Patterson

May 02, 2019
Tweet

Transcript

  1. Hello!

  2. doES iT sCaLe?

  3. Jubilee Year

  4. Look at all the things I’m NOT doing!

  5. Prologue

  6. None
  7. Ruby Script

  8. None
  9. But At What Cost?

  10. Aaron Patterson

  11. Live From Here

  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. I have stickers of my cat! (please say "hello")

  20. @tenderlove

  21. None
  22. None
  23. Other == Puns?

  24. Speaking of Twitter…

  25. "

  26. None
  27. None
  28. None
  29. None
  30. @buritica

  31. ~54% useless

  32. March 14, 2019

  33. None
  34. None
  35. "But At What Cost?" Bot

  36. Twitter: 100% Useless

  37. Facepalm-er

  38. Facepalm-ee

  39. Response Algorithm Is it a pun? "Aaron" "But At What

    Cost?" Yes No
  40. Let’s Practice

  41. G GitHub

  42. Active Job

  43. None
  44. G GitHub We’re Hiring!!!!

  45. None
  46. None
  47. http://git.io/sparkles

  48. Ruby Core Team

  49. 10 Years On Ruby Core! W ow !!!

  50. Rails Core Team

  51. None
  52. I ❤ Ruby

  53. None
  54. Just Worked™

  55. No Boilerplate

  56. Java (1.3?) vs Ruby List list = new ArrayList(); list.add("1");

    list.add("2"); list.add("3"); Iterator iter = list.iterator(); List intList = new ArrayList(); while (iter.hasNext()) { String str = (String)iter.next(); intList.add(Integer.parseInt(str)); } list = %w{ 1 2 3 } intList = list.map { |l| l.to_i } Java Ruby
  57. My First Ruby Program Serious ^

  58. Never Write Infinite Loops

  59. I ❤ Rails

  60. None
  61. Just Worked™

  62. No Boilerplate

  63. Community Values

  64. "I MUST WRITE RAILS!"

  65. BUT AT WHAT COST?

  66. -25% $114k ➡ $85k (in 2006)

  67. More Companies

  68. More People

  69. Thank You!

  70. #ENDGAME

  71. None
  72. BUT AT WHAT COST?

  73. None
  74. Game of Thrones

  75. Dumbledore Dies

  76. None
  77. Cool Performance Tips

  78. No Parenthesis is Faster

  79. Parens vs No Parens def foo1(bar, baz) end def foo2(bar,

    baz) end def foo3(bar, baz) end def foo1 bar, baz end def foo2 bar, baz end def foo3 bar, baz end Parens No Parens
  80. "No code is faster than no code"

  81. ruby -y

  82. Parse States $ ruby --disable-gems -y with-parens.rb | wc -l

    805 $ ruby --disable-gems -y without-parens.rb | wc -l 778
  83. Single Quotes Are 2x Faster Than Double Quotes

  84. You Have To Hit The Shift Key To Type "

  85. Actual Performance Tip Rails 6!

  86. Allocation Count Started GET "/users/new" for ::1 at 2019-04-30 16:45:49

    -0500 Processing by UsersController#new as */* Rendering users/new.html.erb within layouts/application Rendered users/_form.html.erb (Duration: 1.4ms | Allocations: 552) Rendered users/new.html.erb within layouts/application (Duration: 1.7ms | Allocations: 640) Completed 200 OK in 6ms (Views: 5.4ms | ActiveRecord: 0.0ms | Allocations: 4140) Started GET "/users/new" for ::1 at 2019-04-30 16:47:02 -0500 Processing by UsersController#new as */* Rendering users/new.html.erb within layouts/application Rendered users/_form.html.erb (Duration: 1.4ms | Allocations: 827) Rendered users/_expensive.html.erb (Duration: 1.6ms | Allocations: 10098) Rendered users/new.html.erb within layouts/application (Duration: 4.3ms | Allocations: 11553) Completed 200 OK in 9ms (Views: 8.5ms | ActiveRecord: 0.0ms | Allocations: 15981)
  87. Allocation Count Started GET "/users/new" for ::1 at 2019-04-30 16:45:49

    -0500 Processing by UsersController#new as */* Rendering users/new.html.erb within layouts/application Rendered users/_form.html.erb (Duration: 1.4ms | Allocations: 552) Rendered users/new.html.erb within layouts/application (Duration: 1.7ms | Allocations: 640) Completed 200 OK in 6ms (Views: 5.4ms | ActiveRecord: 0.0ms | Allocations: 4140) Started GET "/users/new" for ::1 at 2019-04-30 16:47:02 -0500 Processing by UsersController#new as */* Rendering users/new.html.erb within layouts/application Rendered users/_form.html.erb (Duration: 1.4ms | Allocations: 827) Rendered users/_expensive.html.erb (Duration: 1.6ms | Allocations: 10098) Rendered users/new.html.erb within layouts/application (Duration: 4.3ms | Allocations: 11553) Completed 200 OK in 9ms (Views: 8.5ms | ActiveRecord: 0.0ms | Allocations: 15981)
  88. Rails at GitHub

  89. Template Pre-Compilation Method Name Generation

  90. Template Pre- Compilation

  91. Method Name Generation

  92. Build a Template Renderer!

  93. Rails is MVC

  94. MVC Model View Controller Request

  95. Modal View Controller

  96. MVC Model View Controller Request Action Controller Action View Active

    Record
  97. Action View

  98. Action View Finds Templates Compiles Templates Executes Views

  99. View Templates

  100. ERB

  101. Developed by @m_seki

  102. ERB Template <h1>New User</h1> <%= render_form %> <%= link_to 'Back'

    %> new.html.erb
  103. ERB Compiler require "erb" # Compile the template template =

    ERB.new File.read "test.html.erb" # Print the template source puts template.src # Evaluate the template puts template.result(binding) compile.rb
  104. Compiled Template _erbout = +"" _erbout << "<h1>New User</h1>\n\n".freeze _erbout

    << render_form.to_s _erbout << "\n\n".freeze _erbout << link_to("Back").to_s _erbout << "\n".freeze _erbout
  105. Modified Compile Script require "erb" def render_form "<form></form>" end def

    link_to(text) "<a href=\"#\">#{text}</a>" end # Compile the template template = ERB.new File.read "test.html.erb" # Evaluate the template puts template.result(binding)
  106. Compiler Output $ ruby compiler.rb <h1>New User</h1> <form></form> <a href="#">Back</a>

  107. ERB Translations ERB Source Translated Ruby <%= something %> _erbout

    << something.to_s <% something_else %> something_else
  108. Capturing Blocks <div> <%= capture do %> Hello! <% end

    %> </div> require "erb" def capture yield end # Compile the template template = ERB.new File.read "test.html.erb" puts template.result(binding)
  109. What is the output? Hello! <div> </div> <div> Hello! </div>

    ERROR!
  110. Compiled Source _erbout = +'' _erbout << "<div>\n ".freeze _erbout

    << ( capture do ).to_s _erbout << "\n Hello!\n ".freeze end _erbout << "\n</div>\n".freeze _erbout <div> <%= capture do %> Hello! <% end %> </div>
  111. Compiled Source _erbout = +'' _erbout << "<div>\n ".freeze _erbout

    << ( capture do ).to_s _erbout << "\n Hello!\n ".freeze end _erbout << "\n</div>\n".freeze _erbout <div> <%= capture do %> Hello! <% end %> </div>
  112. How does this work in Rails?

  113. ERB Compiler BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/ def add_expression(indicator, code) flush_newline_if_pending(src) if

    (indicator == "==") || @escape src << "@output_buffer.safe_expr_append=" else src << "@output_buffer.append=" end if BLOCK_EXPR.match?(code) src << " " << code else src << "(" << code << ");" end end
  114. ERB in Rails

  115. Different ERB Flavors Original ERB ERubis ERubi

  116. ERB Performance

  117. Rendering Speed require "erb" require "benchmark/ips" class AaronView TEMPLATES =

    { } TEMPLATES["index"] = <<-eerb <html> <body> <h1>Hello <%= name %><h1> </html> eerb def name; "Aaron"; end def render(template) ERB.new(TEMPLATES[template]).result(binding) end end view = AaronView.new Benchmark.ips do |x| x.report("render") { view.render("index") } end
  118. Rendering Speed $ ruby rendering_speed.rb Warming up -------------------------------------- render 2.696k

    i/100ms Calculating ------------------------------------- render 27.117k (± 5.9%) i/s - 137.496k in 5.092633s
  119. Cache Compilation class AaronView TEMPLATES = { } TEMPLATES["index"] =

    <<-eerb <html> <body> <h1>Hello <%= name %><h1> </html> eerb def name; "Aaron"; end def render(template) ERB.new(TEMPLATES[template]).result(binding) end COMPILED_TEMPLATES = { } def render_compiled(template) erb = COMPILED_TEMPLATES[template] ||= ERB.new(TEMPLATES[template]) erb.result(binding) end end view = AaronView.new Benchmark.ips do |x| x.report("render") { view.render("index") } x.report("render compiled") { view.render_compiled("index") } x.compare! end
  120. Cache Compilation $ ruby rendering_speed.rb Warming up -------------------------------------- render 2.709k

    i/100ms render compiled 6.293k i/100ms Calculating ------------------------------------- render 27.115k (± 2.6%) i/s - 138.159k in 5.098881s render compiled 63.645k (± 3.9%) i/s - 320.943k in 5.050544s Comparison: render compiled: 63645.5 i/s render: 27114.8 i/s - 2.35x slower
  121. Define a Method class AaronView TEMPLATES = { } TEMPLATES["index"]

    = <<-eerb <html> <body> <h1>Hello <%= name %><h1> </html> eerb def name; "Aaron"; end def render(template) ERB.new(TEMPLATES[template]).result(binding) end COMPILED_TEMPLATES = { } def render_compiled(template) erb = COMPILED_TEMPLATES[template] ||= ERB.new(TEMPLATES[template]) erb.result(binding) end METHODS_DEFINED = { } def render_with_method(template) unless METHODS_DEFINED.key? template erb = ERB.new(TEMPLATES[template]) self.class.class_eval "def render_#{template}; #{erb.src}; end" METHODS_DEFINED[template] = true end send "render_#{template}" end end
  122. Define a Method $ ruby rendering_speed.rb Warming up -------------------------------------- render

    2.750k i/100ms render compiled 6.462k i/100ms render method 114.420k i/100ms Calculating ------------------------------------- render 28.389k (± 2.7%) i/s - 143.000k in 5.041158s render compiled 65.860k (± 2.4%) i/s - 329.562k in 5.006962s render method 1.477M (± 1.7%) i/s - 7.437M in 5.038304s Comparison: render method: 1476591.3 i/s render compiled: 65860.1 i/s - 22.42x slower render: 28388.6 i/s - 52.01x slower
  123. Method Generation require "erb" erb_source = <<-erb <html> <body> <h1>Hello

    <%= name %><h1> </html> erb erb = ERB.new erb_source template = "index" self.class.class_eval "def render_#{template}; #{erb.src}; end" send "render_#{template}" Template Source Compile Source Define a m ethod Render the Template
  124. Method Generation def render_index _erbout = +'' _erbout << "<html>\n

    <body>\n <h1>Hello ".freeze _erbout << name.to_s _erbout << "<h1>\n</html>\n".freeze _erbout end Compiled Source
  125. Templates Are Translated to Methods

  126. Rails Template Methods <h1> This is an inner template <%

    puts methods.grep(/_app/) %> </h1> Template in Rails
  127. Rails Template Methods Console Output _app_views_users_index_html_erb___4485674087862414886_70282093274980 _app_views_users__first_ja_html_erb__657974686689484881_70282053324000 _app_views_users__second_html_erb___3508242123539422948_70282053523460

  128. Memory Size def render_index _erbout = +'' _erbout << "<html>\n

    <body>\n <h1>Hello ".freeze _erbout << name.to_s _erbout << "<h1>\n</html>\n".freeze _erbout end require "objspace" iseq = RubyVM::InstructionSequence.of( method(:render_index)) p ObjectSpace.memsize_of iseq $ ruby test.rb 1264
  129. Template Handling In Rails

  130. Instance Variable Visibility <h1>This is template Foo!</h1> <%= @some_ivar %>

    <h1>This is template Bar!</h1> <%= @some_ivar %> _foo.html.erb _bar.html.erb
  131. Compiled Templates class ActionView::Base def render_foo _erbout = +'' _erbout

    << "<h1>This is template Foo!</h1>\n".freeze _erbout << @some_ivar.to_s _erbout << "\n".freeze _erbout end def render_bar _erbout = +'' _erbout << "<h1>This is template Barr!</h1>\n".freeze _erbout << @some_ivar.to_s _erbout << "\n".freeze _erbout end end _foo.html.erb _bar.html.erb Same Instance Same Instance
  132. Don’t use instance variables in your templates

  133. "Don’t use instance variables in your templates" - Aaron

  134. Instance Variable Visibility <h1>This is template Foo!</h1> <%= @some_ivar %>

    <h1>This is template Bar!</h1> <%= @some_ivar %> _foo.html.erb _bar.html.erb
  135. Local Variables

  136. Rendering With Locals <body> <%= render "content", locals: { name:

    "Gorby" } %> </body>
  137. Compiling With Locals <h1> Hello, <%= name %> </h1> def

    render_content _erbout = +'' _erbout << "<h1>\n Hello, ".freeze _erbout << name.to_s _erbout << "\n</h1>\n".freeze _erbout end content.html.erb Compiled Method Method Call
  138. Preamble With Locals

  139. Compiling With Locals <h1> Hello, <%= name %> </h1> def

    render_content(locals) # Locals preamble name = locals[:name] _erbout = +'' _erbout << "<h1>\n Hello, ".freeze _erbout << name.to_s _erbout << "\n</h1>\n".freeze _erbout end content.html.erb Compiled Method
  140. Templates Require Context <body> <%= render "content", locals: { name:

    "Gorby" } %> <%= render "content" %> </body> <h1> Hello, <%= name %> </h1> main.html.erb content.html.erb Method call or local variable? local variable method call
  141. Templates Can’t Be Compiled In Advance* *(FORESHADOWING)

  142. Compiled Too Many Times

  143. Too Many Compilations <body> <%= render "content", locals: { name:

    "Gorby" } %> <%= render "content", locals: { name: "Gorby", friend: "Aaron" } %> </body> def render_content_1(locals) # Locals preamble name = locals[:name] _erbout = +'' _erbout << "<h1>\n Hello, ".freeze _erbout << name.to_s _erbout << "\n</h1>\n".freeze _erbout end def render_content_2(locals) # Locals preamble name = locals[:name] friend = locals[:friend] _erbout = +'' _erbout << "<h1>\n Hello, ".freeze _erbout << name.to_s _erbout << "\n</h1>\n".freeze _erbout end main.html.erb Compiled "content" Unique Preambles
  144. "This is nice, but what should I do?"

  145. Always pass the same locals to individual templates.

  146. The render function

  147. Finds a Template Compiles the Template Calculates a Method Name

    Calls the Method
  148. Find a Template (from the cache) Compiled? Compile the Template

    Call the method Calculate a Method Name
  149. Finding a Template

  150. Template Rendering Depends on "Requested Format"

  151. Users Controller class UsersController < ApplicationController # GET /users #

    GET /users.json def index @users = User.all render "index" end end $ tree app/views/users app/views/users !"" index.html.erb #"" index.xml.erb Controller app/views/users/*
  152. Template Source <h1>XML template</h1> <h1>Users</h1> index.xml.erb index.html.erb

  153. Requests and Rendering Request Response Template Rendered curl -H 'Accept:

    application/xml' http:// localhost:3000/users <h1>XML template</h1> index.xml.erb curl http://localhost: 3000/users <h1>Users</h1> index.html.erb curl -H 'Accept: text/ html' http://localhost: 3000/users <h1>Users</h1> index.html.erb
  154. Template Cache Keys • Local variables • Format • Locale

    • Variant (iPhone, etc)
  155. Strange Render Behavior

  156. Users Controller class UsersController < ApplicationController # GET /users #

    GET /users.json def index @users = User.all render "index" end end $ tree app/views/users app/views/users !"" _my_template.png.erb !"" _my_template.xml.erb #"" index.html.erb Controller app/views/users/*
  157. Template Contents <h1>Users</h1> <%= render "my_template" %> I guess this

    is a png? index.html.erb _my_template.png.erb <cool> XML is cool! </cool> _my_template.xml.erb
  158. Requests and Rendering Request Response Template Rendered curl -H 'Accept:

    application/xml' http://localhost:3000/users Missing XML template Error None curl -H 'Accept: text/html' http://localhost:3000/users Missing HTML partial Error None Browser Missing HTML partial Error None curl http://localhost:3000/users <h1>Users</h1> I guess this is a png? index.html.erb _my_template.png.erb
  159. Error!!

  160. Requests and Rendering Request Response Template Rendered curl -H 'Accept:

    application/xml' http://localhost:3000/users Missing XML template Error None curl -H 'Accept: text/html' http://localhost:3000/users Missing HTML partial Error None Browser Missing HTML partial Error None curl http://localhost: 3000/users <h1>Users</h1> I guess this is a png? index.html.erb _my_template.png.erb
  161. We Can’t Predict <h1>Users</h1> <%= render "my_template" %>

  162. respond_to Controller class UsersController < ApplicationController # GET /users #

    GET /users.json def index @users = User.all render "index" end end class UsersController < ApplicationController # GET /users # GET /users.json def index @users = User.all respond_to do |format| format.html do render "index" end end end end "Bare" render respond_to render
  163. Requests and Rendering Request Response Template Rendered curl -H 'Accept:

    application/ xml' http://localhost:3000/ users Missing XML template Error None curl -H 'Accept: text/html' http://localhost:3000/users Missing HTML partial Error None Browser Missing HTML partial Error None curl http://localhost:3000/ users Missing HTML partial Error None
  164. Context Dependent <h1>Users</h1> <%= render "my_template" %> class UsersController <

    ApplicationController # GET /users # GET /users.json def index @users = User.all render "index" end end class UsersController < ApplicationController # GET /users # GET /users.json def index @users = User.all respond_to do |format| format.html do render "index" end end end end index.html.erb
  165. Templates are not "Context Free"

  166. Prediction and Consistency

  167. Find a Template (from the cache) Compiled? Compile the Template

    Call the method Calculate a Method Name Cheap! Cheap! Expensive!
  168. What Method Will This Call? <h1>Users</h1> <%= render "my_template" %>

    <h1>Users</h1> <%= render_my_template_00011123abf %> index.html.erb translated template Calls a unique method. But what method?
  169. Find a Template (from the cache) Compiled? Compile the Template

    Call the method Calculate a Method Name Cheap! Cheap! Expensive!
  170. #SPOILER

  171. Slower Production Boot (but could be eliminated by BootSnap)

  172. Lower Memory

  173. Faster Runtime No More "Cache Check" Penalty

  174. DETOUR!

  175. Three Templates <ul> <%= render partial: "customer", collection: customers %>

    </ul> <ul> <%= render partial: "customer", collection: customers, cached: true %> </ul> _col.html.erb _cached_col.html.erb <li> Hello: <%= customer.name %> </li> _customer.html.erb
  176. Benchmark https://github.com/rails/rails/issues/35257

  177. Results $ be ruby render_benchmark.rb -- create_table(:customers, {:force=>true}) -> 0.0108s

    <#ActiveSupport::Cache::MemoryStore entries=1000, size=333893, options={}> Warming up -------------------------------------- collection render: no cache 6.000 i/100ms collection render: with cache 1.000 i/100ms Calculating ------------------------------------- collection render: no cache 65.319 (± 7.7%) i/s - 330.000 in 5.080651s collection render: with cache 11.504 (± 8.7%) i/s - 58.000 in 5.071030s Comparison: collection render: no cache: 65.3 i/s collection render: with cache: 11.5 i/s - 5.68x slower
  178. I was convinced the cache didn’t work

  179. I was convinced the cache didn’t work

  180. I was convinced the cache didn’t work

  181. I was convinced the cache didn’t work

  182. But, there were entries in the cache

  183. BUT AT WHAT COST?

  184. Profiled The Cache ================================== Mode: wall(1000) Samples: 4690 (0.00% miss

    rate) GC: 207 (4.41%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 594 (12.7%) 594 (12.7%) ActiveModel::LazyAttributeHash#[] 387 (8.3%) 387 (8.3%) ActiveRecord::Transactions#update_attributes_from_transaction_state 1199 (25.6%) 246 (5.2%) ActiveSupport::Cache::Store#expanded_key 301 (6.4%) 218 (4.6%) AbstractController::Caching::Fragments#combined_fragment_cache_key 207 (4.4%) 207 (4.4%) (garbage collection) 932 (19.9%) 196 (4.2%) ActionView::CollectionCaching#collection_by_cache_keys 450 (9.6%) 181 (3.9%) ActiveSupport::Cache::Store#expanded_version
  185. Cache Key Calculation Was More Expensive Than Template Execution

  186. Cache Payoff <li> Hello: <%= customer.name %> </li> <li>Hello: <%=

    customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> <li>Hello: <%= customer.name %></li> Old Template New Template
  187. Benchmark Results $ be ruby render_benchmark.rb -- create_table(:customers, {:force=>true}) ->

    0.0075s <#ActiveSupport::Cache::MemoryStore entries=1000, size=900893, options={}> Warming up -------------------------------------- collection render: no cache 4.000 i/100ms collection render: with cache 4.000 i/100ms Calculating ------------------------------------- collection render: no cache 41.066 (± 2.4%) i/s - 208.000 in 5.070051s collection render: with cache 43.192 (± 4.6%) i/s - 216.000 in 5.012803s Comparison: collection render: with cache: 43.2 i/s collection render: no cache: 41.1 i/s - same-ish: difference falls within error
  188. "Cache" doesn’t mean "fast"

  189. Speeding Up Templates

  190. Template Static Analysis

  191. Pre-Compiling Templates https://github.com/jhawthorn/actionview_precompiler/

  192. ActionviewPrecompiler.precompile

  193. Static Analysis <%= render "no_locals" %> <%= render "static_locals", locals:

    { foo: bar } %> <%= render "dynamic_locals", locals: { foo: bar }.merge(baz) %> Can Precompile! Can Precompile! Nope!
  194. Predicting "Render" Methods

  195. "Same Format Assumption" <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :png %> $ tree app/views/users app/views/users !"" _my_template.html.erb !"" _my_template.png.erb !"" _my_template.xml.erb #"" index.html.erb index.html.erb templates
  196. Ambiguous Render Problem <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :html %> $ tree app/views/users app/views/users !"" _my_template.png.erb !"" _my_template.xml.erb #"" index.html.erb index.html.erb templates Sometimes an exception Sometimes a PNG Always an exception
  197. "Always Optimize" <h1>Users</h1> <%= render "my_template" %> <%= render "my_template",

    format: :html %> <h1>Users</h1> <%= render "my_template", format: :png %> <%= render "my_template", format: :html %> index.html.erb index.html.erb
  198. "Same Format Assumption" <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :png %> $ tree app/views/users app/views/users !"" _my_template.html.erb !"" _my_template.png.erb !"" _my_template.xml.erb #"" index.html.erb index.html.erb templates
  199. "Same Format Assumption" <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :png %> <h1>Users</h1> <%= render_my_template_html_00123abc %> <%= render_my_template_png_00123abc %> index.html.erb Optimized Template
  200. Ambiguous Render Problem <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :html %> $ tree app/views/users app/views/users !"" _my_template.png.erb !"" _my_template.xml.erb #"" index.html.erb index.html.erb templates
  201. Ambiguous Render Problem <h1>Users</h1> <%= render "my_template" %> <%= render

    "my_template", format: :html %> <h1>Users</h1> <%= render "my_template" %> <%= raise MissingTemplateError %> index.html.erb Optimized Template
  202. Wrap It Up

  203. Be "Context Free" Don’t depend on side effects

  204. Be Consistent

  205. Cache != Fast

  206. Benchmark First!

  207. Keep Building Apps!

  208. Thank You!!!