Repository with sources and generator of https://larlet.fr/david/ https://larlet.fr/david/
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
5 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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>Collaboration technique — 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. <!-- Canonical URL for SEO purposes -->
  28. <link rel="canonical" href="https://larlet.fr/david/blog/2015/collaboration-technique/">
  29. <!-- SEO/Semantic metadata -->
  30. <meta name="description" content="Retour sur une formation/accompagnement chez Natural-Solutions." />
  31. <meta name="twitter:description" property="og:description" itemprop="description" content="Retour sur une formation/accompagnement chez Natural-Solutions." />
  32. <meta name="twitter:title" property="og:title" itemprop="name" content="Collaboration technique" />
  33. <meta name="twitter:card" content="summary" />
  34. <meta name="twitter:creator" content="@davidbgk" />
  35. <meta name="twitter:url" property="og:url" content="https://larlet.fr/david/blog/2015/collaboration-technique/" />
  36. <meta property="og:type" content="article" />
  37. <meta property="og:site_name" content="David Larlet (@davidbgk)" />
  38. <meta name="twitter:image" property="og:image" itemprop="image" content="https://larlet.fr/static/david/blog/2015/collaboration-technique.jpg" />
  39. <style>
  40. /* http://meyerweb.com/eric/tools/css/reset/ */
  41. html, body, div, span,
  42. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  43. a, abbr, address, big, cite, code,
  44. del, dfn, em, img, ins,
  45. small, strike, strong, tt, var,
  46. dl, dt, dd, ol, ul, li,
  47. fieldset, form, label, legend,
  48. table, caption, tbody, tfoot, thead, tr, th, td,
  49. article, aside, canvas, details, embed,
  50. figure, figcaption, footer, header, hgroup,
  51. menu, nav, output, ruby, section, summary,
  52. time, mark, audio, video {
  53. margin: 0;
  54. padding: 0;
  55. border: 0;
  56. font-size: 100%;
  57. font: inherit;
  58. vertical-align: baseline;
  59. }
  60. /* HTML5 display-role reset for older browsers */
  61. article, aside, details, figcaption, figure,
  62. footer, header, hgroup, menu, nav, section { display: block; }
  63. body { line-height: 1; }
  64. blockquote, q { quotes: none; }
  65. blockquote:before, blockquote:after,
  66. q:before, q:after {
  67. content: '';
  68. content: none;
  69. }
  70. table {
  71. border-collapse: collapse;
  72. border-spacing: 0;
  73. }
  74. /* http://practicaltypography.com/equity.html */
  75. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  76. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: normal;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  89. font-weight: 300;
  90. font-style: italic;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'EquityTextB';
  95. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  96. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  97. font-weight: 700;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. @font-face {
  102. font-family: 'ConcourseT3';
  103. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  104. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  105. font-weight: 300;
  106. font-style: normal;
  107. font-display: swap;
  108. }
  109. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  110. body {
  111. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  112. font-family: "EquityTextB", Palatino, serif;
  113. background-color: #f0f0ea;
  114. color: #07486c;
  115. font-kerning: normal;
  116. -moz-osx-font-smoothing: grayscale;
  117. -webkit-font-smoothing: subpixel-antialiased;
  118. text-rendering: optimizeLegibility;
  119. font-variant-ligatures: common-ligatures contextual;
  120. font-feature-settings: "kern", "liga", "clig", "calt";
  121. }
  122. pre, code, kbd, samp, var, tt {
  123. font-family: 'TriplicateT4c', monospace;
  124. }
  125. em {
  126. font-style: italic;
  127. color: #323a45;
  128. }
  129. strong {
  130. font-weight: bold;
  131. color: black;
  132. }
  133. nav {
  134. background-color: #323a45;
  135. color: #f0f0ea;
  136. display: flex;
  137. justify-content: space-around;
  138. padding: 1rem .5rem;
  139. }
  140. nav:last-child {
  141. border-bottom: 1vh solid #2d7474;
  142. }
  143. nav a {
  144. color: #f0f0ea;
  145. }
  146. nav abbr {
  147. border-bottom: 1px dotted white;
  148. }
  149. h1 {
  150. border-top: 1vh solid #2d7474;
  151. border-bottom: .2vh dotted #2d7474;
  152. background-color: #e3e1e1;
  153. color: #323a45;
  154. text-align: center;
  155. padding: 5rem 0 4rem 0;
  156. width: 100%;
  157. font-family: 'ConcourseT3';
  158. display: flex;
  159. flex-direction: column;
  160. }
  161. h1.single {
  162. padding-bottom: 10rem;
  163. }
  164. h1 span {
  165. position: absolute;
  166. top: 1vh;
  167. left: 20%;
  168. line-height: 0;
  169. }
  170. h1 span a {
  171. line-height: 1.7;
  172. padding: 1rem 1.2rem .6rem 1.2rem;
  173. border-radius: 0 0 6% 6%;
  174. background: #2d7474;
  175. font-size: 1.3rem;
  176. color: white;
  177. text-decoration: none;
  178. }
  179. h2 {
  180. margin: 4rem 0 1rem;
  181. border-top: .2vh solid #2d7474;
  182. padding-top: 1vh;
  183. }
  184. h3 {
  185. text-align: center;
  186. margin: 3rem 0 .75em;
  187. }
  188. hr {
  189. height: .4rem;
  190. width: .4rem;
  191. border-radius: .4rem;
  192. background: #07486c;
  193. margin: 2.5rem auto;
  194. }
  195. time {
  196. display: bloc;
  197. margin-left: 0 !important;
  198. }
  199. ul, ol {
  200. margin: 2rem;
  201. }
  202. ul {
  203. list-style-type: square;
  204. }
  205. a {
  206. text-decoration-skip-ink: auto;
  207. text-decoration-thickness: 0.05em;
  208. text-underline-offset: 0.09em;
  209. }
  210. article {
  211. max-width: 50rem;
  212. display: flex;
  213. flex-direction: column;
  214. margin: 2rem auto;
  215. }
  216. article.single {
  217. border-top: .2vh dotted #2d7474;
  218. margin: -6rem auto 1rem auto;
  219. background: #f0f0ea;
  220. padding: 2rem;
  221. }
  222. article p:last-child {
  223. margin-bottom: 1rem;
  224. }
  225. p {
  226. padding: 0 .5rem;
  227. margin-left: 3rem;
  228. }
  229. p + p,
  230. figure + p {
  231. margin-top: 2rem;
  232. }
  233. blockquote {
  234. background-color: #e3e1e1;
  235. border-left: .5vw solid #2d7474;
  236. display: flex;
  237. flex-direction: column;
  238. align-items: center;
  239. padding: 1rem;
  240. margin: 1.5rem;
  241. }
  242. blockquote cite {
  243. font-style: italic;
  244. }
  245. blockquote p {
  246. margin-left: 0;
  247. }
  248. figure {
  249. border-top: .2vh solid #2d7474;
  250. background-color: #e3e1e1;
  251. text-align: center;
  252. padding: 1.5rem 0;
  253. margin: 1rem 0 0;
  254. font-size: 1.5rem;
  255. width: 100%;
  256. }
  257. figure img {
  258. max-width: 250px;
  259. max-height: 250px;
  260. border: .5vw solid #323a45;
  261. padding: 1px;
  262. }
  263. figcaption {
  264. padding: 1rem;
  265. line-height: 1.4;
  266. }
  267. aside {
  268. display: flex;
  269. flex-direction: column;
  270. background-color: #e3e1e1;
  271. padding: 1rem 0;
  272. border-bottom: .2vh solid #07486c;
  273. }
  274. aside p {
  275. max-width: 50rem;
  276. margin: 0 auto;
  277. }
  278. /* https://fvsch.com/code/css-locks/ */
  279. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  280. font-size: 1rem;
  281. line-height: calc( 1.5em + 0.2 * 1rem );
  282. }
  283. h1 {
  284. font-size: 1.9rem;
  285. line-height: calc( 1.2em + 0.2 * 1rem );
  286. }
  287. h2 {
  288. font-size: 1.6rem;
  289. line-height: calc( 1.3em + 0.2 * 1rem );
  290. }
  291. h3 {
  292. font-size: 1.35rem;
  293. line-height: calc( 1.4em + 0.2 * 1rem );
  294. }
  295. @media (min-width: 20em) {
  296. /* The (100vw - 20rem) / (50 - 20) part
  297. resolves to 0-1rem, depending on the
  298. viewport width (between 20em and 50em). */
  299. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  300. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  301. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  302. margin-left: 0;
  303. }
  304. h1 {
  305. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. h2 {
  309. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  310. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  311. }
  312. h3 {
  313. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  314. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  315. }
  316. }
  317. @media (min-width: 50em) {
  318. /* The right part of the addition *must* be a
  319. rem value. In this example we *could* change
  320. the whole declaration to font-size:2.5rem,
  321. but if our baseline value was not expressed
  322. in rem we would have to use calc. */
  323. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  324. font-size: calc( 1rem + .6 * 1rem );
  325. line-height: 1.5em;
  326. }
  327. p, li, pre, details {
  328. margin-left: 3rem;
  329. }
  330. h1 {
  331. font-size: calc( 1.9rem + 1.5 * 1rem );
  332. line-height: 1.2em;
  333. }
  334. h2 {
  335. font-size: calc( 1.5rem + 1.5 * 1rem );
  336. line-height: 1.3em;
  337. }
  338. h3 {
  339. font-size: calc( 1.35rem + 1.5 * 1rem );
  340. line-height: 1.4em;
  341. }
  342. figure img {
  343. max-width: 500px;
  344. max-height: 500px;
  345. }
  346. }
  347. figure.unsquared {
  348. margin-bottom: 1.5rem;
  349. }
  350. figure.unsquared img {
  351. height: inherit;
  352. }
  353. @media print {
  354. body { font-size: 100%; }
  355. a:after { content: " (" attr(href) ")"; }
  356. a, a:link, a:visited, a:after {
  357. text-decoration: underline;
  358. text-shadow: none !important;
  359. background-image: none !important;
  360. background: white;
  361. color: black;
  362. }
  363. abbr[title] { border-bottom: 0; }
  364. abbr[title]:after { content: " (" attr(title) ")"; }
  365. img { page-break-inside: avoid; }
  366. @page { margin: 2cm .5cm; }
  367. h1, h2, h3 { page-break-after: avoid; }
  368. p3 { orphans: 3; widows: 3; }
  369. img {
  370. max-width: 250px !important;
  371. max-height: 250px !important;
  372. }
  373. nav, aside { display: none; }
  374. }
  375. ul.with_columns {
  376. column-count: 1;
  377. }
  378. @media (min-width: 20em) {
  379. ul.with_columns {
  380. column-count: 2;
  381. }
  382. }
  383. @media (min-width: 50em) {
  384. ul.with_columns {
  385. column-count: 3;
  386. }
  387. }
  388. ul.with_two_columns {
  389. column-count: 1;
  390. }
  391. @media (min-width: 20em) {
  392. ul.with_two_columns {
  393. column-count: 1;
  394. }
  395. }
  396. @media (min-width: 50em) {
  397. ul.with_two_columns {
  398. column-count: 2;
  399. }
  400. }
  401. .gallery {
  402. display: flex;
  403. flex-wrap: wrap;
  404. justify-content: space-around;
  405. }
  406. .gallery figure img {
  407. margin-left: 1rem;
  408. margin-right: 1rem;
  409. }
  410. .gallery figure figcaption {
  411. font-family: 'ConcourseT3'
  412. }
  413. footer {
  414. font-family: 'ConcourseT3';
  415. display: flex;
  416. flex-direction: column;
  417. border-top: 3px solid white;
  418. padding: 4rem 0;
  419. background-color: #07486c;
  420. color: white;
  421. }
  422. footer > * {
  423. max-width: 50rem;
  424. margin: 0 auto;
  425. }
  426. footer a {
  427. color: #f1c40f;
  428. }
  429. footer .avatar {
  430. width: 200px;
  431. height: 200px;
  432. border-radius: 50%;
  433. float: left;
  434. -webkit-shape-outside: circle();
  435. shape-outside: circle();
  436. margin-right: 2rem;
  437. padding: 2px 5px 5px 2px;
  438. background: white;
  439. border-left: 1px solid #f1c40f;
  440. border-top: 1px solid #f1c40f;
  441. border-right: 5px solid #f1c40f;
  442. border-bottom: 5px solid #f1c40f;
  443. }
  444. </style>
  445. <h1 class="single">
  446. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  447. Collaboration technique
  448. <time>Publié le 14 avril 2015</time>
  449. </h1>
  450. <article class="single">
  451. <p>J’ai eu l’occasion <a href="/david/blog/2015/formations-explorations/">de transmettre</a> et <a href="/david/pro/accompagnement/">d’accompagner</a> dans la même journée <a href="http://www.natural-solutions.eu/">Natural-Solutions</a> sur le thème de « <strong>Collaborer efficacement autour du code</strong> ». Voici une petite synthèse des échanges et propositions, merci à Olivier et Gilles pour leur confiance. Merci à Vincent pour son accompagnement et sa relecture.</p>
  452. <h2>Référentiel commun</h2>
  453. <blockquote>
  454. <p>What I miss · Most of al­l, the bug track­er. Any em­ploy­ee can file a bug against any prod­uct and be cer­tain that some­one on the en­gi­neer­ing team will at least look at it. There are cer­tain internal-social-engineering tech­niques you can use to fo­cus at­ten­tion on an is­sue you think isn’t get­ting enough. Lots of bug re­ports are feature-requests and oth­ers are feature-removal de­mand­s, and that’s fine.</p>
  455. <p><cite><em><a href="https://www.tbray.org/ongoing/When/201x/2015/03/29/Anniversaries">Google + 1yr</a></em> (<a href="/david/cache/cd48ce5c2d83f8fa7b4c0a957db0d333/">cache</a>)</cite></p>
  456. </blockquote>
  457. <p>Il s’agit du conteneur de la valeur technique de l’entreprise. Il est capital de faire un choix stratégique sur ce point et je n’ai pas de solution miracle. À peu près tous les systèmes que j’ai pu tester étaient déficients et contraignants. Il faut au minimum que le système comporte des identifiants uniques et du contenu editable par toutes les parties prenantes. Il vaut mieux qu’il soit pérenne car il est la meilleure source d’investigation des bugs.</p>
  458. <p>En effet, chaque <em>commit</em> doit comporter une référence à la tâche renseignée dans ce référentiel qui documente les raisons d’une telle implémentation. Les messages de <em>commit</em> <a href="https://github.com/davidbgk/git-style-guide">sont très importants</a> car ils doivent aider vos successeurs lors d’un <em>blame</em> (qui porte assez mal son nom). Le lien est ainsi fait entre chaque ligne de code et chaque développement de fonctionnalité ou correction de bug. Ne pas perdre de vue qu’il faut pouvoir avoir une vue synthétique de ce référentiel (<a href="https://robinpowered.com/blog/best-practice-system-for-organizing-and-tagging-github-issues/">un bon classement</a> (<a href="/david/cache/986d37a75df767708b9e348e71b07345/">cache</a>) est une aide précieuse).</p>
  459. <p>J’ai de plus en plus tendance à commencer à coder en rédigeant mon message de <em>commit</em> de façon détaillée, cela permet d’avoir les idées claires sur les tenants et les aboutissants avant même de faire la première ligne de code. C’est ce message qui aidera le <em>reviewer</em> et potentiellement l’assurance qualité ensuite. Cela rejoint le <a href="http://tom.preston-werner.com/2010/08/23/readme-driven-development.html">Readme Driven Development</a> (<a href="/david/cache/053cc2e6338158d83f898d2d0f5912a8/">cache</a>).</p>
  460. <h3>Cas particulier : monorepo</h3>
  461. <blockquote>
  462. <p>When it comes to repository hosting conversions, I agree with Google and Facebook: <strong>I prefer monolithic repositories</strong>. When I am interacting with version control, I just want to get stuff done. I don’t want to waste time dealing with multiple commands to manage multiple repositories. I don’t want to waste time or expend cognitive load dealing with submodule, sub repository, or big files management. I don’t want to waste time trying to find and reuse code, data, or documentation. I want everything at my fingertips, where it can be easily discovered, inspected, and used. Monolithic repositories facilitate these workflows more than separate repositories and make me more productive as a result.</p>
  463. <p><cite><em><a href="http://gregoryszorc.com/blog/2014/09/09/on-monolithic-repositories/">On Monolithic Repositories</a></em> (<a href="/david/cache/7baab90810027d42259e14006a420074/">cache</a>)</cite></p>
  464. </blockquote>
  465. <p>J’ai longtemps trouvé cela abscons mais je reviens un peu sur ma position (je ne suis pas prêt à bosser pour Google ou Facebook pour autant…), avoir un dépôt pour l’ensemble des projets peut encourager la collaboration transversalement dans les équipes et le partage de code entre les produits.</p>
  466. <h2>Relecture continue</h2>
  467. <blockquote>
  468. <p>As in Maslow’s pyramid, each layer requires the previous one. It is useless for code that is charging the wrong customer to be readable. Code should be:</p>
  469. <ul>
  470. <li>Correct</li>
  471. <li>Secure</li>
  472. <li>Readable</li>
  473. <li>Elegant</li>
  474. <li>Altruist</li>
  475. </ul>
  476. <p><cite><em><a href="http://blog.d3in.org/post/111338685456/maslows-pyramid-of-code-review">Maslow’s pyramid of code review</a></em> (<a href="/david/cache/b6f259d122890a04a89b658b364f62cd/">cache</a>)</cite></p>
  477. </blockquote>
  478. <p>L’auto-évaluation est assez mauvaise, on peut relire 15 fois son code et pourtant passer à côté de bugs qui vont être évidents au premier coup d’œil par un pair. Je l’ai expérimenté de nombreuses fois et j’envisage difficilement de me passer d’une telle pratique aujourd’hui. Il faut bien garder à l’esprit que seul le code est évalué, pas la personne. Ça peut faire mal à l’égo mais ce n’est pas une attaque personnelle pour autant !</p>
  479. <p>Les avantages de la <em>review</em> sont nombreux et <a href="http://julienw.github.io/parisweb-2014-bonnes-pratiques/html5/parisweb-2014-bonnes-pratiques.html">Anthony et Julien en ont parlé</a> lors de ParisWeb, de même <a href="http://www.paris-web.fr/2014/conferences/100-de-revue-de-code.php">qu’Agnès</a> :</p>
  480. <ul>
  481. <li>diffuser la connaissance dans l’équipe</li>
  482. <li>augmenter la cohérence du code au sein des projets</li>
  483. <li>partager la responsabilité et son poids</li>
  484. </ul>
  485. <h3>Cas particulier : pair-programming</h3>
  486. <p>Si vous êtes 2 dans l’équipe et que vous pair-programmez, qui va relire vos développements ? Est-ce que le fait d’être en paire constitue une relecture suffisante ? Nous avons été confrontés à ce problème <a href="http://agopian.info/blog/">avec Mathieu</a> et je n’ai pas d’avis tranché sur la question si ce n’est d’essayer d’avoir une troisième personne dans l’équipe ! Parfois même un collègue sur un autre produit peut être pertinent.</p>
  487. <h2>Flux de travail</h2>
  488. <blockquote>
  489. <p>We don’t want humans waiting on computers. We want computers waiting on humans.</p>
  490. <p><cite><em><a href="http://gregoryszorc.com/blog/2015/03/28/notes-from-facebook%27s-developer-infrastructure-at-scale-f8-talk/">Notes from Facebook’s Developer Infrastructure at Scale F8 Talk</a></em> (<a href="/david/cache/add9aea6059be55754097cf61bd9eee2/">cache</a>)</cite></p>
  491. </blockquote>
  492. <p>En amont, on établit des conventions de développement (<a href="https://www.python.org/dev/peps/pep-0008/">pep8</a>, <a href="https://github.com/citrusbyte/styleguides/">styleguides</a>, etc) propres à l’équipe et au projet. Il est possible d’automatiser le rendu du code avec <a href="https://medium.com/@addyosmani/auto-formatting-javascript-code-style-fe0f98a923b8">JSCS</a> (<a href="/david/cache/cfdfe70dc1d0b6d3a9695c5dd3610b3e/">cache</a>) pour JavaScript ou <a href="https://github.com/google/yapf">yapf</a> pour Python par exemple.</p>
  493. <ol>
  494. <li>on <em>linte</em> et on teste son code pendant le développement avec les plugins propres à l’éditeur choisi </li>
  495. <li>on soumet une <em>pull-request</em> <a href="https://quickleft.com/blog/pull-request-templates-make-code-review-easier/">bien documentée</a> (<a href="/david/cache/e9b8e370e7a0b08985827431c82f1d85/">cache</a>) (pour <a href="https://gun.io/blog/how-to-github-fork-branch-and-pull-request/">du pas à pas</a> (<a href="/david/cache/87088098369e5eadca6728c963966792/">cache</a>))</li>
  496. <li>on teste sur un environnement aussi proche que possible de la production (possibilité d’<a href="https://realpython.com/blog/python/docker-in-action-fitter-happier-more-productive/">utiliser Docker</a> (<a href="/david/cache/37adabe90396072db9cfdd99baa61cd0/">cache</a>) par exemple)</li>
  497. <li>on demande une <em>review</em> de son implémentation, on itère et on <em>merge</em></li>
  498. <li>on déploie sur un environnement identique à la production qui est testable/montrable</li>
  499. <li>on déploie en production avec une possibilité de <em>rollback</em> à partir d’un tag explicite</li>
  500. </ol>
  501. <p>Il est important de conserver <a href="http://mathieu.agopian.info/blog/latence-et-boucle-de-retroaction.html">une boucle de rétroaction courte</a> (<a href="/david/cache/f1ed7d7dff2ed14214bdf7a25dde1d74/">cache</a>) tout au long du flux. Il est possible d’introduire des <em>hooks</em> pour automatiser des tâches (par exemple <a href="https://github.com/naholyr/github-todos/wiki/Full-presentation">la création d’issues</a>). Il existe le <a href="http://www.occitech.fr/blog/2014/12/un-modele-de-branches-git-efficace/">gitflow bien connu</a> (<a href="/david/cache/7dfecff06cb91feccce15d23301c09e8/">cache</a>) ou le <a href="http://scottchacon.com/2011/08/31/github-flow.html">plus simple github flow</a> (<a href="/david/cache/5669f09e770c1bae1f4a84a753eff260/">cache</a>), dans tous les cas il est possible d’<a href="http://haacked.com/archive/2014/07/28/github-flow-aliases/">utiliser des alias</a> (<a href="/david/cache/d69a335a5162c7bf71aa35b2e7ec73a7/">cache</a>) ou des applications dédiées (SourceTree, Tower, Github, etc) pour éviter d’avoir à retenir <a href="http://www.git-attitude.fr/2014/09/15/30-options-git-qui-gagnent-a-etre-connues/">tout plein de trucs</a> (<a href="/david/cache/1cad71d948ab1aee73e88a73a0112df4/">cache</a>).</p>
  502. <h3>Cas particulier : déploiement non-continu</h3>
  503. <p>Certains déploiements doivent se produire avec une régularité calendaire ou pour un événement spécial. Dans ce cas, le flux de travail est altéré. Il est important que la pré-production soit vraiment iso-fonctionnelle pour pouvoir effectuer des tests de montée en charge et/ou que la programmation du déploiement puisse être automatisée. Des outils comme <a href="http://www.fabfile.org/">Fabric</a>, Chef, Puppet ou Ansible peuvent être utiles pour automatiser les déploiements. Ou juste <a href="https://github.com/remind101/deploy">un script</a> tirant partie des <a href="https://developer.github.com/v3/repos/deployments/">déploiements Github</a> déjà mieux qu’un <code>git pull</code> sur le <code>HEAD</code> de <code>master</code>.</p>
  504. <h2>Documentation</h2>
  505. <blockquote>
  506. <p>What does discoverability and consistency mean? It means producing software (hopefully) free from the artifacts of bias, as agreed upon by your team. Do you have to use Rails or protobuf to be discoverable and consistent? Of course not. But you should seek to gain consensus. Here is a simple guide to becoming more consistent and discoverable in your codebase.</p>
  507. <p><cite><em><a href="http://localshred.github.io/consistency.html">Consistency</a></em> (<a href="/david/cache/60d8082d551627ee8c83c1d7da9b4213/">cache</a>)</cite></p>
  508. </blockquote>
  509. <p>La première étape est de documenter l’installation initiale du projet et de réduire celle-ci à 3 étapes (ou moins !). J’ai croisé tellement de projets en <em>open-source</em> qui sont impossibles à installer que ça en est déprimant. Le pire étant que les auteurs se trouvent dans l’incompréhension et sont déçus de n’avoir aucune participation externe… L’optimisation de la prise en main est essentielle à l’intégration de nouvelles personnes sur le projet. Cela doit être votre priorité numéro 1 si vous souhaitez <em>vraiment</em> libérer un produit. Les outils existent pour <a href="http://agileek.github.io/docker/2015/04/13/docker-ansible/">avoir un poste de développement rapidement</a> (<a href="/david/cache/1f566c364ea0d8e96da00f38ecb08651/">cache</a>), n’hésitez pas à les utiliser (et à les maintenir à jour).</p>
  510. <blockquote>
  511. <p>If people <em>are</em> working from the same location, it is important that they do not skimp on writing things down.</p>
  512. <p><cite><em><a href="https://about.gitlab.com/2015/04/08/the-remote-manifesto/">The Remote Manifesto</a></em> (<a href="/david/cache/b51c1c6daa60f39f6d088712176d766f/">cache</a>)</cite></p>
  513. </blockquote>
  514. <p>Il est très utile également de documenter les décisions techniques stratégiques au cours du cycle de vie d’un projet. C’est le seul moyen de ne pas rediscuter indéfiniment les mêmes choix et de reproduire les mêmes erreurs. C’est peut-être la transition culturelle la plus difficile à effectuer pour une équipe interne qui utilise surtout l’oralité pour les prises de décisions.</p>
  515. <p>Les <a href="http://styleguides.io/">styleguides</a> sont <a href="http://primercss.io/">à la mode</a> et au-delà de <a href="https://medium.com/@operatino/living-style-guide-tools-in-depth-overview-28cfffb92d05">l’aspect marketing</a> (<a href="/david/cache/ff9dd5355d2a5b57988ebcfbd74f77b3/">cache</a>), il est possible d’avoir une base rapidement qui sert à maintenir la cohérence entre les différents composants d’une application web. Des <a href="https://trulia.github.io/hologram/">outils</a> <a href="https://github.com/kneath/kss">existent</a> pour <a href="http://jacobrask.github.io/styledocco/">automatiser</a> sa génération, l’utilisation d’<a href="http://www.alsacreations.com/article/lire/1641-bonnes-pratiques-en-css-bem-et-oocss.html">une approche OOCSS/BEM</a> rend les éléments plus simples à isoler/documenter.</p>
  516. <p>J’ai tendance à penser que la dette technique est inévitable (mais je n’ai pas encore pris le temps de lire <a href="http://boutique.letrainde13h37.fr/products/la-dette-technique-bastien-jaillot">le bouquin de Bastien</a> sur le sujet ;-)). Partant de ce constat, il y a deux choses à surveiller :</p>
  517. <ul>
  518. <li>que la taille du code reste raisonnable en restant le plus pertinent possible sur la valeur apportée à l’utilisateur</li>
  519. <li>qu’il n’y ait pas de relâchement sur la qualité en cas de rush ou de fatigue, situations où l’on a tendance à débrayer</li>
  520. </ul>
  521. <h3>Cas particulier : The Big Rewrite™</h3>
  522. <blockquote>
  523. <p>It’s important to remember that when you start from scratch there is <strong>absolutely no reason</strong> to believe that you are going to do a better job than you did the first time. First of all, you probably don’t even have the same programming team that worked on version one, so you don’t actually have « more experience ». You’re just going to make most of the old mistakes again, and introduce some new problems that weren’t in the original version. </p>
  524. <p><cite><em><a href="http://www.joelonsoftware.com/articles/fog0000000069.html">Things You Should Never Do, Part I</a></em> (<a href="/david/cache/136e6aaaf23c95e360dcf6428dade480/">cache</a>)</cite></p>
  525. </blockquote>
  526. <p>C’est quelque chose que j’ai tenté dans une précédente vie lorsque j’ai réécrit une application de gestion de traçabilité dédiée aux laboratoires. Avec 10 années de recul, je pense que c’était une erreur. Non pas de réécrire mais de ne pas avoir évalué l’inertie du changement de culture associé en interne pour combiner une réécriture ET une simplification.</p>
  527. <p>Si vous <a href="http://blog.aelogica.com/development/rewrite-not-question/">prenez cette voie</a> (<a href="/david/cache/effe69ce88eb3a3bc286767b15197de3/">cache</a>), documentez chaque raison de la réécriture et chaque décision prise au cours de celle-ci.</p>
  528. <h2>Discussion</h2>
  529. <blockquote>
  530. <p>Read the Docs, sadly, is still heavily dependent on me being around to operate it. Our campaign to make it more sustainable is the first step towards hopefully fixing this situation. Operating and supporting the site has started to take most of my time, not leaving much room for building community and on boarding contributors. In fact, it leads to an implicitly hostile environment for new contributors, because nobody was around to help them, test their patches, and get changes into the codebase.</p>
  531. <p><cite><em><a href="http://ericholscher.com/blog/2015/apr/10/making-read-the-docs-sustainable/">Making Read the Docs Sustainable</a></em> (<a href="/david/cache/bb89afe29cd75835fd7df428b44b9759/">cache</a>)</cite></p>
  532. </blockquote>
  533. <p>La matinée de formation a été suivie d’une après-midi d’accompagnement qui nous a permis de mettre en application un <em>workflow</em> de travail basé sur git(hub) pour encourager les revues de code et les participations externes.</p>
  534. <p>J’en retiens trois discussions, la première sur le financement de l’<em>open-source</em> où il a été question de développement à base de <em>crowdfounding</em>, voire de <em>companyfounding</em> pour mutualiser les coûts ? Cela m’a rappelé la discussion que nous avions eue avec <a href="http://simons.fr/">Simon</a> lors de <a href="http://moustic.info/2015/wakka.php?wiki=PagePrincipale">MousTIC</a> sur <a href="http://unisson.co/fr/wiki/prestation/">le financement des communs</a> (<a href="/david/cache/817d232d98ee6c2e5f266582c3c47628/">cache</a>).</p>
  535. <p>La seconde portait sur la gestion de l’inclusion de modules non <em>open-source</em> dans un projet <em>open-source</em>, je n’ai pas de solution pour cela à part essayer de faire une architecture qui permet l’inclusion de <em>plugins</em> mais cela reste limité. Si vous avez des suggestions/expériences je suis preneur.</p>
  536. <p>Enfin, la dernière était relative à l’inclusion des contributeurs externes sur un projet <em>open-source</em>, en plus de la documentation d’installation du projet déjà évoquée, j’ai proposé :</p>
  537. <ul>
  538. <li>documenter pourquoi est-ce que le projet a été mis en open-source et quelles sont les attentes au niveau des contributions</li>
  539. <li>documenter comment est-ce que le projet peut recevoir des améliorations (quel <em>workflow</em>, quels tests, quelle réactivité)</li>
  540. <li>identifier clairement des bugs qui sont faciles à prendre pour rentrer dans le projet</li>
  541. <li>proposer d’avoir une position de <em>mentor</em> sur certains bugs pour accompagner les débutants</li>
  542. <li>discuter de manière ouverte les décisions et les orientations du projet avec la communauté</li>
  543. </ul>
  544. <p>C’est un travail fastidieux qui ne porte ses fruits que sur le long terme et dont le retour sur investissement est difficilement quantifiable (recrutement, renommée, etc). Mais quelle joie pour l’équipe lorsque ça prend ! L’ensemble de ces réflexions m’ont amené à écrire <a href="https://github.com/davidbgk/open-source-template">un gabarit de README pour projets open-source</a> encourageant la participation. N’hésitez pas à contribuer ;-).</p>
  545. <blockquote>
  546. <p>Each developer in your company should have two days per month to work on the open source software your product is built on.</p>
  547. <p>[…]</p>
  548. <p>The real reason you should do it is because it will make your product better, make your developers better, and make your company a more attractive place to work.</p>
  549. <p><cite><em><a href="http://twodaymanifesto.com/">The Two Day Manifesto</a></em> (<a href="/david/cache/73fc8424bbfbf91a8f75db7060a24aea/">cache</a>)</cite></p>
  550. </blockquote>
  551. <p>Au final, c’était un plaisir d’accompagner une équipe dans sa recherche de pertinence autour de <a href="https://github.com/NaturalSolutions">ses produits</a> <em>open-source</em>, j’espère vraiment que l’on pourra inscrire cette relation de confiance dans la durée.</p>
  552. </article>
  553. <figure class="image" property="schema:image">
  554. <img src="/static/david/blog/2015/collaboration-technique.jpg" alt="" />
  555. </figure>
  556. <nav id="jumpto">
  557. <p>
  558. <a rel=prev href="/david/blog/2015/coworking-transition/">← Co-working en transition</a> | <a href="/david/blog/" title="Retour à la liste des expériences">↑</a> | <a rel=next href="/david/blog/2015/chiffrer-emails/">Chiffrer ses emails →</a>
  559. </p>
  560. </nav>
  561. <footer>
  562. <div>
  563. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  564. <p>
  565. Bonjour/Hi!
  566. 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>
  567. 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>).
  568. </p>
  569. <p>
  570. 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>.
  571. </p>
  572. <p>
  573. Les dernières publications hebdomadaires sont :
  574. </p>
  575. <ul class="with_columns">
  576. <li>
  577. <a href="/david/stream/2019/12/31/">Merci</a>
  578. </li>
  579. <li>
  580. <a href="/david/stream/2019/12/27/">Intemporels</a>
  581. </li>
  582. <li>
  583. <a href="/david/stream/2019/12/24/">Outils</a>
  584. </li>
  585. <li>
  586. <a href="/david/stream/2019/12/17/">Origines</a>
  587. </li>
  588. <li>
  589. <a href="/david/stream/2019/12/10/">Publier</a>
  590. </li>
  591. <li>
  592. <a href="/david/stream/2019/12/03/">En forêt</a>
  593. </li>
  594. <li>
  595. <a href="/david/stream/2019/11/26/">Ecocentric</a>
  596. </li>
  597. <li>
  598. <a href="/david/stream/2019/11/19/">Se livrer</a>
  599. </li>
  600. <li>
  601. <a href="/david/stream/2019/11/12/">Dépendances</a>
  602. </li>
  603. <li>
  604. <a href="/david/stream/2019/11/05/">Positif</a>
  605. </li>
  606. <li>
  607. <a href="/david/stream/2019/10/29/">Dettes</a>
  608. </li>
  609. <li>
  610. <a href="/david/stream/2019/10/22/">Privilèges</a>
  611. </li>
  612. <li>
  613. <a href="/david/stream/2019/10/15/">Discrétion</a>
  614. </li>
  615. <li>
  616. <a href="/david/stream/2019/10/08/">Désespérance</a>
  617. </li>
  618. <li>
  619. <a href="/david/stream/2019/10/01/">Présent</a>
  620. </li>
  621. <li>
  622. <a href="/david/stream/2019/09/24/">Manifester</a>
  623. </li>
  624. <li>
  625. <a href="/david/stream/2019/09/17/">Arpenter</a>
  626. </li>
  627. <li>
  628. <a href="/david/stream/2019/09/10/">Nostalgie</a>
  629. </li>
  630. <li>
  631. <a href="/david/stream/2019/09/03/">Déconstruire</a>
  632. </li>
  633. <li>
  634. <a href="/david/stream/2019/08/27/">Documenter</a>
  635. </li>
  636. <li>
  637. <a href="/david/stream/2019/08/20/">Frustration</a>
  638. </li>
  639. <li>
  640. <a href="/david/stream/2019/08/13/">Holisme</a>
  641. </li>
  642. <li>
  643. <a href="/david/stream/2019/08/06/">1%</a>
  644. </li>
  645. <li>
  646. <a href="/david/stream/2019/07/30/">Exemplarité</a>
  647. </li>
  648. <li>
  649. <a href="/david/stream/2019/07/23/">Timelines</a>
  650. </li>
  651. <li>
  652. <a href="/david/stream/2019/07/16/">Écoute</a>
  653. </li>
  654. <li>
  655. <a href="/david/stream/2019/07/02/">Anxiété</a>
  656. </li>
  657. <li>
  658. <a href="/david/stream/2019/06/21/">À lier</a>
  659. </li>
  660. <li>
  661. <a href="/david/stream/2019/06/14/">Pauvreté</a>
  662. </li>
  663. <li>
  664. <a href="/david/stream/2019/06/07/">Amateur</a>
  665. </li>
  666. <li>
  667. <a href="/david/stream/2019/05/31/">Pollution</a>
  668. </li>
  669. <li>
  670. <a href="/david/stream/2019/05/24/">Apaisement</a>
  671. </li>
  672. <li>
  673. <a href="/david/stream/2019/05/10/">Folie</a>
  674. </li>
  675. <li>
  676. <a href="/david/stream/2019/05/03/">Sympathie</a>
  677. </li>
  678. <li>
  679. <a href="/david/stream/2019/04/12/">Péremption</a>
  680. </li>
  681. <li>
  682. <a href="/david/stream/2019/04/05/">Définitions</a>
  683. </li>
  684. <li>
  685. <a href="/david/stream/2019/03/29/">Acceptation</a>
  686. </li>
  687. <li>
  688. <a href="/david/stream/2019/03/22/">Dissonance</a>
  689. </li>
  690. <li>
  691. <a href="/david/stream/2019/03/15/">Reconnaissance</a>
  692. </li>
  693. <li>
  694. <a href="/david/stream/2019/03/08/">Lecture</a>
  695. </li>
  696. <li>
  697. <a href="/david/stream/2019/03/01/">Journaux</a>
  698. </li>
  699. <li>
  700. <a href="/david/stream/2019/02/22/">Écriture</a>
  701. </li>
  702. <li>
  703. <a href="/david/stream/2019/02/15/">Kyriarchie</a>
  704. </li>
  705. <li>
  706. <a href="/david/stream/2019/02/08/">Mots-serrures</a>
  707. </li>
  708. <li>
  709. <a href="/david/stream/2019/02/01/">Sans voie</a>
  710. </li>
  711. <li>
  712. <a href="/david/stream/2019/01/25/">Auto-diagnostic</a>
  713. </li>
  714. <li>
  715. <a href="/david/stream/2019/01/18/">Agilité</a>
  716. </li>
  717. <li>
  718. <a href="/david/stream/2019/01/11/">Métaphores</a>
  719. </li>
  720. <li>
  721. <a href="/david/stream/2019/01/04/">Balbutiements</a>
  722. </li>
  723. </ul>
  724. <p>
  725. Voici quelques articles choisis :
  726. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  727. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  728. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  729. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  730. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  731. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  732. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  733. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  734. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  735. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  736. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  737. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  738. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  739. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  740. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  741. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  742. </p>
  743. <p>
  744. 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>.
  745. </p>
  746. <p>
  747. Je ne traque pas ta navigation mais mon
  748. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  749. conserve des logs d’accès.
  750. </p>
  751. </div>
  752. </footer>
  753. <script type="text/javascript">
  754. ;(_ => {
  755. const jumper = document.getElementById('jumper')
  756. jumper.addEventListener('click', e => {
  757. e.preventDefault()
  758. const anchor = e.target.getAttribute('href')
  759. const targetEl = document.getElementById(anchor.substring(1))
  760. targetEl.scrollIntoView({behavior: 'smooth'})
  761. })
  762. })()
  763. </script>
  764. <script>
  765. /* Service workers */
  766. if (navigator.serviceWorker) {
  767. window.addEventListener('load', function () {
  768. var selector = 'a[href^="/david/cache/"], a[rel=prev], a[rel=next]'
  769. function sendLinks (selector) {
  770. var links = [].slice.call(document.querySelectorAll(selector)).map(function (link) {
  771. return link.getAttribute('href')
  772. })
  773. links.push(location.pathname) // Put the current page in cache too.
  774. navigator.serviceWorker.controller.postMessage({ links: links })
  775. }
  776. navigator.serviceWorker.getRegistration()
  777. .then(function (registration) {
  778. if (!registration || !navigator.serviceWorker.controller) {
  779. return navigator.serviceWorker.register('/serviceworker.js')
  780. .then(navigator.serviceWorker.ready)
  781. .then(function () {
  782. console.log('[ServiceWorker] Ready to go!')
  783. })
  784. .catch(console.error.bind(console))
  785. } else {
  786. console.log('[ServiceWorker] Send links via registration')
  787. sendLinks(selector)
  788. }
  789. })
  790. navigator.serviceWorker.addEventListener('controllerchange', function () {
  791. console.log('[ServiceWorker] Send links via controller change')
  792. sendLinks(selector)
  793. })
  794. navigator.serviceWorker.addEventListener('message', function (event) {
  795. var link = document.querySelector('a[href="' + event.data.link + '"]')
  796. if (event.data.status && link) {
  797. link.style.backgroundColor = '#2d7474'
  798. link.style.color = '#f0f0ea'
  799. link.setAttribute('title', 'En cache pour consultation sans connexion')
  800. }
  801. })
  802. })
  803. } else {
  804. console.warn('[ServiceWorker] No cache for old browsers.')
  805. }
  806. </script>