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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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>How I Build JavaScript Apps In 2021 (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://timdaub.github.io/2021/01/16/web-principles/">
  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>How I Build JavaScript Apps In 2021</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.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://timdaub.github.io/2021/01/16/web-principles/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>It's now roughly seven or eight years that I'm building dynamic front ends for the web. From <a target="_blank" rel="noopener" href="https://www.ascribe.io/">digital art wallets</a> to games (<a target="_blank" rel="noopener" href="https://ipfs.leapdao.org/blog/Planet-A-ccc-ethberlin-recap/">1</a>, <a href="https://timdaub.github.io/videogame">2</a>) and <a href="https://timdaub.github.io/wasm-synth">synthesizers</a>, I've seen it all. And since my process of creation has dramatically changed over the years, today, I'd like to share how I'm developing web apps in 2021.</p>
  71. <h2 id="i-avoid-build-processes.">I avoid build processes.</h2>
  72. <p>I still remember the debates with colleagues about using <a target="_blank" rel="noopener" href="https://babeljs.io/">babel</a> a few years ago. Within the front end development world, transpiling had just become a thing, so we ended up babelifying our builds to use ES6. Our argument back then was that one day, we would be able to push our application's directory structure on a web server and since all browsers would then support the augmented ES6 features, our app would just work! Without a build process. WOW! That must have been around 2015. When I look at the source code of these old applications now, our technical visions didn't end up becoming reality.</p>
  73. <p>Now, I try to keep my build process to a minimum. When I need to write a demo app, I particularly like using <a target="_blank" rel="noopener" href="https://babeljs.io/docs/en/babel-standalone#script-tags"><code>&lt;script type="text/babel"&gt;</code></a>. I love <a target="_blank" rel="noopener" href="https://preactjs.com/guide/v10/getting-started/#no-build-tools-route">preact's "no build tools route."</a> too. When I have to set up an actual app, I avoid <a target="_blank" rel="noopener" href="https://webpack.js.org/">webpack</a> and <a target="_blank" rel="noopener" href="https://rollupjs.org/">rollup</a>. I mainly get frustrated about the myriad ways of configuring them. Some minor thing always ends up being broken, which leads to hours of debugging foreign code. And that's frustrating. Using preact's no build route, finally something like the above is possible:</p>
  74. <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;script type=<span class="string">"module"</span>&gt;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">import</span> { h, Component, render } <span class="keyword">from</span> <span class="string">'https://unpkg.com/preact?module'</span>;</span><br><span class="line"> <span class="keyword">const</span> app = h(<span class="string">'h1'</span>, <span class="literal">null</span>, <span class="string">'Hello World!'</span>);</span><br><span class="line"> render(app, <span class="built_in">document</span>.body);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>
  75. <p>When having to use a build tool, I gravitate towards <a target="_blank" rel="noopener" href="https://parceljs.org/">parcel</a> or <a target="_blank" rel="noopener" href="https://github.com/developit/microbundle">microbundle</a> as they come preconfigured. And in particular, parcel is excellent, as it's merely using an HTML file as its entry point. To me, that's promising as it assumes a proper directory structure and properly connected files such that maybe one day I can push my app to the web without that build step.</p>
  76. <h2 id="i-avoid-transpiling.">I avoid transpiling.</h2>
  77. <p>For the same reasons as pointed out above, I also try to avoid transpiling. It's not because I don't like ESNext features, but more because I want to minimize the risk of getting stuck with the transpiler. Hence, I try to avoid using babel. <a href="https://timdaub.github.io/2020/09/01/typescript/">I also don't use Typescript</a>. To me, JavaScript is productive enough. Additionally, for <a target="_blank" rel="noopener" href="https://reactjs.org/">react</a>-style projects, no transpilers mean I stopped using <a target="_blank" rel="noopener" href="https://reactjs.org/docs/introducing-jsx.html">JSX</a>. Instead, I found an excellent library called <a target="_blank" rel="noopener" href="https://github.com/developit/htm">htm</a> that uses JavaScript's template strings. It has a similar syntax to JSX, but it's not breaking ECMAScript standards and hence needs no transpliation.</p>
  78. <h2 id="i-avoid-the-new-and-shiny.">I avoid the <em>new</em> and <em>shiny</em>.</h2>
  79. <p>I even avoid changing the way I work if I don't feel comfortable or inclined. For example, I never switched to <a target="_blank" rel="noopener" href="https://reactjs.org/docs/hooks-intro.html">react hooks</a>. The <a target="_blank" rel="noopener" href="https://reactjs.org/docs/state-and-lifecycle.html">lifecycle methods</a> that I initially know from iOS Objective-C programming are - in my opinion - a beautiful metaphor for writing front end components. And neither did I have any issues with my sites' performance. I would make the switch if I started to have problems. But I don't. The same goes for up and coming frameworks. Angular V2? <a target="_blank" rel="noopener" href="https://svelte.dev/">Svelte</a>? Cool, but why relearn a framework when I'm already productive with the one I use?</p>
  80. <h2 id="i-test-everything.">I test EvErYtHiNg.</h2>
  81. <p>When I started front-end development, testing was complicated. Only a few front end developer colleagues tested their apps properly. I ended up doing a lot of testing by hand. It was frustrating and unproductive. But testing front ends has improved dramatically over the years. Not only have the tools been significantly enhanced.</p>
  82. <p>We, as front-end developers, have now also figured out how to correctly write front end tests. Finally, we're able to distinguish between functional code and presentational code. For functional code, we now write unit tests. For presentational code, we use snapshot-based testing and integration tests. I'm pleased about tools like <a target="_blank" rel="noopener" href="https://www.cypress.io/">cypress</a> that is great for integration tests. I also like <a target="_blank" rel="noopener" href="https://github.com/avajs/ava/">ava</a> for unit tests.</p>
  83. <h2 id="i-optimize-for-performance-and-quality.">I optimize for performance and quality.</h2>
  84. <p>I used to be eager about building extensively functional software: the more features, the better. I'm not anymore. Instead, I try to develop software that works well for my users. I strive for quality. So I try to measure my build's size. I take proper care about delivering my application, meaning I turn on compression and <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">caching</a>. I care about optimizing my static assets like pictures. And I like to check my apps by using tools like <a target="_blank" rel="noopener" href="https://developers.google.com/speed/pagespeed/insights/?hl=de">PageSpeed Insights</a> or <a target="_blank" rel="noopener" href="https://developers.google.com/web/tools/lighthouse">lighthouse</a>.</p>
  85. <h2 id="i-use-my-own-work">I use my own work</h2>
  86. <p>From experience, I learned that I hardly ever get stuck on a algorithmic problem. On the contrary, I get motivated to learn something new and excel in the process. However, I caught my self often spending many hours on debugging other people's code. Mainly when it's third-party libraries that I included via npm. Once, I thought that using npm packages was a JavaScript developer's superpower. Now, I know that it can also be their curse.</p>
  87. <p>Instead of collecting heaps of third-party code, I now prefer following <a target="_blank" rel="noopener" href="http://hintjens.com/blog:96">Peter Hintjens' principles for writing good code</a>. I "use my own work." Meaning, I dare to write seemingly complex code myself. I still won't roll my own crypto or write a date library, but I dare to implement parts of a protocol or build an algorithm. It's not to say that I ditch every npm package and go npm-keto-diet. No, instead, I take a more mindful approach towards dependencies and only include them when I truly need them.</p>
  88. <p>I try to do that by leaving my technical vision at the doorstep of my office, so that I can now focus on solving the problems at hand. I try to stop worrying about eventualities far in the future as I view that as speculation. In cases I have past experiences, I use it to form my decision. For unchartered teritory, I move carefully, step by step.</p>
  89. <h2 id="i-use-open-source-to-my-advantage.">I use open source to my advantage.</h2>
  90. <p>I'm building websites since I'm a teenager. And had I open-sourced and maintained a few pieces of code that I need frequently, I'd be more productive now. Unfortunately, I was short-sighted. Surprisingly, I didn't think about still using JavaScript at 29 years of age 😂</p>
  91. <p>I stopped framing open source mainly around certain virtues like free speech, fairness or certain politics. They're still important virtues for me, but I learned that I could use open source also for building a personal toolset.</p>
  92. <p>Extracting a library from a codebase allows me to think about it from a user's perspective. It means I'm able to think about a piece of code's interfaces emphatically. Additionally, there's positive peer pressure. I'm not going to release shit to the world. When my project is public, it's going to have a proper README and some docs. And it's going to be tested. Since everybody can see it, I might as well create something I can be proud of.</p>
  93. <p>Hence, contributing to open source, for me, is about building high-quality software. Being anxious about not being able to monetize this particular piece of code has become less of a concern. <a target="_blank" rel="noopener" href="https://github.com/sindresorhus">sindresohrus</a> inspired me to treat open source packages like my personal toolbox.</p>
  94. <h2 id="conclusion">Conclusion</h2>
  95. <p>And that's my incomplete list of principles. I'm sure there's more than just these. Anyway, I still find some of these points quite controversial. I'm sure they won't work for everyone as all our contexts differ. But working solo as a freelancer, I've found that these principles contribute to me being content about what I'm doing. Hence, I was eager to share them.</p>
  96. <p>I'm curious to hear other's thoughts and see if they've taken similar paths. Please reach out! Also, don't forget to subscribe to my newsletter!</p>
  97. </article>
  98. <hr>
  99. <footer>
  100. <p>
  101. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  102. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  103. </svg> Accueil</a> •
  104. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  105. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  106. </svg> Suivre</a> •
  107. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  108. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  109. </svg> Pro</a> •
  110. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  111. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  112. </svg> Email</a> •
  113. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  114. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  115. </svg> Légal</abbr>
  116. </p>
  117. <template id="theme-selector">
  118. <form>
  119. <fieldset>
  120. <legend><svg class="icon icon-brightness-contrast">
  121. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  122. </svg> Thème</legend>
  123. <label>
  124. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  125. </label>
  126. <label>
  127. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  128. </label>
  129. <label>
  130. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  131. </label>
  132. </fieldset>
  133. </form>
  134. </template>
  135. </footer>
  136. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  137. <script>
  138. function loadThemeForm(templateName) {
  139. const themeSelectorTemplate = document.querySelector(templateName)
  140. const form = themeSelectorTemplate.content.firstElementChild
  141. themeSelectorTemplate.replaceWith(form)
  142. form.addEventListener('change', (e) => {
  143. const chosenColorScheme = e.target.value
  144. localStorage.setItem('theme', chosenColorScheme)
  145. toggleTheme(chosenColorScheme)
  146. })
  147. const selectedTheme = localStorage.getItem('theme')
  148. if (selectedTheme && selectedTheme !== 'undefined') {
  149. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  150. }
  151. }
  152. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  153. window.addEventListener('load', () => {
  154. let hasDarkRules = false
  155. for (const styleSheet of Array.from(document.styleSheets)) {
  156. let mediaRules = []
  157. for (const cssRule of styleSheet.cssRules) {
  158. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  159. continue
  160. }
  161. // WARNING: Safari does not have/supports `conditionText`.
  162. if (cssRule.conditionText) {
  163. if (cssRule.conditionText !== prefersColorSchemeDark) {
  164. continue
  165. }
  166. } else {
  167. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  168. continue
  169. }
  170. }
  171. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  172. }
  173. // WARNING: do not try to insert a Rule to a styleSheet you are
  174. // currently iterating on, otherwise the browser will be stuck
  175. // in a infinite loop…
  176. for (const mediaRule of mediaRules) {
  177. styleSheet.insertRule(mediaRule.cssText)
  178. hasDarkRules = true
  179. }
  180. }
  181. if (hasDarkRules) {
  182. loadThemeForm('#theme-selector')
  183. }
  184. })
  185. </script>
  186. </body>
  187. </html>