A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

index.html 15KB

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>
  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>Vendor by default (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_2021-01-20.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>
  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://macwright.com/2021/03/11/vendor-by-default.html">
  55. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Vendor by default</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  63. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  64. </svg> Accueil</a> •
  65. <a href="https://macwright.com/2021/03/11/vendor-by-default.html" title="Lien vers le contenu original">Source originale</a>
  66. </p>
  67. </nav>
  68. <hr>
  69. <p>This isn’t a recommendation. It’s more of a habit that I’ve acquired recently. If this makes its way to Hacker News and people think <em>how could he</em>, my only response will be 🤷, why not.</p>
  70. <p>I’ve been building a thing, in JavaScript, an application sort of like things I’ve built in the past. With the same basic goal of making something useful. How I get to that goal is flexible. But it ends up being a lot of <a href="http://boringtechnology.club">using boring technology</a>, trying not to overthink the easy parts, trying to properly-think the hard parts, and so on. So there’s not that much to write home about that would surprise your usual React/Next.js/JavaScript engineer person.</p>
  71. <p>But one thing that I do think is sort of unusual is: I’m vendoring <em>a lot of stuff</em>.</p>
  72. <p><em>Vendoring</em>, in the programming sense, means “copying the source code of another project into your project.” It’s in contrast to the practice of using dependencies, which would be adding another project’s name to your <code class="language-plaintext highlighter-rouge">package.json</code> file and having npm or yarn download and link it up for you.</p>
  73. <p>Back in 2016, I wrote <a href="https://macwright.com/2016/08/23/optimistic-pessimistic-versioning.html">an article about optimistic and pessimistic versioning, which mentioned vendoring</a> as the ‘most pessimistic’ approach, and that’s roughly true. Vendoring means that you aren’t going to get automatic bugfixes, or new bugs, from dependencies. It means that you don’t have to trust dependency authors at all.</p>
  74. <p>The downsides of vendoring - no automatic updates, a bigger source tree, more code to maintain, it seems weird and unconventional - are probably pretty clear to JavaScripters, and most members of most language families. Languages like C did vendoring by default, in that people would copy header-only libraries into their projects. Go, for a while, was a little like that - you’d copy files in, whether automatically or manually. But nowadays Go modules (to my untrained eye) look a lot like the norms of other languages.</p>
  75. <p>So what are the reasons for doing this? Here’s what’s on my mind.</p>
  76. <h3 id="i-read-dependencies-anyway">I read dependencies anyway</h3>
  77. <p>I read a lot of other people’s code. I highly recommend it. One of my golden rules is that you shouldn’t <a href="https://en.wikipedia.org/wiki/Black_box">blackbox</a> things you don’t need to. I like to “use dependencies for efficiency, not ignorance.”</p>
  78. <p>When I’m vendoring code - copying it into the project and making it pass my basic eslint &amp; testing standards, I’ll do light rewrites and refactors of new code, allowing me to get a deeper understanding of how they work and where their limits lie.</p>
  79. <p>Obviously, other people’s code is their code. I didn’t do 100% of the thinking that led to it - I’m probably doing 5% of it. But absorbing that bit into my mind, instead of seeing only the external API surface, pays dividends.</p>
  80. <p>And sometimes, sure - I’ll read through a dependency, start refactoring, and realize that it’s going to be simpler to write it myself, or I should find another option. It doesn’t matter if something is a dependency or my code: when you ship a product, it’s all your responsibility.</p>
  81. <h3 id="you-dont-really-use-most-of-your-dependencies">You don’t really use most of your dependencies</h3>
  82. <p>Vendoring makes it very obvious that many dependencies have lots of surface area - API calls and methods - that you don’t use. This hooks into the debate about tiny modules (less surface area! less waste! way harder to maintain!), or big ones, but for this article, the point is that you can pare down dependencies into their ideal form in your application.</p>
  83. <p><a href="https://webpack.js.org/guides/tree-shaking/">Dead code elimination</a> helps! But it can only do so much, and the dead code <em>still exists in your project.</em> Even the most advanced DCE isn’t going to pare down methods of a class that you’re using. Only some DCE systems can handle CommonJS, and most work on the level of individual variables, not <em>parts</em> of those variables.</p>
  84. <p>To go a step further, you can trim the test suite down to the API surface you use, run coverage, and comment out or remove the parts that are dead.</p>
  85. <h3 id="old-projects-stabilize">Old projects stabilize</h3>
  86. <p>Old projects might not be stable. They might have bugs - they usually do. In every issue tracker, there’s at least one multi-year bug that is so wicked that it might never go away.</p>
  87. <p>But old projects have <em>stabilized</em>. They don’t change that often, and when they do the changes are minor. So the value that you got out of that fast release cycle two years ago doesn’t apply. And worse - you have a lot of old patterns that the project probably can’t remove! Do you want IE10 workarounds in your project? If you’re using a lot of third-party dependencies, you’re probably inheriting polyfills for stuff like <code class="language-plaintext highlighter-rouge">Array.includes</code>.</p>
  88. <p>In my brief rewrites, I can pluck out old-fashioned polyfills for <code class="language-plaintext highlighter-rouge">isArray</code> or code that checks that <code class="language-plaintext highlighter-rouge">JSON.parse</code> really exists in this environment. In 2021, it does.</p>
  89. <h3 id="its-sort-of-good-for-open-source">It’s sort of good for open source</h3>
  90. <p>This is a mixed bag. My vendored copy might float far away from the original, and contributing back the changes in full isn’t going to be useful. A lot of the changes that I make are going to be somewhat cosmetic or things like adding TypeScript support for things that didn’t have it.</p>
  91. <p>But on the other hand, porting a fix from my vendored copy to the original is going to be pretty quick, and having dependencies in the source tree itself is an ideal scenario for fixing bugs in vendored code quickly.</p>
  92. <p>Dependencies that are GPL’ed (there aren’t many, but the license still lingers) will get released when the thing is released, because the law requires it - though those changes won’t be especially exciting. Things that are MIT/BSD/ISC licensed will mostly get upstream fixes, for the good of the community.</p>
  93. <h3 id="not-everything">Not <em>everything</em></h3>
  94. <p>I don’t vendor React or Webpack or other large, incredibly complex dependencies. They’re going to change quickly and there’s little I can really contribute to those projects. I’m happy to keep <code class="language-plaintext highlighter-rouge">lodash</code> as a dependency for a few extremely battle-tested utilities. But for small to medium-sized dependencies, vendoring makes a lot of sense.</p>
  95. </article>
  96. <hr>
  97. <footer>
  98. <p>
  99. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  100. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  101. </svg> Accueil</a> •
  102. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  103. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  104. </svg> RSS</a> •
  105. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  106. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  107. </svg> Pro</a> •
  108. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  109. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  110. </svg> Email</a> •
  111. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  112. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  113. </svg> Légal</abbr>
  114. </p>
  115. <template id="theme-selector">
  116. <form>
  117. <fieldset>
  118. <legend><svg class="icon icon-brightness-contrast">
  119. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  120. </svg> Thème</legend>
  121. <label>
  122. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  123. </label>
  124. <label>
  125. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  126. </label>
  127. <label>
  128. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  129. </label>
  130. </fieldset>
  131. </form>
  132. </template>
  133. </footer>
  134. <script>
  135. function loadThemeForm(templateName) {
  136. const themeSelectorTemplate = document.querySelector(templateName)
  137. const form = themeSelectorTemplate.content.firstElementChild
  138. themeSelectorTemplate.replaceWith(form)
  139. form.addEventListener('change', (e) => {
  140. const chosenColorScheme = e.target.value
  141. localStorage.setItem('theme', chosenColorScheme)
  142. toggleTheme(chosenColorScheme)
  143. })
  144. const selectedTheme = localStorage.getItem('theme')
  145. if (selectedTheme && selectedTheme !== 'undefined') {
  146. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  147. }
  148. }
  149. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  150. window.addEventListener('load', () => {
  151. let hasDarkRules = false
  152. for (const styleSheet of Array.from(document.styleSheets)) {
  153. let mediaRules = []
  154. for (const cssRule of styleSheet.cssRules) {
  155. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  156. continue
  157. }
  158. // WARNING: Safari does not have/supports `conditionText`.
  159. if (cssRule.conditionText) {
  160. if (cssRule.conditionText !== prefersColorSchemeDark) {
  161. continue
  162. }
  163. } else {
  164. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  165. continue
  166. }
  167. }
  168. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  169. }
  170. // WARNING: do not try to insert a Rule to a styleSheet you are
  171. // currently iterating on, otherwise the browser will be stuck
  172. // in a infinite loop…
  173. for (const mediaRule of mediaRules) {
  174. styleSheet.insertRule(mediaRule.cssText)
  175. hasDarkRules = true
  176. }
  177. }
  178. if (hasDarkRules) {
  179. loadThemeForm('#theme-selector')
  180. }
  181. })
  182. </script>
  183. </body>
  184. </html>