A place to cache linked articles (think custom and personal wayback machine)
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

4 anos atrás
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>What is good code? (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://loup-vaillant.fr/articles/good-code">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. What is good code? (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://loup-vaillant.fr/articles/good-code">Source originale du contenu</a></h3>
  445. <p><strong>Good code is cheap code that meets our needs. The cheaper the
  446. better.</strong></p>
  447. <p>Well, assuming code is just a means to an end. Sometimes we want to
  448. enjoy the code for itself. Most of the time however, the overriding
  449. concern is the <em>bottom line</em>.</p>
  450. <p>I could almost stop right there, but I feel like I should elaborate.</p>
  451. <h2>Good (enough) programs</h2>
  452. <p>There is the code, and there is the program. Code is read by a human.
  453. Programs are interpreted by computers (or virtual machines).</p>
  454. <p>Programs have many requirements, most of which are domain specific.
  455. Some requirements however are universal:</p>
  456. <ul>
  457. <li>
  458. <p><strong>Correctness.</strong> The program must not have too many bugs. It would
  459. make it worse than useless.</p>
  460. </li>
  461. <li>
  462. <p><strong>Performance.</strong> The program must not be too slow. It would waste
  463. the users' time, make them angry, or even behave incorrectly (in the
  464. case of real time software).</p>
  465. </li>
  466. <li>
  467. <p><strong>Frugality.</strong> The program must not use too much resources. We have
  468. plenty of CPU memory… but they still have limits. Use too much
  469. resources and your program will drain the battery of your phone,
  470. waste your bandwidth, or plain stop working.</p>
  471. </li>
  472. </ul>
  473. <p>We don't always think of these requirements explicitly. Still, they
  474. must be met, and they have a cost. Want more performance or waste
  475. less resources? Your code will be more complex. Want less bugs? You
  476. will have to spend more time shaking them out.</p>
  477. <p>Once a program do meet all its requirements however, it is good
  478. enough. There is little point in trying to make it even better. With
  479. that goal set, the only thing left to optimise is the cost of code.</p>
  480. <h2>Code as a dependency graph</h2>
  481. <p>Before we can hope to estimate the cost of code, we must understand a
  482. few things about its structure.</p>
  483. <p>Basically, your code is made up of little chunks that can fit in your
  484. head (if it didn't, you couldn't have written it in the first place).
  485. Most of the time, a chunk is a function —or a method, or a procedure.
  486. Each such chunk is a node of your graph. And their dependencies form
  487. the edges.</p>
  488. <p>Like in any directed graph, when you have N nodes, you can have up to
  489. N² edges. This has important consequences when estimating the cost of
  490. code. The number of edges, relative to the number of nodes is called
  491. the <em>density</em> of the graph. Dense graphs have many edges (close to
  492. N²). Sparse graphs have relatively few edges (close to N).</p>
  493. <p>Each chunk of code have 2 levels of understanding: interface, and
  494. implementation.</p>
  495. <ul>
  496. <li>If you just want to <em>use</em> a piece of code, understanding its
  497. interface is enough.</li>
  498. <li>If you want to <em>contribute</em> to a piece of code, you have to
  499. understand its implementation. Which requires understanding the
  500. interface of all the direct dependencies.</li>
  501. </ul>
  502. <p>A few caveats, however</p>
  503. <ul>
  504. <li>
  505. <p>Dependencies aren't always obvious. With purely functional code,
  506. the dependency graph and the call graph are one and the same. On
  507. the other hand, things like shared state create more edges.
  508. <em>Implicit</em> edges. Those implicit dependencies are often easy to
  509. miss.</p>
  510. </li>
  511. <li>
  512. <p>To understand a properly documented interface, you generally don't
  513. have to look at anything else. Of course, some <em>aren't</em> properly
  514. documented, forcing you to check the implementation… This little
  515. dance can go coup<a href="https://en.wikipedia.org/wiki/Transitive_closure" title="Transitive closure (Wikipedia)">pretty far</a>).</p>
  516. </li>
  517. </ul>
  518. <h2>The cost of code</h2>
  519. <p>Basically, code costs however much time we spend on it. I personally
  520. break it down to 3 activities:</p>
  521. <ul>
  522. <li><strong>Typing.</strong> We need to write the code.</li>
  523. <li><strong>Understanding.</strong> We can't type randomly.</li>
  524. <li><strong>Coordination.</strong> We should avoid duplicate or incompatible work.</li>
  525. </ul>
  526. <p>Some would talk about development, maintenance, testing… But this is
  527. not an interesting way to break things down. Development and
  528. maintenance have a <em>lot</em> in common. Even testing involves writing and
  529. reading code. And all three activities involve typing, understanding,
  530. and coordination.</p>
  531. <h3>Typing</h3>
  532. <p>It is generally admitted that we spend much more time thinking about
  533. our code than we do typing it down. I still find it interesting
  534. however, because it provides an obvious lower bound. Code <em>cannot</em>
  535. cost less than the time taken to type it.</p>
  536. <p>Imagine a 20,000 lines program. By current standards, this is not a
  537. big program. If you were to print it, it would fit in a medium-sized
  538. book: 400 pages, 50 lines per page. Prose would typically have about
  539. 12 words per line, but lines of code are shorter. Let's say 6 words
  540. per line. That's 120,000 words.</p>
  541. <p>Assuming you type 50 words per minute (a fair speed), typing it all
  542. down would take 40 hours. A full work week of mindless typing, and
  543. the program is not even "big". I say current standards are insane.</p>
  544. <h3>Understanding</h3>
  545. <p>You can't write code randomly. You need to know what you're doing.
  546. More specifically, you need to understand three things:</p>
  547. <ul>
  548. <li>New code (that you are about to write).</li>
  549. <li>Existing code. (<em>This</em> is why code is so expensive.)</li>
  550. <li>Prerequisites. (Required background knowledge.)</li>
  551. </ul>
  552. <h4>New code</h4>
  553. <p>The depth of understanding required to write new code is significant.
  554. This is going to take longer than 50 words per minute. Those 20,000
  555. lines aren't going to write themselves in a week. Nevertheless,
  556. assuming you work on this code piecemeal (it is impossible not to),
  557. the time taken to understand new code is still proportional to the
  558. length of that code.</p>
  559. <p>Oh, right. <em>Length.</em></p>
  560. <p>Intuitively, the time required to understand a piece of code is
  561. proportional to its <em>complexity</em>, not its length. Length, measured in
  562. lines of code, is an incredibly crude proxy. But it works. It is
  563. strongly correlated with most complexity measures we came up with so
  564. far, and those don't have more predictive power than length alone.
  565. For instance, if you know a program's length, learning its cyclomatic
  566. complexity won't tell you anything more about things like time to
  567. completion or number of bugs.</p>
  568. <p>Besides a <a href="https://en.wikipedia.org/wiki/Code_golf" title="Code Golf (Wikipedia)">few exceptions</a>, complexity and length are roughly
  569. proportional. Of two chunks of code solving the same problem, if one
  570. is twice as big, it is probably twice as complex. Roughly.
  571. Amazingly, this heuristic works across languages. Terser languages
  572. make the code cheaper. (Again, we may have some <a href="https://en.wikipedia.org/wiki/APL_%28programming_language%29" title="APL (Wikipedia)">exceptions</a>).</p>
  573. <h4>Existing code</h4>
  574. <p>Any new code you write will use, or be used by, existing code. You
  575. need to understand some of the old code before you write anything new.
  576. Unless you're just starting your project, but you won't start forever.</p>
  577. <p>The ideal case is when you work alone, and everything you have written
  578. so far is still fresh in your mind. Understanding it <em>again</em> costs
  579. you nothing.</p>
  580. <p>Well, that never happens. You forget about code you have written long
  581. ago. You don't work alone, and must understand code <em>others</em> have
  582. written. Or maybe you arrived late in the project. Now the density
  583. of your dependency graph matters a great deal:</p>
  584. <ul>
  585. <li>
  586. <p>If the graph is sparse, you'll rarely have to understand more than a
  587. few dependencies to contribute. The cost is not nil, but it can
  588. still be proportional to the size of the code base.</p>
  589. </li>
  590. <li>
  591. <p>If the graph is dense however, you may have to understand many
  592. things before you do anything. In the worst case, that cost can
  593. rise up to the <em>square</em> of the size of code base. Even if no graph
  594. gets <em>that</em> dense, density can still kill your project.</p>
  595. </li>
  596. </ul>
  597. <h4>Prerequisites</h4>
  598. <p>This one is highly context specific. Common knowledge costs nothing,
  599. because everyone knows it (by definition), and some knowledge is
  600. required to merely understand the problem.</p>
  601. <p>Some background knowledge however relates to <em>how</em> you solve your
  602. problem. There are different ways to write a program, and some are
  603. more… esoteric than others. Take for instance that little
  604. <a href="../projects/metacompilers">metacompiler</a> I have written in Haskell. Very handy when you need
  605. to parse some textual data format. On the other hand, you will need
  606. to know about top-down parsing, parsing expression grammars, parser
  607. combinators, monads, applicative functors… are you <em>sure</em> you want to
  608. learn all that just to parse some text? By the way, I no longer
  609. maintain this tool.</p>
  610. <p>Required background knowledge is the reason why lambdas and recursion
  611. are often frowned upon in mainstream settings. The typical OOP
  612. programmer is not used to this eldritch stuff from <a href="../tutorials/from-imperative-to-functional">FP hell</a>. Not
  613. yet, at least.</p>
  614. <p>I don't have a magic bullet for this problem. If you don't know some
  615. useful concept or external tool, you can't use it unless you spend
  616. time to learn it. Good luck with the cost-benefit analysis.</p>
  617. <h3>Coordination</h3>
  618. <p><em>(I have worked in several teams, but never lead one. Take this
  619. section with a grain of salt.)</em></p>
  620. <p>Most of the time, you will work in a team. You have to. Most
  621. programmers can't do everything a full system requires. Domain
  622. specific algorithms, user interface, network, database, security… Even
  623. if you're one of those miraculous full stack experts, you probably
  624. won't have the time to code it by yourself.</p>
  625. <p>Hence coordination. In the worst case, everyone communicates with
  626. everyone else all the time. The cost of that overhead is quadratic
  627. with respect to the size of the team. Not a problem with 4 people.
  628. Quite a mess with 40. In practice, when teams grow large enough, two
  629. things inevitably happen:</p>
  630. <ul>
  631. <li>Someone ends up leading the team. Most communications go through
  632. that person. This brings the amount of communication back to linear
  633. (mostly), but it is all concentrated on one person.</li>
  634. <li>When the team gets <em>real</em> big, it gets divided into sub-teams, each
  635. with their own leader. That means less overhead for the main
  636. leader, without increasing the communication overhead too much.</li>
  637. </ul>
  638. <p>(If neither happens, communication overhead explodes and little gets
  639. done.)</p>
  640. <p>How that relates to code is explained by <a href="https://en.wikipedia.org/w/index.php?title=Conway%27s_law" title="Wikipedia">Conway's law:</a></p>
  641. <p>&gt; organizations which design systems … are constrained to produce
  642. &gt; designs which are copies of the communication structures of these
  643. &gt; organizations</p>
  644. <p>This works both ways. The organisation of your team will shape the
  645. code it produces, and the code you ask of your team will shape its
  646. organisation. You just can't build a big monolith with separate
  647. sub-teams. Either the teams will communicate a lot (effectively
  648. merging them together), or they will come up with a more modular
  649. design.</p>
  650. <h2>Driving down the costs</h2>
  651. <p>From the above we can deduce 4 levers to reduce the cost of code:</p>
  652. <ul>
  653. <li>
  654. <p><strong>Write less code.</strong> Less code is just plain cheaper. Enough said.</p>
  655. </li>
  656. <li>
  657. <p><strong>Keep it modular.</strong> Make your dependency graph as sparse as you
  658. can. It will reduce communication overheads, let your team work in
  659. parallel, and reduce the amount of code you need to understand
  660. before you write something new.</p>
  661. </li>
  662. <li>
  663. <p><strong>Ponder your external dependencies.</strong> External tools are a tough
  664. call. While they can massively reduce the amount of code you need
  665. to write, they can have a steep learning curve, or a significant
  666. maintenance burden. Make sure your external dependencies are worth
  667. your while.</p>
  668. </li>
  669. <li>
  670. <p><strong>Use fewer concepts.</strong> If your code requires less background
  671. knowledge, the time saved not learning it can be put to good use.
  672. Be careful, though. The right concepts often makes your code
  673. shorter, and are often widely applicable.</p>
  674. </li>
  675. </ul>
  676. <p>Pretty standard stuff. I just want to stress one thing: those 4
  677. levers are probably the <em>only</em> ones that matter. Found a new
  678. technique, a new language, a new methodology? It has to do one of
  679. those:</p>
  680. <ul>
  681. <li>Reduce the amount of code;</li>
  682. <li>increase its modularity;</li>
  683. <li>replace or subsume heavier external dependencies;</li>
  684. <li>or <em>maybe</em> reduce the amount of required background knowledge.</li>
  685. </ul>
  686. <p>Otherwise it won't reduce your costs.</p>
  687. </article>
  688. </section>
  689. <nav id="jumpto">
  690. <p>
  691. <a href="/david/blog/">Accueil du blog</a> |
  692. <a href="http://loup-vaillant.fr/articles/good-code">Source originale</a> |
  693. <a href="/david/stream/2019/">Accueil du flux</a>
  694. </p>
  695. </nav>
  696. <footer>
  697. <div>
  698. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  699. <p>
  700. Bonjour/Hi!
  701. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  702. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  703. </p>
  704. <p>
  705. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  706. </p>
  707. <p>
  708. Voici quelques articles choisis :
  709. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  710. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  711. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  712. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  713. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  714. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  715. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  716. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  717. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  718. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  719. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  720. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  721. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  722. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  723. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  724. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  725. </p>
  726. <p>
  727. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  728. </p>
  729. <p>
  730. Je ne traque pas ta navigation mais mon
  731. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  732. conserve des logs d’accès.
  733. </p>
  734. </div>
  735. </footer>
  736. <script type="text/javascript">
  737. ;(_ => {
  738. const jumper = document.getElementById('jumper')
  739. jumper.addEventListener('click', e => {
  740. e.preventDefault()
  741. const anchor = e.target.getAttribute('href')
  742. const targetEl = document.getElementById(anchor.substring(1))
  743. targetEl.scrollIntoView({behavior: 'smooth'})
  744. })
  745. })()
  746. </script>