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

index.html 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="en">
  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>Your tech stack is not the product (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. <!-- Is that even respected? Retrospectively? What a shAItshow…
  28. https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
  29. <meta name="robots" content="noai, noimageai">
  30. <!-- Documented, feel free to shoot an email. -->
  31. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  32. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  33. <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>
  34. <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>
  35. <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>
  36. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  37. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  38. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  39. <script>
  40. function toggleTheme(themeName) {
  41. document.documentElement.classList.toggle(
  42. 'forced-dark',
  43. themeName === 'dark'
  44. )
  45. document.documentElement.classList.toggle(
  46. 'forced-light',
  47. themeName === 'light'
  48. )
  49. }
  50. const selectedTheme = localStorage.getItem('theme')
  51. if (selectedTheme !== 'undefined') {
  52. toggleTheme(selectedTheme)
  53. }
  54. </script>
  55. <meta name="robots" content="noindex, nofollow">
  56. <meta content="origin-when-cross-origin" name="referrer">
  57. <!-- Canonical URL for SEO purposes -->
  58. <link rel="canonical" href="https://hoho.com/posts/your-stack-is-not-the-product/">
  59. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  60. <article>
  61. <header>
  62. <h1>Your tech stack is not the product</h1>
  63. </header>
  64. <nav>
  65. <p class="center">
  66. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  67. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  68. </svg> Accueil</a> •
  69. <a href="https://hoho.com/posts/your-stack-is-not-the-product/" title="Lien vers le contenu original">Source originale</a>
  70. <br>
  71. Mis en cache le 2024-01-09
  72. </p>
  73. </nav>
  74. <hr>
  75. <p>If you are the technical co-founder or early engineering lead at a startup, and you want to talk about your microservices, hand-rolled CI/CD, in-house monitoring stack, or any other unique part of your stack, I will say: Cool. Let’s riff. Take me deep, I’m ready.</p>
  76. <p>But there’s something I’m likely to tell you in return, something I’ll probably insist you’re overlooking and need to internalize as soon as possible: <em>Your technology stack is not the product.</em></p>
  77. <p>A mindset of technology being the means, not the end, is uncomfortable. But it will help you stay focused on what matters most (the product and your customers), avoid wasteful misadventures, and maximize the company’s chance of success.</p>
  78. <h2 id="the-typical-startup">The typical startup<a hidden class="anchor" aria-hidden="true" href="#the-typical-startup">#</a></h2>
  79. <p>At most software startups, customers typically don’t care if your product runs on Heroku, Kubernetes, or a really brittle singly-homed machine in Joe’s closet. No purchasing decisions hinge on your commitment to write servers in Rust or use Nix for hermetic everything. And although they might exist, I have sadly never had a customer write a testimonial for the elegant collection of internal services involved in responding to that single HTTP request.</p>
  80. <p>No; customers are not paying for, nor give a shit about, these things. Sorry. It’s still cool stuff. It’s just not what you’re selling.<sup id="fnref:1"></sup></p>
  81. <p>Customers want software that delivers problem-solving <em>impact</em>. And at the early stage, which is all the way until you’ve reached product-market fit, they’re almost certainly not getting enough, fast enough. You should be spending as much time as you can at this level of the stack, The Product: thinking, building, learning.</p>
  82. <p>So what then, is that it? Is Kube always silly and Joe’s closet machine always good? Am I saying just YOLO it? Of course not.</p>
  83. <p>There are still better and worse decisions. Being able to make good decisions, ones that maximize product impact, starts with <em>intentionality</em>: realizing how and when you as a leader are actually making them.</p>
  84. <h2 id="recognize-implicit-decisions">Recognize implicit decisions<a hidden class="anchor" aria-hidden="true" href="#recognize-implicit-decisions">#</a></h2>
  85. <p>Many of the most consequential technology decisions happen <em>implicitly</em>, without even realizing a decision is being made. This is especially true when the team is small and everything is a green field. For example:</p>
  86. <ul><li>You’ve used Go before, so you start writing the stack in it. Suddenly you’re a “Go shop”.</li><li>You were curious to play with Kubernetes<sup id="fnref:2"></sup>, and got Hello World working, so since nothing else is around it becomes the <em>de facto</em> serving environment.</li><li>You are coming from a much later-stage and/or higher-scale company, so approach problems for a scale the company does not yet have.</li></ul>
  87. <p>These decisions glide through so easily because they feel good, fun, and natural. There’s probably nobody else around to notice! And they can often be the right decisions.</p>
  88. <p>But there are many instances where an innocent, ill-considered early decision turned out much worse over the longer term. It becomes a time-sucking, success-hindering mess requiring costly correction later. I’ve certainly been responsible for my share of them.</p>
  89. <p>How do we get out of making these sort of “just because” decisions, and increase our chances of success?</p>
  90. <h2 id="set-overarching-goals">Set overarching goals<a hidden class="anchor" aria-hidden="true" href="#set-overarching-goals">#</a></h2>
  91. <p>A good technology decision—one that is more than a “just because”—is decision that can be connected to an overarching long-term goal. As a technology team, why do we exist?</p>
  92. <p>Set technology goals, early, which answer that question and which will be used guide technology decisions. Here’s a starting set:</p>
  93. <blockquote><p>Our Technology Goals</p><ol><li><strong>Ship the product.</strong> Frequently and reliably.</li><li><strong>Support growth.</strong> Be able to bring in more people, gradually, that can do (1).</li></ol></blockquote>
  94. <p>These might seem unrealistically simple, but it’s pretty easy for me to connect all variety of technical decisions to these.</p>
  95. <ul><li>If the system is unreliable, we won’t ship as much product.</li><li>If we’re constantly building/maintaining infrastructure, we won’t ship as much product.</li><li>If we pick exotic technology, it’ll be harder to hire (and we won’t ship as much product).</li></ul>
  96. <p>They work well at a later stage, too:</p>
  97. <ul><li>If we don’t hire a dedicated SRE, product builders will spend more time maintaining infra, and we won’t ship as much.</li><li>If we don’t replace this system, more and more customers will experience a broken product, and the costs to fix will grow.</li></ul>
  98. <p>Best of all, by keeping “the stack” out of the picture, these goals make it clear where your brainpower is best focused: on keeping the product moving and improving. The stack is but a means to that end.</p>
  99. <p>Now you can start evaluating some of those concrete decisions the same way:</p>
  100. <ul><li>Why is Joe’s closet computer a bad choice? Because it’s a single point of failure and we won’t be able to ship fast if it breaks, which it will.</li><li>Why am I building in Python and not Arc? Because I know the language and when we start hiring, there are a lot of others who do, too.</li><li>Why are we building a monolith? Because the product isn’t at the scale where anything benefits from more services, and building more tooling would cut into the time we have for product features.</li></ul>
  101. <p>Goal alignment moves the “why” behind your decisions from “because I’m awesome and have great instincts” to “it’s what we need, now”. (You are still awesome.)</p>
  102. <p>Still, there will be situations where multiple options would seem to meet the goal. How do you make a choice when there is not clear winner?</p>
  103. <h2 id="establish-shared-values">Establish shared values<a hidden class="anchor" aria-hidden="true" href="#establish-shared-values">#</a></h2>
  104. <p>Goals alone tell us what we want to achieve, but not how. Pair your technology goals with a set of <em>values</em> that accelerate the process of picking “the how”.</p>
  105. <p>I usually start with a set something like this:</p>
  106. <ol><li><strong>No sacred bits: Launch, learn, iterate.</strong> It’s expected we’ll throw things out and outgrow past decisions. And some of our product ideas won’t succeed no matter how well-built the software was. We value <em>getting it out the door</em> over <em>getting it perfect</em>, and we will quickly and unemotionally change our approach when new information supports it.</li><li><strong>Today’s bets over tomorrow’s theoreticals.</strong> We do our best to anticipate future needs, but we place the most emphasis on building what we <em>know</em> we need today.</li><li><strong>Favor “boring technology” and in-house expertise.</strong> We move fastest when we know our tools well, not just their strengths but also their limitations. The ideal tools are the ones we’re already expert in, or at least can learn to proficiency quickly, even if they have some drawbacks.</li><li><strong>Buy non-core competencies whenever prudent.</strong> If we need something we don’t have, <em>and it’s not something this company needs to become a leader in</em>, buy it whenever practical (i.e. affordable and easily integrated).</li></ol>
  107. <p>You don’t have to use my values as long as you set <em>some</em><sup id="fnref:3"></sup>. There are plenty of examples out there. An old friend turned me on to <a href="https://medium.com/the-ready/even-overs-the-prioritization-tool-that-brings-your-strategy-to-life-e4f28f2949ac">“even-over” values</a>, which put two virtues in tension (to emphasize the primacy of the first). Someone even built <a href="https://www.keyvalues.com/">an aggregator of values by company</a> so you can select your next job accordingly. Go be you. Have fun with it.</p>
  108. <p>The important thing values should do is create cultural license for breaking habits and instincts that might send us in the wrong direction. <em>Make it okay</em> to say “we’re not Google”, or, “we’ll solve that later”.</p>
  109. <h2 id="you-still-set-and-edit-strategy">You still set, and edit, strategy<a hidden class="anchor" aria-hidden="true" href="#you-still-set-and-edit-strategy">#</a></h2>
  110. <p>Panicking about the “simplicity” of the stack this would lead to? Worried that when you reach scale, the PaaS bill will be through the roof? Upset that you’ll have nothing crazy to blog about for years?</p>
  111. <p>Relax. You will still own the technology strategy. You will be making decisions continuously, and have both the right and the obligation to make changes when needed.</p>
  112. <p>But your stack is unlikely to ever be the “main character” your customers rave about it. At best, only its qualities are what stand out. That’s a good thing. Feel good about keeping it that way.</p>
  113. </article>
  114. <hr>
  115. <footer>
  116. <p>
  117. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  118. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  119. </svg> Accueil</a> •
  120. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  121. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  122. </svg> Suivre</a> •
  123. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  124. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  125. </svg> Pro</a> •
  126. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  127. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  128. </svg> Email</a> •
  129. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  130. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  131. </svg> Légal</abbr>
  132. </p>
  133. <template id="theme-selector">
  134. <form>
  135. <fieldset>
  136. <legend><svg class="icon icon-brightness-contrast">
  137. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  138. </svg> Thème</legend>
  139. <label>
  140. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  141. </label>
  142. <label>
  143. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  144. </label>
  145. <label>
  146. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  147. </label>
  148. </fieldset>
  149. </form>
  150. </template>
  151. </footer>
  152. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  153. <script>
  154. function loadThemeForm(templateName) {
  155. const themeSelectorTemplate = document.querySelector(templateName)
  156. const form = themeSelectorTemplate.content.firstElementChild
  157. themeSelectorTemplate.replaceWith(form)
  158. form.addEventListener('change', (e) => {
  159. const chosenColorScheme = e.target.value
  160. localStorage.setItem('theme', chosenColorScheme)
  161. toggleTheme(chosenColorScheme)
  162. })
  163. const selectedTheme = localStorage.getItem('theme')
  164. if (selectedTheme && selectedTheme !== 'undefined') {
  165. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  166. }
  167. }
  168. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  169. window.addEventListener('load', () => {
  170. let hasDarkRules = false
  171. for (const styleSheet of Array.from(document.styleSheets)) {
  172. let mediaRules = []
  173. for (const cssRule of styleSheet.cssRules) {
  174. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  175. continue
  176. }
  177. // WARNING: Safari does not have/supports `conditionText`.
  178. if (cssRule.conditionText) {
  179. if (cssRule.conditionText !== prefersColorSchemeDark) {
  180. continue
  181. }
  182. } else {
  183. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  184. continue
  185. }
  186. }
  187. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  188. }
  189. // WARNING: do not try to insert a Rule to a styleSheet you are
  190. // currently iterating on, otherwise the browser will be stuck
  191. // in a infinite loop…
  192. for (const mediaRule of mediaRules) {
  193. styleSheet.insertRule(mediaRule.cssText)
  194. hasDarkRules = true
  195. }
  196. }
  197. if (hasDarkRules) {
  198. loadThemeForm('#theme-selector')
  199. }
  200. })
  201. </script>
  202. </body>
  203. </html>