A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 12KB

4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Visitors, Developers, or Machines (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2020-06-19.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script type="text/javascript">
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://garrettdimon.com/2020/visitors-developers-or-machines/">
  55. <body class="remarkdown h1-underline h2-underline h3-underline hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Visitors, Developers, or Machines</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  63. <a href="https://garrettdimon.com/2020/visitors-developers-or-machines/" title="Lien vers le contenu original">Source originale</a>
  64. </p>
  65. </nav>
  66. <hr>
  67. <main>
  68. <p>I keep my feet squarely planted in two worlds when it comes to development. One of those is all things front-end, and the other is Ruby (and Rails).</p>
  69. <p>With Ruby/Rails, they’re frequently maligned as a not-serious programming language/framework pair because they focus on developer happiness more than pure performance. They follow a philosophy that <a href="https://m.signalvnoise.com/only-15-of-the-basecamp-operations-budget-is-spent-on-ruby/">the productivity gains from a happy developer far outweigh any performance losses from being less performant</a>. That’s not to say that they outright ignore performance, but it isn’t the only or even primary goal. Nor should it be.</p>
  70. <p>So Ruby gets a bad reputation for not putting performance above all other things. On the other hand, despite a deeper understanding and increased attention to performance in front-end development, the most popular front-end frameworks are going in an entirely different direction. These heavy JavaScript frameworks give some benefit to perceived performance once the JavaScript is loaded and cached, but oftentimes, pages that could be trivially lightweight and work fine end up including significant overhead in the form of frameworks.</p>
  71. <p>I’ve been trying to understand the appeal of these frameworks by giving them an objective chance. I’ve expanded my knowledge of JavaScript and tried to give them the benefit of the doubt. They do have their places, but the only explanation I can come up with is that developers are taking a similar approach as Ruby and focusing on developer convenience and productivity. Only, instead of Ruby’s performance being tied to the CPU level, JavaScript frameworks push the performance burden to the client.</p>
  72. <p>In both cases, the tradeoff happens in the name of developer happiness and productivity, but the strategies have entirely different consequences. With Ruby, the CPU is still (mostly) the responsibility of the development team, and it can be upgraded. With JavaScript, the page weight becomes an externality pushed onto visitors.</p>
  73. <p>Increased bandwidth costs may be somewhat of a concern to the developer, but with the exception of the savviest organizations, I’m not aware of front-end developers giving significant consideration to bandwidth bills. Bandwidth costs might receive lip service, but given that the average page weight has <a href="https://httparchive.org/reports/page-weight">gone from about 500Kb in 2010 to almost 2,000Kb in 2020</a>, the evidence shows it’s not a significant consideration.</p>
  74. <p>The unfortunate reality with pushing the performance burden to the front-end is the lack of reliable connections, and those aren’t just about being hard-wired to fiber or on mobile. Everyone at some point has a less-than-stellar connection even if only from concrete or metal walls and roofs, and it becomes crystal clear when a site works well on a thin connection. Disasters are a great example of this. Connectivity can be incredibly limited when disasters affect infrastructure.</p>
  75. <p>The performance tradeoff isn’t about <em>where</em> the bottleneck is. It’s about <em>who</em> has to carry the burden. It’s one thing for a developer to push the burden onto a server they control. It’s another thing entirely to expect visitors to carry that load when connectivity and device performance isn’t a constant.</p>
  76. <p>Developer productivity is a great metric, but it can’t be isolated from the larger ecosystem. With Ruby, the tradeoff works because nothing is externalized, and it’s barely even a tradeoff these days. But with large front-end JavaScript frameworks, things aren’t just slow. If that JavaScript isn’t able to be loaded for a variety of reasons, sites don’t just become a little slower. They break entirely.</p>
  77. <p>JavaScript isn’t inherently problematic. Using JavaScript in a way that leads to complete failure is the problem. Layering in something like <a href="https://github.com/turbolinks/turbolinks">Turbolinks</a> provides the performance benefits of the larger JavaScript frameworks, but the difference is that Turbolinks is smaller, and, even if it doesn’t load, the site can still work.</p>
  78. <p>There’s promise in the front-end frameworks. They’re not entirely bad or wrong, but the gains in developer productivity shouldn’t come at the expense of the visitor. It may not be fashionable, but rendering usable HTML on the server and then sprinkling in progressive enhancement via CSS and JavaScript is still the best all around experience for the people visiting the site. That’s the direction these frameworks should be heading.</p>
  79. </main>
  80. </article>
  81. <hr>
  82. <footer>
  83. <p>
  84. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  85. <a href="/david/log/" title="Accès au flux RSS">🤖</a> •
  86. <a href="http://larlet.com" title="Go to my English profile" data-instant>🇨🇦</a> •
  87. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">📮</a> •
  88. <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">🧚</abbr>
  89. </p>
  90. <template id="theme-selector">
  91. <form>
  92. <fieldset>
  93. <legend>Thème</legend>
  94. <label>
  95. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  96. </label>
  97. <label>
  98. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  99. </label>
  100. <label>
  101. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  102. </label>
  103. </fieldset>
  104. </form>
  105. </template>
  106. </footer>
  107. <script type="text/javascript">
  108. function loadThemeForm(templateName) {
  109. const themeSelectorTemplate = document.querySelector(templateName)
  110. const form = themeSelectorTemplate.content.firstElementChild
  111. themeSelectorTemplate.replaceWith(form)
  112. form.addEventListener('change', (e) => {
  113. const chosenColorScheme = e.target.value
  114. localStorage.setItem('theme', chosenColorScheme)
  115. toggleTheme(chosenColorScheme)
  116. })
  117. const selectedTheme = localStorage.getItem('theme')
  118. if (selectedTheme && selectedTheme !== 'undefined') {
  119. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  120. }
  121. }
  122. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  123. window.addEventListener('load', () => {
  124. let hasDarkRules = false
  125. for (const styleSheet of Array.from(document.styleSheets)) {
  126. let mediaRules = []
  127. for (const cssRule of styleSheet.cssRules) {
  128. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  129. continue
  130. }
  131. // WARNING: Safari does not have/supports `conditionText`.
  132. if (cssRule.conditionText) {
  133. if (cssRule.conditionText !== prefersColorSchemeDark) {
  134. continue
  135. }
  136. } else {
  137. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  138. continue
  139. }
  140. }
  141. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  142. }
  143. // WARNING: do not try to insert a Rule to a styleSheet you are
  144. // currently iterating on, otherwise the browser will be stuck
  145. // in a infinite loop…
  146. for (const mediaRule of mediaRules) {
  147. styleSheet.insertRule(mediaRule.cssText)
  148. hasDarkRules = true
  149. }
  150. }
  151. if (hasDarkRules) {
  152. loadThemeForm('#theme-selector')
  153. }
  154. })
  155. </script>
  156. </body>
  157. </html>