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 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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` element
  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>An update on Robust Client-Side JavaScript (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="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <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>
  31. <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>
  32. <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>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://molily.de/update-on-robust-javascript/">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>An update on Robust Client-Side JavaScript</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://molily.de/update-on-robust-javascript/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>In 2017, I published the online book <strong><a href="/robust-javascript/">Robust Client-Side JavaScript – A Developer’s Guide</a></strong>. More than five years later, I think my advice is still valuable. JavaScript as a language has not changed much when it come to techniques for writing robust code.</p>
  71. <p>At the moment I do not have the time for a substantial book update. In this post, I would like to describe how I regard the whole issue today and how I would restructure the book today.</p>
  72. <p>Recently, yet another debate sparked over single page apps, client-side frameworks like <a href="https://www.zachleat.com/web/react-criticism/">React</a> and client-side versus server-side rendering. The arguments are not new. During the last decade, I found criticism of the dominant client-side JavaScript usage necessary and worthwhile. Especially proponents of progressive enhancement challenged the JavaScript ecosystem. Yet the mainstream did not adapt patterns for robustness and performance. Web sites became slower, more fragile, error-prone, inaccessible and exclusive largely due to client-side JavaScript misuse.</p>
  73. <p>There always has been a class of full-stack, “universal” JavaScript frameworks with a focus on speed and simplicity – think of <a href="https://markojs.com/">Marko</a>, <a href="https://astro.build/">Astro</a>, <a href="https://fresh.deno.dev/">Fresh</a> or <a href="https://qwik.builder.io/">Qwik</a>. They render HTML on the server and aim to ship the minimal necessary amount of JavaScript to the client. The client-side JavaScript then picks up where the server-side JavaScript left off, ideally without duplicating the server-side logic. More and more frameworks adapt such a workflow. But the mainstream is still dominated by React monoliths that comprises megabytes of client-side JavaScript.</p>
  74. <p>Unfortunately, the chasm between the opposite sides of the debate grew wider. The groups are no longer listening and talking to each other, but mock and ridicule each other while talking to their in-group. Echo chambers in social media amplify this divisive discourse.</p>
  75. <p>Certain posts from prominent JavaScript critics are filled with scathing polemic. They personally attack JavaScript project maintainers, calling them <a href="https://infrequently.org/2023/02/the-market-for-lemons/">liars, fraudsters and grifters</a> because they only recently have “discovered” the downsides of single page apps.</p>
  76. <p>I am all for radical criticism, especially targeted at billion-dollar tech corporations and powerful project leaders who <a href="https://hachyderm.io/@zachleat@zachleat.com/109830049815165514">try to silence critics</a>. We need to hold them accountable for tech that produces harmful, subpar web experiences. They deserve their decent share of “told you so”.</p>
  77. <p>It is frustrating that influential people did not listen to the facts or did not deliver on their own promises. Will it help to call them fraudsters in the moment they start to acknowledge the facts? I believe vitriol makes the situation even more dire. I am disappointed that smart people who made valueable contributions to the web engage in mudslinging.</p>
  78. <p>Only a few note that such a behavior may harm the developer community that is already toxic and exclusive:</p>
  79. <blockquote cite="https://hachyderm.io/@hbuchel/109830004886148541"> <p>I’ve been wailing on about <abbr title="single page apps">SPA</abbr> frameworks recently (like a lot of us have, probably because we’ve found more of our people over here) but just to be clear:</p> <p>The world sucks right now. Get a job where you can. If React or Vue or Angular or Next or whatever gets you a job, then that is absolutely fantastic. If you are new in web dev and it introduces you to a love for the web, that is also fantastic.</p> <p>You’re going to spend a lot of time as a web developer figuring out where web experiences are broken for humans and how your platform or tech choices affect that. That always has been and is always going to be true.</p> <p>Sometimes this conversation can veer into the “sounding self righteous” lane which gives me the ick.</p>
  80. <p>– <a href="https://hachyderm.io/@hbuchel/109830004886148541">Heather Buchel</a></p> </blockquote>
  81. <blockquote cite="https://social.lol/@sophie/109836319615664190"> <p>There’s a lot of fair criticism of React floating around at the mo. My one request is that people take care to separate “The React Community” from the people who just get paid to build things in React. I build stuff in React for money, but I do it as accessibly and consciously as I can. I know it’s far from ideal but that’s what we use. Switching to Not React isn’t really an option in an org of our size. So please avoid mud-slinging and name-calling, and keep the discussion civil.</p> <p>I think the biggest takeaway is that you probably don’t need React; and if you really do, and you’re sure, make sure you’re aware of the drawbacks and have plans to mitigate them.</p> <p>– <a href="https://social.lol/@sophie/109836319615664190">Sophie Koonin</a></p> </blockquote>
  82. <p>What a year, huh? – Captain, it’s February!</p>
  83. <p>Anyway, where were we?</p>
  84. <p>My 2017 book was an attempt to mediate between the different camps of the debate on client-side JavaScript usage. Much advice from JavaScript critics was and is abstract for the sole reason that you need a robust site architecture in the first place before writing any robust client-side JavaScript code. With my book, I wanted to teach patterns any developer who writes client-side JavaScript can apply in their daily work regardless of the framework used.</p>
  85. <p>Around 2015 and 2016, several front-end developers wondered how to properly address JavaScript failure. <a href="https://molily.de/javascript-failure/">What happens when a JavaScript enhancement fails?</a> These essential and much needed discussions urged me to write a book on robust JavaScript.</p>
  86. <p>Unfortunately, only a few developers and projects picked up the ideas and integrated them into their work. Preventing and dealing with failing JavaScript is still an afterthought for many projects, frameworks and libraries. We need to continuously explore how practical progressive enhancement in JavaScript looks like.</p>
  87. <p>In 2017, I also wanted to emphasize that progressive enhancement is much more than server-side rendering. The discussion on progressive enhancement regarding JavaScript still revolves around server-side rendering versus client-side rendering. For good reason, but this is where progressive enhancement starts.</p>
  88. <p>It is a first step to use a framework that renders fully-functional HTML documents on the server. A frameworks that understands your code, identifies client-side interactivity and figures out the least amount of JavaScript that needs to be shipped to the client.</p>
  89. <p>It does not mean you are done with it. Adding client-side JavaScript should not be one monolithic enhancement step from 0 to 100, but many small deliberate and considerate steps. That is why it is called progressive enhancement.</p>
  90. <p>So, where are we today? I welcome the new discussions on better web site architectures and sensible JavaScript usage. The industry is already shifting and new universal JavaScript frameworks are pushing the existing ecosystems.</p>
  91. <p>My 2017 book concluded with <a href="/robust-javascript/#writing-less-javascript">Writing less JavaScript</a>:</p>
  92. <blockquote> <p>An important skill of a front-end developer is to know when not to solve a problem with client-side JavaScript. It is always more robust to solve a problem further down in the stack.</p> <p>If all techniques and tools did not help you to write robust JavaScript, consider reducing the code complexity and the amount of code. In the last resort, reduce the usage of client-side JavaScript. Find simpler solutions that rely on HTML, CSS and server-side logic alone.</p> </blockquote>
  93. <p>Today I would turn the reasoning upside down. Reducing client-side JavaScript is the most effective technique to prevent JavaScript failure. The most robust client-side JavaScript is the JavaScript that is never written, never shipped to the client and never executed. (<a href="https://knowyourmeme.com/memes/roll-safe">Clever, huh?</a>)</p>
  94. <p>Apart from that, what techniques are essential today to write robust JavaScript?</p>
  95. <p>I’ve mentioned <strong>TypeScript</strong> as a compile-to-JavaScript language in the book. Since 2017, TypeScript conquered the JavaScript world, superseded other compile-to-JavaScript languages and became a de-facto standard for a great share of libraries and frameworks.</p>
  96. <p>TypeScript is the single tool that made my JavaScript significantly more robust in the last couple of years. It helps me to design cascades of operations that may fail and helps me to implement the success and error handling. Strict typing forces me to think twice and write cautious code.</p>
  97. <p>With the goal of writing robust JavaScript in mind, we should utilize TypeScript as a static code checker for JavaScript – like ESLint.</p>
  98. <p>TypeScript is also a language that is a superset of JavaScript. It is also a program that compiles (transpiles) this language to JavaScript. But you do not have to use these parts to get most benefits.</p>
  99. <p>You can check plain JavaScript files with the TypeScript type checker. It understands plain JavaScript well enough to spot my lapses and lazy thinking. You can <a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html">define types and add type annotations with JSDoc</a>.</p>
  100. <p>While there are benefits to write <code class="language-plaintext highlighter-rouge">.ts</code> files with TypeScript syntax, this is not necessary to get started. In several projects, I have hardly written any <code class="language-plaintext highlighter-rouge">.ts</code> file, but TypeScript is an essential development tool.</p>
  101. <p>My advice is to install an editor like Visual Studio Code and start using TypeScript in your <code class="language-plaintext highlighter-rouge">.js</code> or <code class="language-plaintext highlighter-rouge">.jsx</code> or <code class="language-plaintext highlighter-rouge">.vue</code> or <code class="language-plaintext highlighter-rouge">.svelte</code> files today.</p>
  102. <p>I understand that TypeScript adds a heavy burden for web developers who already struggle with JavaScript and its toolchains. (I guess that is, ehm, everyone here?) Fortunately, editors, libraries and frameworks make it easy to integrate typed code gradually so you can learn as you go.</p>
  103. <p>My 2017 book featured several chapters on <strong>automated testing</strong> from a high-level perspective. Since then, I gained more experience on automatic testing of web sites in general and client-side JavaScript in particular.</p>
  104. <p>In 2021, I published an extensive book on <strong><a href="https://testing-angular.com/">Testing Angular</a></strong>. It is the spiritual successor of <cite>Robust JavaScript</cite>.</p>
  105. <p>The fact that the book deals with Angular is partly consequence, partly coincidence. I had been working on large Angular projects for clients who demanded (and could afford) technical robustness and understood test automation. Plus, Angular is built with testability in mind and the community values well-tested, robust code.</p>
  106. <p>While the second book goes into all necessary details and Angular specifics, it was the big picture that inspired me. How can testing help us to make better web sites that do not let the user down, but empower them? How can we write better, more robust JavaScript through testing?</p>
  107. <p>So eventually I wrote <cite>Testing Angular</cite> for the same reason I wrote <cite>Robust JavaScript</cite>: I wanted to explore and teach writing better web experiences. Preventing JavaScript failure is still my white whale.</p>
  108. <p>Fortunately, testing tools improved since 2017 and both end-to-end testing as well as JavaScript unit and integration testing got more approachable and reliable. Today I put much more emphasis on automated testing to make JavaScript robust.</p>
  109. <p>Angular is the “special occasion” framework in the JavaScript world. While <a href="https://almanac.httparchive.org/en/2022/javascript#libraries-and-frameworks">jQuery is used on 81% and React is used on 8% of the web sites analyzed</a>, Angular is suitable for heavy-weight corporate intranet applications with a restricted audience using desktop computers.</p>
  110. <p>Especially JavaScript critics shake their head when Angular is mentioned. It ships larger amounts of client-side JavaScript than other frameworks. While it has rudimentary server-side rendering, it is not a practically viable default yet. Nonetheless, I think the Angular community listens to criticism and innovates.</p>
  111. <p>Angular has some architectural advantages that allows it to evolve in order to improve the site’s robustness and thereby the user experience. The core of Angular is a compiler. Like the Svelte compiler, it transforms your high-level, declarative and human-readable code into low-level, imperative and concise code that is shipped to the browser.</p>
  112. <p>The Angular team can optimize the compiler to produce more efficient and less code. In fact, the team recently rewrote the whole compiler and rendering engine. Thanks to the ahead-of-time compilation, there is dormant potential in Angular.</p>
  113. <p>I am excited that the Angular ecosystem catches up with projects like <a href="https://github.com/analogjs/analog">Analog.js</a>, a full-stack framework that compares itself to SvelteKit, Nuxt and Next.js. Although Angular will most likely stick with the single page application architecture, folks from the Angular community are building next-generation frameworks. For example, Miško Hevery, the creator of the original AngularJS framework and co-creator of Angular, now works on Qwik.</p>
  114. <p>While I still deem it necessary to teach basic defensive coding techniques, one cannot talk about robust client-side JavaScript without discussing the whole web site architecture. I have done so in several blog posts, but not prominently in <cite>Robust JavaScript</cite>.</p>
  115. <p>Client-side JavaScript affects performance and accessibility, and I have realized that these are essential aspects of robustness as well. If JavaScript is freezing a mobile browser for 20 seconds, the site is not usable, let alone robust. If a single page application breaks browser navigation features that users of assistive technologies rely on in particular, the site is not robust. And so on.</p>
  116. <p>All things considered, I think we are in a crucial phase of turmoil. My hope is that the JavaScript community convenes in a respectful and productive way, takes criticism seriously and makes radical technical and social changes.</p>
  117. </div>
  118. </article>
  119. <hr>
  120. <footer>
  121. <p>
  122. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  123. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  124. </svg> Accueil</a> •
  125. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  126. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  127. </svg> Suivre</a> •
  128. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  129. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  130. </svg> Pro</a> •
  131. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  132. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  133. </svg> Email</a> •
  134. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  135. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  136. </svg> Légal</abbr>
  137. </p>
  138. <template id="theme-selector">
  139. <form>
  140. <fieldset>
  141. <legend><svg class="icon icon-brightness-contrast">
  142. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  143. </svg> Thème</legend>
  144. <label>
  145. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  146. </label>
  147. <label>
  148. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  149. </label>
  150. <label>
  151. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  152. </label>
  153. </fieldset>
  154. </form>
  155. </template>
  156. </footer>
  157. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  158. <script>
  159. function loadThemeForm(templateName) {
  160. const themeSelectorTemplate = document.querySelector(templateName)
  161. const form = themeSelectorTemplate.content.firstElementChild
  162. themeSelectorTemplate.replaceWith(form)
  163. form.addEventListener('change', (e) => {
  164. const chosenColorScheme = e.target.value
  165. localStorage.setItem('theme', chosenColorScheme)
  166. toggleTheme(chosenColorScheme)
  167. })
  168. const selectedTheme = localStorage.getItem('theme')
  169. if (selectedTheme && selectedTheme !== 'undefined') {
  170. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  171. }
  172. }
  173. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  174. window.addEventListener('load', () => {
  175. let hasDarkRules = false
  176. for (const styleSheet of Array.from(document.styleSheets)) {
  177. let mediaRules = []
  178. for (const cssRule of styleSheet.cssRules) {
  179. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  180. continue
  181. }
  182. // WARNING: Safari does not have/supports `conditionText`.
  183. if (cssRule.conditionText) {
  184. if (cssRule.conditionText !== prefersColorSchemeDark) {
  185. continue
  186. }
  187. } else {
  188. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  189. continue
  190. }
  191. }
  192. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  193. }
  194. // WARNING: do not try to insert a Rule to a styleSheet you are
  195. // currently iterating on, otherwise the browser will be stuck
  196. // in a infinite loop…
  197. for (const mediaRule of mediaRules) {
  198. styleSheet.insertRule(mediaRule.cssText)
  199. hasDarkRules = true
  200. }
  201. }
  202. if (hasDarkRules) {
  203. loadThemeForm('#theme-selector')
  204. }
  205. })
  206. </script>
  207. </body>
  208. </html>