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.

4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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>Choose Boring Technology (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_2020-06-19.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 type="text/javascript">
  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://mcfunley.com/choose-boring-technology">
  55. <body class="remarkdown h1-underline h2-underline h3-underline hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Choose Boring Technology</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  63. <a href="https://mcfunley.com/choose-boring-technology" title="Lien vers le contenu original">Source originale</a>
  64. </p>
  65. </nav>
  66. <hr>
  67. <main>
  68. <p>Probably the single best thing to happen to me in my career was having had <a href="http://laughingmeme.org/">Kellan</a> placed in charge of me. I stuck around long enough to see Kellan’s technical decisionmaking start to bear fruit. I learned a great deal <em>from</em> this, but I also learned a great deal as a <em>result</em> of this. I would not have been free to become the engineer that wrote <a href="/data-driven-products-lean-startup-2014">Data Driven Products Now!</a> if Kellan had not been there to so thoroughly stick the landing on technology choices.</p>
  69. <figure>
  70. <img src="http://i.imgur.com/FRQKLCy.jpg"/>
  71. <figcaption>Being inspirational as always.</figcaption>
  72. </figure>
  73. <p>In the year since leaving Etsy, I’ve resurrected my ability to care about technology. And my thoughts have crystallized to the point where I can write them down coherently. What follows is a distillation of the Kellan gestalt, which will hopefully serve to horrify him only slightly.</p>
  74. <h5 id="embrace-boredom">Embrace Boredom.</h5>
  75. <p>Let’s say every company gets about three innovation tokens. You can spend these however you want, but the supply is fixed for a long while. You might get a few more <em>after</em> you achieve a <a href="http://rc3.org/2015/03/24/the-pleasure-of-building-big-things/">certain level of stability and maturity</a>, but the general tendency is to overestimate the contents of your wallet. Clearly this model is approximate, but I think it helps.</p>
  76. <p>If you choose to write your website in NodeJS, you just spent one of your innovation tokens. If you choose to use <a href="/why-mongodb-never-worked-out-at-etsy">MongoDB</a>, you just spent one of your innovation tokens. If you choose to use <a href="https://consul.io/">service discovery tech that’s existed for a year or less</a>, you just spent one of your innovation tokens. If you choose to write your own database, oh god, you’re in trouble.</p>
  77. <p>Any of those choices might be sensible if you’re a javascript consultancy, or a database company. But you’re probably not. You’re probably working for a company that is at least ostensibly <a href="https://www.etsy.com">rethinking global commerce</a> or <a href="https://stripe.com">reinventing payments on the web</a> or pursuing some other suitably epic mission. In that context, devoting any of your limited attention to innovating ssh is an excellent way to fail. Or at best, delay success .</p>
  78. <p>What counts as boring? That’s a little tricky. “Boring” should not be conflated with “bad.” There is technology out there that is both boring and bad <a ref="#f2" href="#f2">[2]</a>. You should not use any of that. But there are many choices of technology that are boring and good, or at least good enough. MySQL is boring. Postgres is boring. PHP is boring. Python is boring. Memcached is boring. Squid is boring. Cron is boring.</p>
  79. <p>The nice thing about boringness (so constrained) is that the capabilities of these things are well understood. But more importantly, their failure modes are well understood. Anyone who knows me well will understand that it’s only with a overwhelming sense of malaise that I now invoke the spectre of Don Rumsfeld, but I must.</p>
  80. <figure>
  81. <img src="http://i.imgur.com/n8ElWr3.jpg"/>
  82. <figcaption>To be clear, fuck this guy.</figcaption>
  83. </figure>
  84. <p>When choosing technology, you have both known unknowns and unknown unknowns .</p>
  85. <ul>
  86. <li>A known unknown is something like: <em>we don’t know what happens when this database hits 100% CPU.</em></li>
  87. <li>An unknown unknown is something like: <em>geez it didn’t even occur to us that <a href="http://www.evanjones.ca/jvm-mmap-pause.html">writing stats would cause GC pauses</a>.</em></li>
  88. </ul>
  89. <p>Both sets are typically non-empty, even for tech that’s existed for decades. But for shiny new technology the magnitude of unknown unknowns is significantly larger, and this is important.</p>
  90. <h5 id="optimize-globally">Optimize Globally.</h5>
  91. <p>I unapologetically think a bias in favor of boring technology is a good thing, but it’s not the only factor that needs to be considered. Technology choices don’t happen in isolation. They have a scope that touches your entire team, organization, and the system that emerges from the sum total of your choices.</p>
  92. <p>Adding technology to your company comes with a cost. As an abstract statement this is obvious: if we’re already using Ruby, adding Python to the mix doesn’t feel sensible because the resulting complexity would outweigh Python’s marginal utility. But somehow when we’re talking about Python and Scala or MySQL and Redis people <a href="http://martinfowler.com/bliki/PolyglotPersistence.html">lose their minds</a>, discard all constraints, and start raving about using the best tool for the job.</p>
  93. <p><a href="https://twitter.com/coda/status/580531932393504768">Your function in a nutshell</a> is to map business problems onto a solution space that involves choices of software. If the choices of software were truly without baggage, you could indeed pick a whole mess of locally-the-best tools for your assortment of problems.</p>
  94. <figure>
  95. <svg viewbox="0 0 423 420" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
  96. <title>Crazy</title>
  97. <desc>Created with Sketch.</desc>
  98. <defs/>
  99. <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
  100. <g id="Crazy" sketch:type="MSLayerGroup" transform="translate(1.000000, -4.000000)">
  101. <ellipse id="Solutions" stroke="#979797" sketch:type="MSShapeGroup" cx="341.5" cy="229.5" rx="79.5" ry="193.5"/>
  102. <ellipse id="Problems" stroke="#979797" sketch:type="MSShapeGroup" cx="79.5" cy="229.5" rx="79.5" ry="193.5"/>
  103. <g id="arrows" transform="translate(45.000000, 77.000000)" stroke="#D0011B" stroke-width="3" fill="#D0011B" stroke-linecap="square">
  104. <path d="M19.5,26.5 L255.502121,26.5" id="Line" sketch:type="MSShapeGroup"/>
  105. <path id="Line-decoration-1" d="M255.5,26.5 C251.72,25.45 248.48,24.55 244.7,23.5 C244.7,25.6 244.7,27.4 244.7,29.5 C248.48,28.45 251.72,27.55 255.5,26.5 C255.5,26.5 255.5,26.5 255.5,26.5 Z"/>
  106. <path d="M19.5,26.5 L245.5,84.5" id="Line-2" sketch:type="MSShapeGroup"/>
  107. <path id="Line-2-decoration-1" d="M245.186355,84.419507 C241.786016,82.4628271 238.87144,80.7856729 235.471101,78.8289931 C234.94908,80.8630761 234.501633,82.6065758 233.979612,84.6406589 C237.901972,84.5632557 241.263995,84.4969101 245.186355,84.419507 C245.186355,84.419507 245.186355,84.419507 245.186355,84.419507 Z"/>
  108. <path d="M19.5,26.5 L299.5,0.5" id="Line-3" sketch:type="MSShapeGroup"/>
  109. <path id="Line-3-decoration-1" d="M299.296324,0.518912741 C295.435434,-0.177093062 292.126099,-0.773669465 288.265208,-1.46967527 C288.459373,0.621329291 288.6258,2.41361891 288.819965,4.50462347 C292.486691,3.10962472 295.629598,1.9139115 299.296324,0.518912741 C299.296324,0.518912741 299.296324,0.518912741 299.296324,0.518912741 Z"/>
  110. <path d="M19.5,26.5 L255.502121,26.5" id="Line-4" sketch:type="MSShapeGroup"/>
  111. <path id="Line-4-decoration-1" d="M255.5,26.5 C251.72,25.45 248.48,24.55 244.7,23.5 C244.7,25.6 244.7,27.4 244.7,29.5 C248.48,28.45 251.72,27.55 255.5,26.5 C255.5,26.5 255.5,26.5 255.5,26.5 Z"/>
  112. <path d="M63.5,79.5 L256.5,34.5" id="Line-5" sketch:type="MSShapeGroup"/>
  113. <path id="Line-5-decoration-1" d="M256.327927,34.5401208 C252.408243,34.3758734 249.048513,34.2350899 245.128829,34.0708426 C245.605677,36.1159872 246.014403,37.8689684 246.49125,39.9141131 C249.934087,38.0332157 252.88509,36.4210181 256.327927,34.5401208 C256.327927,34.5401208 256.327927,34.5401208 256.327927,34.5401208 Z"/>
  114. <path d="M63.5,79.5 L301.5,116.5" id="Line-6" sketch:type="MSShapeGroup"/>
  115. <path id="Line-6-decoration-1" d="M300.651315,116.368062 C297.077479,114.749853 294.014192,113.362816 290.440356,111.744607 C290.117761,113.819681 289.84125,115.598316 289.518655,117.67339 C293.415086,117.216525 296.754884,116.824927 300.651315,116.368062 C300.651315,116.368062 300.651315,116.368062 300.651315,116.368062 Z"/>
  116. <path d="M63.5,79.5 L254.5,209.5" id="Line-7" sketch:type="MSShapeGroup"/>
  117. <path id="Line-7-decoration-1" d="M254.464216,209.475644 C251.930146,206.480751 249.758085,203.9137 247.224014,200.918806 C246.042418,202.654845 245.02962,204.142878 243.848024,205.878916 C247.563691,207.137771 250.748549,208.216789 254.464216,209.475644 C254.464216,209.475644 254.464216,209.475644 254.464216,209.475644 Z"/>
  118. <path d="M0.5,115.5 L251.5,216.5" id="Line-8" sketch:type="MSShapeGroup"/>
  119. <path id="Line-8-decoration-1" d="M250.981706,216.291443 C247.866929,213.906268 245.19712,211.861831 242.082342,209.476656 C241.298409,211.424847 240.626466,213.094725 239.842533,215.042916 C243.741243,215.4799 247.082995,215.854459 250.981706,216.291443 C250.981706,216.291443 250.981706,216.291443 250.981706,216.291443 Z"/>
  120. <path d="M54.5,176.5 L300.5,193.5" id="Line-10" sketch:type="MSShapeGroup"/>
  121. <path id="Line-10-decoration-1" d="M299.914697,193.459552 C296.216079,192.151452 293.045835,191.030224 289.347217,189.722124 C289.202441,191.817128 289.078346,193.612845 288.93357,195.707849 C292.776964,194.920945 296.071303,194.246456 299.914697,193.459552 C299.914697,193.459552 299.914697,193.459552 299.914697,193.459552 Z"/>
  122. <path d="M54.5,176.5 L288.5,273.5" id="Line-11" sketch:type="MSShapeGroup"/>
  123. <path id="Line-11-decoration-1" d="M288.215373,273.382013 C285.125578,270.964562 282.477183,268.892461 279.387389,266.47501 C278.58323,268.41494 277.89395,270.077737 277.089791,272.017667 C280.983745,272.495188 284.321419,272.904492 288.215373,273.382013 C288.215373,273.382013 288.215373,273.382013 288.215373,273.382013 Z"/>
  124. <path d="M11.5,231.5 L287.5,283.5" id="Line-12" sketch:type="MSShapeGroup"/>
  125. <path id="Line-12-decoration-1" d="M286.658962,283.341544 C283.138722,281.609837 280.121373,280.125516 276.601133,278.393809 C276.212321,280.457502 275.879054,282.226381 275.490243,284.290073 C279.399294,283.958088 282.74991,283.673529 286.658962,283.341544 C286.658962,283.341544 286.658962,283.341544 286.658962,283.341544 Z"/>
  126. <path d="M11.5,231.5 L249.5,223.5" id="Line-13" sketch:type="MSShapeGroup"/>
  127. <path id="Line-13-decoration-1" d="M249.36566,223.504516 C245.552519,222.582095 242.284113,221.79145 238.470973,220.869029 C238.541521,222.967844 238.601991,224.766828 238.67254,226.865643 C242.415132,225.689248 245.623068,224.68091 249.36566,223.504516 C249.36566,223.504516 249.36566,223.504516 249.36566,223.504516 Z"/>
  128. <path d="M0.5,115.5 L248.5,156.5" id="Line-9" sketch:type="MSShapeGroup"/>
  129. <path id="Line-9-decoration-1" d="M248.138638,156.440259 C244.580524,154.78777 241.530711,153.371351 237.972596,151.718862 C237.630068,153.790739 237.336473,155.566633 236.993945,157.63851 C240.894588,157.219122 244.237996,156.859647 248.138638,156.440259 C248.138638,156.440259 248.138638,156.440259 248.138638,156.440259 Z"/>
  130. </g>
  131. <g id="problems" transform="translate(33.000000, 91.000000)" stroke="#979797" fill="#4990E2" sketch:type="MSShapeGroup">
  132. <circle id="Oval-3" cx="30" cy="14" r="14"/>
  133. <circle id="Oval-4" cx="74" cy="66" r="14"/>
  134. <circle id="Oval-5" cx="14" cy="103" r="14"/>
  135. <circle id="Oval-6" cx="64" cy="163" r="14"/>
  136. <circle id="Oval-7" cx="23" cy="219" r="14"/>
  137. </g>
  138. <g id="Solutions" transform="translate(293.000000, 68.000000)" stroke="#979797" fill="#7ED321" sketch:type="MSShapeGroup">
  139. <circle id="Oval-8" cx="26" cy="37" r="14"/>
  140. <circle id="Oval-9" cx="74" cy="69" r="14"/>
  141. <circle id="Oval-10" cx="14" cy="99" r="14"/>
  142. <circle id="Oval-11" cx="71" cy="129" r="14"/>
  143. <circle id="Oval-12" cx="18" cy="168" r="14"/>
  144. <circle id="Oval-13" cx="71" cy="205" r="14"/>
  145. <circle id="Oval-14" cx="22" cy="229" r="14"/>
  146. <circle id="Oval-15" cx="66" cy="14" r="14"/>
  147. <circle id="Oval-16" cx="58" cy="289" r="14"/>
  148. </g>
  149. <text id="Problems" sketch:type="MSTextLayer" font-family="Lato" font-size="18" font-weight="normal" fill="#000000">
  150. <tspan x="43" y="18">Problems</tspan>
  151. </text>
  152. <text id="Technical-Solutions" sketch:type="MSTextLayer" font-family="Lato" font-size="18" font-weight="normal" fill="#000000">
  153. <tspan x="262" y="18">Technical Solutions</tspan>
  154. </text>
  155. </g>
  156. </g>
  157. </svg>
  158. <figcaption class="text-center">The way you might choose technology in a world where choices are cheap: "pick the right tool for the job."</figcaption>
  159. </figure>
  160. <p>But of course, the baggage exists. We call the baggage “operations” and to a lesser extent “cognitive overhead.” You have to monitor the thing. You have to figure out unit tests. You need to know the first thing about it to hack on it. You need an init script. I could go on for days here, and all of this adds up fast.</p>
  161. <figure>
  162. <svg viewbox="0 0 423 420" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
  163. <title>Sane</title>
  164. <desc>Created with Sketch.</desc>
  165. <defs/>
  166. <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
  167. <g id="Sane" sketch:type="MSLayerGroup" transform="translate(1.000000, -4.000000)">
  168. <ellipse id="Solutions-3" stroke="#979797" sketch:type="MSShapeGroup" cx="341.5" cy="229.5" rx="79.5" ry="193.5"/>
  169. <ellipse id="Problems-2" stroke="#979797" sketch:type="MSShapeGroup" cx="79.5" cy="229.5" rx="79.5" ry="193.5"/>
  170. <g id="arrows" transform="translate(51.000000, 102.000000)" stroke="#D0011B" stroke-width="3" fill="#D0011B" stroke-linecap="square">
  171. <path d="M13.5,1.5 L249.5,1.5" id="Line-14" sketch:type="MSShapeGroup"/>
  172. <path id="Line-14-decoration-1" d="M249.5,1.5 C245.72,0.45 242.48,-0.45 238.7,-1.5 C238.7,0.6 238.7,2.4 238.7,4.5 C242.48,3.45 245.72,2.55 249.5,1.5 C249.5,1.5 249.5,1.5 249.5,1.5 Z"/>
  173. <path d="M13.5,1.5 L248.5,120.5" id="Line-15" sketch:type="MSShapeGroup"/>
  174. <path id="Line-15-decoration-1" d="M248.132239,120.313772 C245.23431,117.669362 242.75037,115.402724 239.852441,112.758314 C238.903738,114.631803 238.090564,116.237651 237.141861,118.111141 C240.988493,118.882062 244.285607,119.542851 248.132239,120.313772 C248.132239,120.313772 248.132239,120.313772 248.132239,120.313772 Z"/>
  175. <path d="M57.5,54.5 L249.5,8.5" id="Line-17" sketch:type="MSShapeGroup"/>
  176. <path id="Line-17-decoration-1" d="M249.078398,8.6010088 C245.157787,8.46060711 241.797264,8.34026282 237.876654,8.19986114 C238.365932,10.2420674 238.785314,11.9925299 239.274592,14.0347362 C242.705924,12.1329316 245.647066,10.5028134 249.078398,8.6010088 C249.078398,8.6010088 249.078398,8.6010088 249.078398,8.6010088 Z"/>
  177. <path d="M0.5,92.5 L240.5,137.5" id="Line-20" sketch:type="MSShapeGroup"/>
  178. <path id="Line-20-decoration-1" d="M240.320814,137.466403 C236.79906,135.737776 233.780414,134.256096 230.25866,132.52747 C229.871654,134.591501 229.539934,136.360671 229.152928,138.424703 C233.061688,138.089298 236.412054,137.801808 240.320814,137.466403 C240.320814,137.466403 240.320814,137.466403 240.320814,137.466403 Z"/>
  179. <path d="M57.5,52.5 L242.5,129.5" id="Line-18" sketch:type="MSShapeGroup"/>
  180. <path id="Line-18-decoration-1" d="M242.1449,129.352202 C239.058585,126.930309 236.413173,124.854402 233.326858,122.432509 C232.51991,124.371281 231.828241,126.033085 231.021292,127.971856 C234.914555,128.454977 238.251637,128.869081 242.1449,129.352202 C242.1449,129.352202 242.1449,129.352202 242.1449,129.352202 Z"/>
  181. <path d="M13.5,1.5 L248.5,183.5" id="Line-16" sketch:type="MSShapeGroup"/>
  182. <path id="Line-16-decoration-1" d="M248.313733,183.355742 C245.968119,180.211065 243.957592,177.515627 241.611978,174.37095 C240.32613,176.031249 239.223974,177.454363 237.938125,179.114662 C241.569588,180.59904 244.68227,181.871364 248.313733,183.355742 C248.313733,183.355742 248.313733,183.355742 248.313733,183.355742 Z"/>
  183. <path d="M0.5,92.5 L253.5,15.5" id="Line-19" sketch:type="MSShapeGroup"/>
  184. <path id="Line-19-decoration-1" d="M253.061904,15.6333334 C249.139957,15.7294168 245.778289,15.8117739 241.856342,15.9078572 C242.467781,17.9168724 242.991872,19.6388854 243.603311,21.6479005 C246.913819,19.542802 249.751397,17.7384319 253.061904,15.6333334 C253.061904,15.6333334 253.061904,15.6333334 253.061904,15.6333334 Z"/>
  185. <path d="M0.5,92.5 L244.5,191.5" id="Line-21" sketch:type="MSShapeGroup"/>
  186. <path id="Line-21-decoration-1" d="M244.204221,191.379991 C241.09632,188.985863 238.432405,186.933753 235.324504,184.539624 C234.534968,186.485551 233.858223,188.153489 233.068687,190.099416 C236.966124,190.547618 240.306784,190.93179 244.204221,191.379991 C244.204221,191.379991 244.204221,191.379991 244.204221,191.379991 Z"/>
  187. <path d="M49.5,150.5 L258.5,19.5" id="Line-22" sketch:type="MSShapeGroup"/>
  188. <path id="Line-22-decoration-1" d="M257.939322,19.8514296 C254.178828,20.9692764 250.955547,21.9274308 247.195052,23.0452775 C248.310345,24.8246376 249.26631,26.3498034 250.381603,28.1291635 C253.026805,25.2319566 255.29412,22.7486365 257.939322,19.8514296 C257.939322,19.8514296 257.939322,19.8514296 257.939322,19.8514296 Z"/>
  189. <path d="M3.5,207.5 L265.5,22.5" id="Line-23" sketch:type="MSShapeGroup"/>
  190. <path id="Line-23-decoration-1" d="M264.902063,22.9222075 C261.208605,24.2448071 258.042784,25.378464 254.349327,26.7010636 C255.560618,28.4165147 256.598868,29.8869013 257.81016,31.6023523 C260.292326,28.5643016 262.419897,25.9602582 264.902063,22.9222075 C264.902063,22.9222075 264.902063,22.9222075 264.902063,22.9222075 Z"/>
  191. <path d="M3.5,207.5 L243.5,147.5" id="Line-24" sketch:type="MSShapeGroup"/>
  192. <path id="Line-24-decoration-1" d="M243.125198,147.593701 C239.203396,147.491836 235.841853,147.404523 231.920052,147.302658 C232.429376,149.339957 232.865941,151.086214 233.375265,153.123513 C236.787742,151.188079 239.712721,149.529135 243.125198,147.593701 C243.125198,147.593701 243.125198,147.593701 243.125198,147.593701 Z"/>
  193. <path d="M3.5,207.5 L244.5,201.5" id="Line-25" sketch:type="MSShapeGroup"/>
  194. <path id="Line-25-decoration-1" d="M244.425346,201.501859 C240.620384,200.546263 237.358988,199.72718 233.554026,198.771584 C233.606292,200.870934 233.651091,202.670376 233.703357,204.769726 C237.456053,203.625972 240.67265,202.645612 244.425346,201.501859 C244.425346,201.501859 244.425346,201.501859 244.425346,201.501859 Z"/>
  195. </g>
  196. <g id="problems-2" transform="translate(33.000000, 91.000000)" stroke="#979797" fill="#4990E2" sketch:type="MSShapeGroup">
  197. <circle id="Oval-3" cx="30" cy="14" r="14"/>
  198. <circle id="Oval-4" cx="74" cy="66" r="14"/>
  199. <circle id="Oval-5" cx="14" cy="103" r="14"/>
  200. <circle id="Oval-6" cx="64" cy="163" r="14"/>
  201. <circle id="Oval-7" cx="23" cy="219" r="14"/>
  202. </g>
  203. <g id="Solutions-2" transform="translate(293.000000, 68.000000)" stroke="#979797" fill="#7ED321" sketch:type="MSShapeGroup">
  204. <circle id="Oval-8" cx="26" cy="37" r="14"/>
  205. <circle id="Oval-9" cx="74" cy="69" r="14"/>
  206. <circle id="Oval-10" cx="14" cy="99" r="14"/>
  207. <circle id="Oval-11" cx="71" cy="129" r="14"/>
  208. <circle id="Oval-12" cx="18" cy="168" r="14"/>
  209. <circle id="Oval-13" cx="71" cy="205" r="14"/>
  210. <circle id="Oval-14" cx="22" cy="229" r="14"/>
  211. <circle id="Oval-15" cx="66" cy="14" r="14"/>
  212. <circle id="Oval-16" cx="58" cy="289" r="14"/>
  213. </g>
  214. <text id="Problems-3" sketch:type="MSTextLayer" font-family="Lato" font-size="18" font-weight="normal" fill="#000000">
  215. <tspan x="43" y="18">Problems</tspan>
  216. </text>
  217. <text id="Technical-Solutions-2" sketch:type="MSTextLayer" font-family="Lato" font-size="18" font-weight="normal" fill="#000000">
  218. <tspan x="262" y="18">Technical Solutions</tspan>
  219. </text>
  220. </g>
  221. </g>
  222. </svg>
  223. <figcaption class="text-center">The way you choose technology in the world where operations are a serious concern (i.e., "reality"). </figcaption>
  224. </figure>
  225. <p>The problem with “best tool for the job” thinking is that it takes a myopic view of the words “best” and “job.” Your job is keeping the company in business, god damn it. And the “best” tool is the one that occupies the “least worst” position for as many of your problems as possible.</p>
  226. <p>It is basically always the case that the long-term costs of keeping a system working reliably vastly exceed any inconveniences you encounter while building it. Mature and productive developers understand this.</p>
  227. <h5 id="choose-new-technology-sometimes">Choose New Technology, Sometimes.</h5>
  228. <p>Taking this reasoning to its <em>reductio ad absurdum</em> would mean picking Java, and then trying to implement a website without using anything else at all. And that would be crazy. You need some means to add things to your toolbox.</p>
  229. <p>An important first step is to acknowledge that this is a process, and a conversation. New tech eventually has company-wide effects, so adding tech is a decision that requires company-wide visibility. Your organizational specifics may force the conversation, or <a href="https://twitter.com/mcfunley/status/578603932949164032">they may facilitate developers adding new databases and queues without talking to anyone</a>. One way or another you have to set cultural expectations that <strong>this is something we all talk about</strong>.</p>
  230. <p>One of the most worthwhile exercises I recommend here is to <strong>consider how you would solve your immediate problem without adding anything new</strong>. First, posing this question should detect the situation where the “problem” is that someone really wants to use the technology. If that is the case, you should immediately abort.</p>
  231. <figure>
  232. <img src="http://i.imgur.com/rmdSx.gif"/>
  233. <figcaption>I just watched a webinar about this graph database, we should try it out.</figcaption>
  234. </figure>
  235. <p>It can be amazing how far a small set of technology choices can go. The answer to this question in practice is almost never “we can’t do it,” it’s usually just somewhere on the spectrum of “well, we could do it, but it would be too hard” . If you think you can’t accomplish your goals with what you’ve got now, you are probably just not thinking creatively enough.</p>
  236. <p>It’s helpful to <strong>write down exactly what it is about the current stack that makes solving the problem prohibitively expensive and difficult.</strong> This is related to the previous exercise, but it’s subtly different.</p>
  237. <p>New technology choices might be purely additive (for example: “we don’t have caching yet, so let’s add memcached”). But they might also overlap or replace things you are already using. If that’s the case, you should <strong>set clear expectations about migrating old functionality to the new system.</strong> The policy should typically be “we’re committed to migrating,” with a proposed timeline. The intention of this step is to keep wreckage at manageable levels, and to avoid proliferating locally-optimal solutions.</p>
  238. <p>This process is not daunting, and it’s not much of a hassle. It’s a handful of questions to fill out as homework, followed by a meeting to talk about it. I think that if a new technology (or a new service to be created on your infrastructure) can pass through this gauntlet unscathed, adding it is fine.</p>
  239. <h5 id="just-ship">Just Ship.</h5>
  240. <p>Polyglot programming is sold with the promise that letting developers choose their own tools with complete freedom will make them more effective at solving problems. This is a naive definition of the problems at best, and motivated reasoning at worst. The weight of day-to-day operational <a href="https://twitter.com/handler">toil</a> this creates crushes you to death.</p>
  241. <p>Mindful choice of technology gives engineering minds real freedom: the freedom to <a href="/effective-web-experimentation-as-a-homo-narrans">contemplate bigger questions</a>. Technology for its own sake is snake oil.</p>
  242. <p><em>Update, July 27th 2015: I wrote a talk based on this article. You can see it <a href="http://boringtechnology.club">here</a>.</em></p>
  243. </main>
  244. </article>
  245. <hr>
  246. <footer>
  247. <p>
  248. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  249. <a href="/david/log/" title="Accès au flux RSS">🤖</a> •
  250. <a href="http://larlet.com" title="Go to my English profile" data-instant>🇨🇦</a> •
  251. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">📮</a> •
  252. <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">🧚</abbr>
  253. </p>
  254. <template id="theme-selector">
  255. <form>
  256. <fieldset>
  257. <legend>Thème</legend>
  258. <label>
  259. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  260. </label>
  261. <label>
  262. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  263. </label>
  264. <label>
  265. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  266. </label>
  267. </fieldset>
  268. </form>
  269. </template>
  270. </footer>
  271. <script type="text/javascript">
  272. function loadThemeForm(templateName) {
  273. const themeSelectorTemplate = document.querySelector(templateName)
  274. const form = themeSelectorTemplate.content.firstElementChild
  275. themeSelectorTemplate.replaceWith(form)
  276. form.addEventListener('change', (e) => {
  277. const chosenColorScheme = e.target.value
  278. localStorage.setItem('theme', chosenColorScheme)
  279. toggleTheme(chosenColorScheme)
  280. })
  281. const selectedTheme = localStorage.getItem('theme')
  282. if (selectedTheme && selectedTheme !== 'undefined') {
  283. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  284. }
  285. }
  286. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  287. window.addEventListener('load', () => {
  288. let hasDarkRules = false
  289. for (const styleSheet of Array.from(document.styleSheets)) {
  290. let mediaRules = []
  291. for (const cssRule of styleSheet.cssRules) {
  292. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  293. continue
  294. }
  295. // WARNING: Safari does not have/supports `conditionText`.
  296. if (cssRule.conditionText) {
  297. if (cssRule.conditionText !== prefersColorSchemeDark) {
  298. continue
  299. }
  300. } else {
  301. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  302. continue
  303. }
  304. }
  305. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  306. }
  307. // WARNING: do not try to insert a Rule to a styleSheet you are
  308. // currently iterating on, otherwise the browser will be stuck
  309. // in a infinite loop…
  310. for (const mediaRule of mediaRules) {
  311. styleSheet.insertRule(mediaRule.cssText)
  312. hasDarkRules = true
  313. }
  314. }
  315. if (hasDarkRules) {
  316. loadThemeForm('#theme-selector')
  317. }
  318. })
  319. </script>
  320. </body>
  321. </html>