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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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>Innovating beyond libraries and frameworks (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://nilsnh.no/2022/04/09/innovating-beyond-libraries-and-frameworks/">
  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>Innovating beyond libraries and frameworks</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://nilsnh.no/2022/04/09/innovating-beyond-libraries-and-frameworks/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>I believe we should look beyond libraries and frameworks and rediscover the value of patterns and principles, and I’d argue that it would lead us to have less breaking changes and add more longevity to the stuff we build.</p>
  71. <p>I have been a big fan of the <a href="https://www.brandons.me/blog/libraries-not-frameworks">write libraries, not frameworks</a> argument for a while now. Lately, I’ve come to ponder that there might be a fruitful expansion to this argument, that we should start to value principles over patterns, patterns over libraries, and libraries over frameworks.</p>
  72. <p>Let’s clarify some terminology:</p>
  73. <ul>
  74. <li>
  75. <p><strong>Framework:</strong> This is (usually) someone else’s code that calls your code. In order for this to work your code will need to conform to constraints set down by the framework. These constraints are often firm boundaries that’s hard to code around. On the flipside by coding within the framework’s conventions and constraints you tend to get a lot of useful functionality out of the box making coding quicker.</p>
  76. </li>
  77. <li>
  78. <p><strong>Library:</strong> This is (usually) someone else’s code that <em>you call</em> from your code. A library tends to be some code that imposes fewer constraints on your code as compared to frameworks. By using one or more libraries you’re able to reuse someone else’s code to solve your problems. Libraries are easier to combine and interchange, while putting frameworks on top of frameworks can lead to a bad time.</p>
  79. </li>
  80. <li>
  81. <p><strong>Pattern:</strong> This is a descriptive, reusable <em>approach</em> to writing your code (see: <a href="https://en.wikipedia.org/wiki/Software_design_pattern">software design pattern</a>). Patterns range in their vaguenes, applicability and prescriptiveness. Examples of programming patterns include <a href="https://www.itamarweiss.com/personal/2018/02/28/return-early-pattern.html">early-return pattern</a>, <a href="https://en.wikipedia.org/wiki/Builder_pattern">builder-pattern</a>, <a href="https://en.wikipedia.org/wiki/Actor_model">actor model</a>, <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">model-view-controller</a>, <a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/">onion architecture</a>, <a href="https://en.wikipedia.org/wiki/Microservices">microservices</a>, <a href="https://m.signalvnoise.com/the-majestic-monolith/">majestic monolith</a>, <a href="https://en.wikipedia.org/wiki/Monorepo">monorepos</a>, and <a href="https://en.wikipedia.org/wiki/React_(JavaScript_library)#Unidirectional_data_flow">flux architecture</a>. These are just some patterns that I can think off from the top of my head, but there are many books that cover programming patterns.</p>
  82. </li>
  83. </ul>
  84. <p>Having listed all these helpful points, I must also add a caution here about not to overuse them and/or apply them in ill-fit contexts (<a href="https://en.wikipedia.org/wiki/Law_of_the_instrument">see: Law of the instrument</a>). Case in point, here’s an article titled <a href="https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction">The Wrong Abstraction</a> where developer Sandi Metz gives an excellent critique of developers' tendency to apply the DRY principle at all costs.</p>
  85. <h2 id="who-maintains-the-maintainers">Who maintains the maintainers? <a href="#who-maintains-the-maintainers" aria-label="Link to section: Who maintains the maintainers?">#</a></h2>
  86. <p>So, I’m not here to argue that we should stop using frameworks and libraries, but we should recognize that using them is a tradeoff where we gain some functionality by incurring some costs. For instance there’s a cost in having to keep up with updates. Codebases that lag behind on updates can become vulnerable to security exploits. Conversely, if you uncritically install the latest updates you open the door to supply-chain attacks where malicious code is inserted into one or more packages.</p>
  87. <p>Beyond security-related costs there’s also the risk that the code you rely on becomes abandon-ware <a href="https://medium.com/@timrwood/moment-endof-term-522d8965689">because an author burns</a> out or <a href="https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c">an in-house team changes focus</a>. Apparently, <a href="https://endoflife.date/">all sorts of stuff is discontinued</a> all the time. And to this you might retort, “it’s open-source we can always fork it.” That’s true, so will you maintain it? I think it’s useful to ask oneself when adding any given dependency what’s the chance of this being abandoned and if so what do I do then, maintain it or switch it out? Libraries tend at least to be more replaceable.</p>
  88. <p>How do we gauge whether some code project is in danger of being abandoned? We tend to look at the recent activity, just <a href="https://github.com/search?q=is+this+dead&amp;type=issues">try searching Github for “is this dead”</a>. Faced with an onslaught of such questions authors can either declare bancrupcy and archive the project, not respond to the issue and fade into oblivion, or put the pedal to the metal by pushing out new features and expanding the code’s scope to solve any problem ever encountered by any user of the code. Succumbing to such a feature-fever quickly leads to breaking changes, necessitating <a href="https://www.sitepoint.com/getting-started-with-codemods/">codemods</a>, documenting upgrade-paths and responding to issues by developers who are desperately trying to keep up with all the changes. Any technology that matures and stabilizes risk being declared “dead” and shunned by a hype-sensitive industry which is something I covered in <a href="https://nilsnh.no/2018/01/13/how-good-code-dies/">How good code dies</a>.</p>
  89. <p>Others' code can also impose a hidden change-cost that makes it painful to upgrade or move away from it. Frameworks and libraries often introduce concepts that are specific to them. In other words all the experience that you build up by using and debugging this tool risk being useless if you decide to move to another framework or library. Bat an eyelash and your framework releases a major version upgrade removing old framework-specific concepts and thereby by making all your hardwon experience irrelevant anyway. I think this cost is particularly insidious especially coupled with <a href="https://en.wikipedia.org/wiki/Sunk_cost">the sunk-cost fallacy</a>.</p>
  90. <h2 id="beyond-libraries-and-frameworks">Beyond libraries and frameworks <a href="#beyond-libraries-and-frameworks" aria-label="Link to section: Beyond libraries and frameworks">#</a></h2>
  91. <p>I believe that in our search for new and better ways to build technology we should not confine our thinking to only looking to libraries and frameworks for answers.</p>
  92. <p>So, what lies beyond?</p>
  93. <p>To start to answer that I’d like to point to some interesting undercurrents I’ve stumbled upon on the web. First, there’s an interesting talk by Adrian Holovaty titled <a href="https://www.youtube.com/watch?v=VvOsegaN9Wk">A framework author’s case against frameworks</a> where he argues that it’s fully feasible to build rich web applications without a framework. Second, I was pleasantly surprised when I read the <a href="https://remix.run/docs/en/v1/pages/philosophy">Remix Run framework’s philosophy</a>:</p>
  94. <blockquote>
  95. <p>We abstract enough to optimize your app’s performance […], without hiding the underlying technology. Learn how to prefetch assets in Remix with links, and you’ve learned how to prefetch assets in any website.</p>
  96. </blockquote>
  97. <p>This quote reads like an antidote to the change-cost I covered above. It’s a plus if learning some technology means learning transferrable knowledge.</p>
  98. <p>Third, I learned about the concept of the <a href="https://tutorials.yax.com/articles/build-websites-the-yax-way/quicktakes/what-is-the-yax-way.html">Stackless Way</a> and read <a href="https://elisehe.in/2021/08/22/using-the-platform">a very interesting article by Elise Hein</a> who explored this nonorthodox idea of going stackless by not having a build-step and not using a framework.</p>
  99. <p>When I <a href="https://nilsnh.no/2022/03/26/building-and-hosting-a-workshop-on-web-components/">built my workshop on web components</a> I also opted to explore how far I could go with a stackless approach. What I found was that our modern browsers are very capable and that I was able to write rich functionality without pulling in external code. It felt quite exhilarating and freeing. Yes, there’s still some rough edges to this approach. However, what’s missing might not be mature frameworks or libraries but rather a cookbook of patterns and principles.</p>
  100. <p>Imagine that, an industry that’s a little less reliant on libraries and frameworks because we invest time into discovering and cultivating patterns and principles. I believe that’s a future with less maintainer burnout, less breaking changes and better, more long-lived programs.</p>
  101. <p>Moving forward you’ll still find me using frameworks and libraries, and maybe I’ll even author some new one’s. Nevertheless, this stackless approach and searching for useful patterns and principles all the while prioritizing transferrable knowledge is something I aim to explore more.</p>
  102. </article>
  103. <hr>
  104. <footer>
  105. <p>
  106. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  107. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  108. </svg> Accueil</a> •
  109. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  110. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  111. </svg> Suivre</a> •
  112. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  113. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  114. </svg> Pro</a> •
  115. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  116. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  117. </svg> Email</a> •
  118. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  119. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  120. </svg> Légal</abbr>
  121. </p>
  122. <template id="theme-selector">
  123. <form>
  124. <fieldset>
  125. <legend><svg class="icon icon-brightness-contrast">
  126. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  127. </svg> Thème</legend>
  128. <label>
  129. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  130. </label>
  131. <label>
  132. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  133. </label>
  134. <label>
  135. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  136. </label>
  137. </fieldset>
  138. </form>
  139. </template>
  140. </footer>
  141. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  142. <script>
  143. function loadThemeForm(templateName) {
  144. const themeSelectorTemplate = document.querySelector(templateName)
  145. const form = themeSelectorTemplate.content.firstElementChild
  146. themeSelectorTemplate.replaceWith(form)
  147. form.addEventListener('change', (e) => {
  148. const chosenColorScheme = e.target.value
  149. localStorage.setItem('theme', chosenColorScheme)
  150. toggleTheme(chosenColorScheme)
  151. })
  152. const selectedTheme = localStorage.getItem('theme')
  153. if (selectedTheme && selectedTheme !== 'undefined') {
  154. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  155. }
  156. }
  157. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  158. window.addEventListener('load', () => {
  159. let hasDarkRules = false
  160. for (const styleSheet of Array.from(document.styleSheets)) {
  161. let mediaRules = []
  162. for (const cssRule of styleSheet.cssRules) {
  163. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  164. continue
  165. }
  166. // WARNING: Safari does not have/supports `conditionText`.
  167. if (cssRule.conditionText) {
  168. if (cssRule.conditionText !== prefersColorSchemeDark) {
  169. continue
  170. }
  171. } else {
  172. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  173. continue
  174. }
  175. }
  176. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  177. }
  178. // WARNING: do not try to insert a Rule to a styleSheet you are
  179. // currently iterating on, otherwise the browser will be stuck
  180. // in a infinite loop…
  181. for (const mediaRule of mediaRules) {
  182. styleSheet.insertRule(mediaRule.cssText)
  183. hasDarkRules = true
  184. }
  185. }
  186. if (hasDarkRules) {
  187. loadThemeForm('#theme-selector')
  188. }
  189. })
  190. </script>
  191. </body>
  192. </html>