<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sébastien Roccaserra</title>
    <description>The latest articles on DEV Community by Sébastien Roccaserra (@sroccaserra).</description>
    <link>https://dev.to/sroccaserra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F346174%2F9e74b0d0-e372-458e-99c7-1fd2332e07b1.jpeg</url>
      <title>DEV Community: Sébastien Roccaserra</title>
      <link>https://dev.to/sroccaserra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sroccaserra"/>
    <language>en</language>
    <item>
      <title>Blog inactif + nouvelle adresse</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Mon, 10 Jul 2023 10:10:59 +0000</pubDate>
      <link>https://dev.to/sroccaserra/blog-inactif-nouvelle-adresse-2ll</link>
      <guid>https://dev.to/sroccaserra/blog-inactif-nouvelle-adresse-2ll</guid>
      <description>&lt;p&gt;Bonjour, si vous visitez ce blog, pour information il n'est plus actif et mon activité principale est sur :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sroccaserra.github.io/"&gt;https://sroccaserra.github.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Hello, if you come across this blog, please understand that it is not active anymore, and you might want to visit my new blog instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sroccaserra.github.io/"&gt;https://sroccaserra.github.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Numérique et accords de Paris</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Thu, 18 May 2023 13:50:25 +0000</pubDate>
      <link>https://dev.to/sroccaserra/numerique-et-accords-de-paris-2mn6</link>
      <guid>https://dev.to/sroccaserra/numerique-et-accords-de-paris-2mn6</guid>
      <description>&lt;p&gt;J'ai été intéressé par cet article du Monde dans lequel on trouve que pour respecter les accords de Paris, il faudrait réduire les émissions de gaz à effet de serre du secteur du numérique de 7 % par an jusqu'en 2050. On y lit aussi que le rythme actuel est une augmentation des émissions du secteur de 6 % par an. On ne va pas dans la bonne direction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.lemonde.fr/sciences/article/2023/05/01/la-recherche-au-defi-de-la-sobriete-energetique-du-numerique_6171677_1650684.html"&gt;La recherche au défi de la sobriété énergétique du numérique&lt;/a&gt; ~ (réservé aux abonné·es)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;+ 6 %&lt;/code&gt;, &lt;code&gt;- 7 %&lt;/code&gt;, on ne se rend pas compte mais les deux sont énormes car les courbes sont exponentielles (principe des intérêts cumulés). Pour visualiser un peu les ordres de grandeur, j'ai fait rapidement ces deux courbes qui montrent :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le résultat de la croissance actuelle si elle est maintenue jusqu'en 2050 = multiplier nos émissions actuelles par presque 5&lt;/li&gt;
&lt;li&gt;le résultat de la baisse nécessaire si on commençait aujourd’hui (ce qui est loin d’être le cas) = diviser nos émissions actuelles par plus de 5&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note : l’axe vertical part bien de zéro, on doit réellement arriver bien bas si on veut respecter les accords de Paris.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OwZydPPP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdt7m7eu1m48sbaumb58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OwZydPPP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdt7m7eu1m48sbaumb58.png" alt="Image description" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On lit aussi dans l'article que les émissions de gaz à effet de serre ne sont pas le seul impact négatif du secteur du numérique dans la catastrophe écologique actuelle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Ça me paraît évident que :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prendre des décisions stratégique en se fiant au rythme de croissance actuel du secteur du numérique n'est pas réaliste.&lt;/li&gt;
&lt;li&gt;Les sociétés doivent prendre conscience du problème et agir ASAP dans la bonne direction et avec les bons ordres de grandeur.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Savoir construire ses outils</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Sun, 14 May 2023 10:18:57 +0000</pubDate>
      <link>https://dev.to/sroccaserra/savoir-construire-ses-outils-3i9c</link>
      <guid>https://dev.to/sroccaserra/savoir-construire-ses-outils-3i9c</guid>
      <description>&lt;p&gt;Parfois, face à une situation nouvelle que je ne comprends pas, j’ai besoin de voir le problème sous un autre angle. Dans ces conditions, je suis content d’utiliser tous les outils que j’ai déjà. Et parfois, quand mes outils habituels ne sont pas adaptés, savoir fabriquer très vite un nouvel outil spécifique à la situation est très utile.&lt;/p&gt;

&lt;p&gt;Voici le dernier exemple en date, où j’ai eu besoin de visualiser des événements sur un axe temporel pour les corréler et chercher des patterns.&lt;/p&gt;

&lt;p&gt;Dans ma mission actuelle, nous avons eu besoin d’établir une connexion VPN entre un serveur applicatif et un serveur SMTP pour envoyer des emails. Et nous avons détecté que cette connexion était défectueuse régulièrement, car environ un email sur dix ne partait pas : le serveur applicatif n’arrivait pas à joindre le serveur SMTP.&lt;/p&gt;

&lt;p&gt;Autre point important : sur ce projet on n’a pas encore pris le temps ni le budget de s’outiller sur l’analyse de logs, avec des outils du marché comme Datadog par exemple. Ces outils fonctionnent très bien et je les recommande, mais nous ne les avons pas encore sous la main, d’où cette démarche un peu plus rustique.&lt;/p&gt;

&lt;p&gt;Voilà la démarche que j’ai suivie pour rendre visible les patterns de perte de connexion :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;activer les logs de ping VPN&lt;/li&gt;
&lt;li&gt;extraire des logs de ping les événements qui m’intéressent&lt;/li&gt;
&lt;li&gt;dessiner un graph avec tous ces événements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exploration des logs
&lt;/h3&gt;

&lt;p&gt;Une connexion VPN, pour se maintenir, envoie à interval régulier des pings entre client et serveur. J’ai activé les logs de ces pings du point de vue de mon serveur applicatif. J’ai donc dans mes logs des pings envoyés et des pings reçus.&lt;/p&gt;

&lt;p&gt;Ces logs sont très verbeux, ils contiennent beaucoup d’information. En plissant les yeux, on peut voir dans ces lignes des lignes ‘RECEIVED PING PACKET’ et ‘SENT PING’. On peut voire aussi des lignes ‘Inactivity timeout’ et ‘ping-restart’ : ce sont les lignes qui montrent le problème, le redémarrage fréquent de la connexion VPN qui causait les pertes d’emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023-05-12 17:21:28.999239998 +0200 CEST [web-1] 2023-05-12 15:21:28 us=359574 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:21:28.999245803 +0200 CEST [web-1] 2023-05-12 15:21:28 us=364929 UDP READ [41] from [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:21:28.999246770 +0200 CEST [web-1] 2023-05-12 15:21:28 us=364965 TLS: tls_pre_decrypt, key_id=0, IP=[AF_INET]12.34.56.789:1194
2023-05-12 17:21:28.999247475 +0200 CEST [web-1] 2023-05-12 15:21:28 us=364984 PID_TEST [0] [SSL-0] [] 0:0 0:1 t=1683904888[0] r=[0,64,15,0,1] sl=[0,0,64,528]
2023-05-12 17:21:28.999248049 +0200 CEST [web-1] 2023-05-12 15:21:28 us=364993 RECEIVED PING PACKET
2023-05-12 17:21:39.000111417 +0200 CEST [web-1] 2023-05-12 15:21:38 us=479455 TLS: tls_pre_encrypt: key_id=0
2023-05-12 17:21:39.000143400 +0200 CEST [web-1] 2023-05-12 15:21:38 us=479527 SENT PING
2023-05-12 17:21:39.000152558 +0200 CEST [web-1] 2023-05-12 15:21:38 us=479567 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:21:49.001203807 +0200 CEST [web-1] 2023-05-12 15:21:48 us=710285 TLS: tls_pre_encrypt: key_id=0
2023-05-12 17:21:49.001231283 +0200 CEST [web-1] 2023-05-12 15:21:48 us=710359 SENT PING
2023-05-12 17:21:49.001235143 +0200 CEST [web-1] 2023-05-12 15:21:48 us=710413 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:21:59.002236188 +0200 CEST [web-1] 2023-05-12 15:21:58 us=745173 TLS: tls_pre_encrypt: key_id=0
2023-05-12 17:21:59.002246836 +0200 CEST [web-1] 2023-05-12 15:21:58 us=745261 SENT PING
2023-05-12 17:21:59.002287152 +0200 CEST [web-1] 2023-05-12 15:21:58 us=745302 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:22:09.003668616 +0200 CEST [web-1] 2023-05-12 15:22:08 us=957512 TLS: tls_pre_encrypt: key_id=0
2023-05-12 17:22:09.003689111 +0200 CEST [web-1] 2023-05-12 15:22:08 us=957579 SENT PING
2023-05-12 17:22:09.003693708 +0200 CEST [web-1] 2023-05-12 15:22:08 us=957618 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:22:20.004539495 +0200 CEST [web-1] 2023-05-12 15:22:19 us=79677 TLS: tls_pre_encrypt: key_id=0
2023-05-12 17:22:20.004544393 +0200 CEST [web-1] 2023-05-12 15:22:19 us=79749 SENT PING
2023-05-12 17:22:20.004545188 +0200 CEST [web-1] 2023-05-12 15:22:19 us=79805 UDP WRITE [41] to [AF_INET]12.34.56.789:1194: P_DATA_V2 kid=0 DATA len=40
2023-05-12 17:22:29.005460487 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327448 [OpenVPN-Server-mon-serveur-vpn.com] Inactivity timeout (--ping-restart), restarting
2023-05-12 17:22:29.005472487 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327506 PID packet_id_free
2023-05-12 17:22:29.005495216 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327605 PID packet_id_free
2023-05-12 17:22:29.005500324 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327617 PID packet_id_free
2023-05-12 17:22:29.005501491 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327624 PID packet_id_free
2023-05-12 17:22:29.005552612 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327640 PID packet_id_free
2023-05-12 17:22:29.005555228 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327647 PID packet_id_free
2023-05-12 17:22:29.005555749 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327654 PID packet_id_free
2023-05-12 17:22:29.005556455 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327661 PID packet_id_free
2023-05-12 17:22:29.005568587 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327672 TCP/UDP: Closing socket
2023-05-12 17:22:29.005569472 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327690 PID packet_id_free
2023-05-12 17:22:29.005574817 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327709 SIGUSR1[soft,ping-restart] received, process restarting
2023-05-12 17:22:29.005575207 +0200 CEST [web-1] 2023-05-12 15:22:28 us=327727 Restart pause, 5 second(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On peut déjà en tirer des informations mais ce n’est pas très pratique : dans l’exemple il y a une ou deux minutes de logs, et pour mon problème j’ai besoin d’analyser un bon quart d’heure pour chercher des patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transformation des logs
&lt;/h3&gt;

&lt;p&gt;J’ai commencé par extraire les événements qui m’intéressent avec &lt;code&gt;rg&lt;/code&gt; (un genre de &lt;code&gt;grep&lt;/code&gt;) et à les structurer pour isoler le timestamp.&lt;/p&gt;

&lt;p&gt;Isoler les envois de pings :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s1"&gt;'SENT PING'&lt;/span&gt; logs
99694:2023-05-12 17:20:53.995533501 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:20:53 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;775530 SENT PING
99697:2023-05-12 17:21:03.996518529 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:03 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;991544 SENT PING
99739:2023-05-12 17:21:18.998396048 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:18 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;82178 SENT PING
99833:2023-05-12 17:21:28.999163512 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:28 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;359535 SENT PING
99840:2023-05-12 17:21:39.000143400 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:38 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;479527 SENT PING
99843:2023-05-12 17:21:49.001231283 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:48 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;710359 SENT PING
99846:2023-05-12 17:21:59.002246836 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:21:58 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;745261 SENT PING
99849:2023-05-12 17:22:09.003689111 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:22:08 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;957579 SENT PING
99852:2023-05-12 17:22:20.004544393 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:22:19 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;79749 SENT PING
99894:2023-05-12 17:22:34.006820819 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:22:33 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;328337 SENT PING
99992:2023-05-12 17:22:44.007459739 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:22:43 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;490301 SENT PING
99995:2023-05-12 17:22:54.008443004 +0200 CEST &lt;span class="o"&gt;[&lt;/span&gt;web-1] 2023-05-12 15:22:53 &lt;span class="nv"&gt;us&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;684270 SENT PING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extraire le timestamp avec &lt;code&gt;cut&lt;/code&gt; (on délimite sur les espaces et on prend les deux premiers champs)  :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s1"&gt;'SENT PING'&lt;/span&gt; logs | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1-2&lt;/span&gt;
2023-05-12 17:20:53.995533501
2023-05-12 17:21:03.996518529
2023-05-12 17:21:18.998396048
2023-05-12 17:21:28.999163512
2023-05-12 17:21:39.000143400
2023-05-12 17:21:49.001231283
2023-05-12 17:21:59.002246836
2023-05-12 17:22:09.003689111
2023-05-12 17:22:20.004544393
2023-05-12 17:22:34.006820819
2023-05-12 17:22:44.007459739
2023-05-12 17:22:54.008443004
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ajouter le marqueur ‘ &amp;gt;’ (pour signifier “envoyé”) en fin de ligne, et envoyer le résultat dans un fichier “sent” (ici avec &lt;code&gt;tee&lt;/code&gt; pour visualiser le résultat en même temps) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s1"&gt;'SENT PING'&lt;/span&gt; logs | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1-2&lt;/span&gt; | rg &lt;span class="s1"&gt;'$'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;' &amp;gt;'&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;sent
2023-05-12 17:20:53.995533501 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:03.996518529 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:18.998396048 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:28.999163512 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:39.000143400 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:49.001231283 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:59.002246836 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:09.003689111 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:20.004544393 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:34.006820819 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:44.007459739 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:54.008443004 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Faire la même chose avec les pings reçus :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s1"&gt;'RECEIVED PING'&lt;/span&gt; logs | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1-2&lt;/span&gt; | rg &lt;span class="s1"&gt;'$'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;' &amp;lt; Received'&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;received
2023-05-12 17:20:12.991392556 &amp;lt; Received
2023-05-12 17:21:28.999248049 &amp;lt; Received
2023-05-12 17:22:44.007432531 &amp;lt; Received
2023-05-12 17:22:54.008453476 &amp;lt; Received
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et les redémarrages de session VPN :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rg &lt;span class="s1"&gt;'12 17:.*Inactivity timeout'&lt;/span&gt; logs | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1-2&lt;/span&gt; | rg &lt;span class="s1"&gt;'$'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;' * Restart !!'&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;restart
2023-05-12 17:21:13.997629899 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
2023-05-12 17:22:29.005460487 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite on peut joindre les fichiers avec &lt;code&gt;cat&lt;/code&gt; et les trier (par date puisque c’est le début de ligne) dans un fichier “history”::&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;received sent restarts | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;history&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat history
&lt;/span&gt;2023-05-12 17:20:53.995533501 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:03.996518529 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:13.997629899 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
2023-05-12 17:21:18.998396048 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:28.999163512 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:28.999248049 &amp;lt; Received
2023-05-12 17:21:39.000143400 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:49.001231283 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:21:59.002246836 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:09.003689111 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:20.004544393 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:29.005460487 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
2023-05-12 17:22:34.006820819 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:44.007432531 &amp;lt; Received
2023-05-12 17:22:44.007459739 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:54.008443004 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-12 17:22:54.008453476 &amp;lt; Received
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C’est déjà plus lisible, on peut maintenant commencer à corréler et voir des patterns. En plissant les yeux, on peut voir que les pings envoyés sont beaucoup plus fréquents que les pings reçus.&lt;/p&gt;

&lt;p&gt;On peut voir aussi que le redémarrage de 17h22 survient une minute et une seconde après le dernier ping reçu : ça correspond à notre configuration VPN : envoyez-vous des pings toutes les 10 secondes, et redémarrez si vous n’avez pas de nouvelles après 1 mn. Mais j’avais envie de voir ces infos sur un graph pendant une longue période pour mieux confirmer mes intuitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Affichage dans un graph
&lt;/h3&gt;

&lt;p&gt;Ça tombe bien, ça fait longtemps que j’avais envie d’explorer l’aspect visuel de Smalltalk, un petit langage qui contient tout ce qu’il faut pour générer des images et les afficher out of the box (petit, en effet : 30 Mo le zip avec la VM, la lib standard, toutes les sources, et un IDE puissant intégré !).&lt;/p&gt;

&lt;p&gt;J’ai donc exploré un peu et j’ai trouvé une classe &lt;code&gt;RSChart&lt;/code&gt; qui pouvait afficher des &lt;code&gt;RSLinePlot&lt;/code&gt; et des &lt;code&gt;RSScatterPlot&lt;/code&gt;. Dans les commentaires de la classe il y a même tout ce qu’il faut pour démarrer.&lt;/p&gt;

&lt;p&gt;En explorant quelques minutes de plus, j’ai trouvé comment ajouter des marqueurs, et en peu de temps j’avais ce résultat à partir de mes logs, exactement ce dont j’avais besoin :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--885Bnhwy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4s2pvj6k6qkfd7babusp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--885Bnhwy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4s2pvj6k6qkfd7babusp.png" alt="Image description" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On peut voir qu’il y a effectivement un pattern super régulier : mon client VPN reçoit des pings du serveur pendant quelques dizaines de secondes, ne reçoit pas les pings du serveur pendant une minute et redémarre. Et ainsi de suite.&lt;/p&gt;

&lt;p&gt;En réfléchissant un peu, j’ai pu faire l’hypothèse qu’il y avait une autre machine qui utilisait la même configuration. Quand le serveur voyait cette autre machine arriver, il lui se mettait à lui envoyer les pings et mon serveur ne les recevait plus. Ma connexion redémarre, le serveur m’envoie les pings, (donc l’autre machine ne les reçoit plus et redémarre), et ainsi de suite, à toi, à moi, etc.&lt;/p&gt;

&lt;p&gt;Et bingo, j’avais bien utilisé la même conf VPN sur deux de mes serveurs qui se volaient la vedette à chacun leur tour 🤦. Problème enfin résolu ! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Pour résumer, bien connaître GNU core-utils et un langage de programmation un peu visuel permet de se fabriquer ses propres outils spécifiques rapidement et à peu de frais, à tel point que je ne “capitalise” pas sur ces outils, je n’en fait surtout pas une librairie. Je conserve quelques snippets pour me rappeler rapidement les détails, et je me reconstruis un outil vite fait à chaque fois, en jetant parfois un œil à un de mes vieux snippets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Annexe
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Version awk
&lt;/h4&gt;

&lt;p&gt;On peut aussi faire toute la phase d’exploration de logs en une seule ligne de awk :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &amp;lt;logs &lt;span class="s1"&gt;'/SENT PING/ { print $1 $2 " &amp;gt;" } ; /RECEIVED PING/ { print $1 $2 " &amp;lt; Received"} ; /Inactivity/ { print $1 $2 " * Restart !!" }'&lt;/span&gt;
2023-05-1217:20:43.994461854 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:20:53.995533501 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:03.996518529 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:13.997629899 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
2023-05-1217:21:18.998396048 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:28.999163512 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:28.999248049 &amp;lt; Received
2023-05-1217:21:39.000143400 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:49.001231283 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:21:59.002246836 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:09.003689111 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:20.004544393 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:29.005460487 &lt;span class="k"&gt;*&lt;/span&gt; Restart &lt;span class="o"&gt;!!&lt;/span&gt;
2023-05-1217:22:34.006820819 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:44.007432531 &amp;lt; Received
2023-05-1217:22:44.007459739 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:54.008443004 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
2023-05-1217:22:54.008453476 &amp;lt; Received
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Code du graph en Pharo Smalltalk
&lt;/h4&gt;

&lt;p&gt;Si vous connaissez Ruby, une bonne partie de Smalltalk va vous paraître familière : les collections, les blocks de code, et le paradigme "tout est message".&lt;/p&gt;

&lt;p&gt;Exemple de graph qui affiche une sinusoïde :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight smalltalk"&gt;&lt;code&gt;&lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;-3.14&lt;/span&gt; &lt;span class="nf"&gt;to:&lt;/span&gt; &lt;span class="m"&gt;3.14&lt;/span&gt; &lt;span class="nf"&gt;by:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nc"&gt;RSChart&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addPlot:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSLinePlot&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x:&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nf"&gt;y:&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSChartTitleDecoration&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;fontSize:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSXLabelDecoration&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'My X Axis'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;fontSize:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSYLabelDecoration&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'My Y Axis'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;fontSize:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pkxHgzQk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ihh1e931x55fxc02j75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pkxHgzQk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ihh1e931x55fxc02j75.png" alt="Graph de sinus" width="613" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà tout le code écrit à l’arrache pour le graph des redémarrages. Ça tient en une vingtaine de lignes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight smalltalk"&gt;&lt;code&gt;&lt;span class="nv"&gt;file&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nc"&gt;FileSystem&lt;/span&gt; &lt;span class="nf"&gt;disk&lt;/span&gt; &lt;span class="nf"&gt;workingDirectory&lt;/span&gt;
        &lt;span class="nf"&gt;/&lt;/span&gt; &lt;span class="s"&gt;'Developer/project/history'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nv"&gt;file&lt;/span&gt; &lt;span class="nf"&gt;contents&lt;/span&gt; &lt;span class="nf"&gt;lines&lt;/span&gt; &lt;span class="nf"&gt;allButFirst:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nc"&gt;RSChart&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;xS&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;lines&lt;/span&gt; &lt;span class="nf"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="nf"&gt;endsWith:&lt;/span&gt; &lt;span class="s"&gt;' &amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;collect:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateAndTime&lt;/span&gt; &lt;span class="nf"&gt;fromString:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="nf"&gt;first:&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nf"&gt;asUnixTime&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addPlot:&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSScatterPlot&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x:&lt;/span&gt; &lt;span class="nv"&gt;xS&lt;/span&gt; &lt;span class="nf"&gt;y:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderedCollection&lt;/span&gt; &lt;span class="nf"&gt;new:&lt;/span&gt; &lt;span class="nv"&gt;xS&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="nf"&gt;withAll:&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;

&lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;xRc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;lines&lt;/span&gt; &lt;span class="nf"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="nf"&gt;endsWith:&lt;/span&gt; &lt;span class="s"&gt;'Received'&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;collect:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateAndTime&lt;/span&gt; &lt;span class="nf"&gt;fromString:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="nf"&gt;first:&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nf"&gt;asUnixTime&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addPlot:&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSScatterPlot&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x:&lt;/span&gt; &lt;span class="nv"&gt;xRc&lt;/span&gt; &lt;span class="nf"&gt;y:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderedCollection&lt;/span&gt; &lt;span class="nf"&gt;new:&lt;/span&gt; &lt;span class="nv"&gt;xRc&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="nf"&gt;withAll:&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;

&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;lines&lt;/span&gt; &lt;span class="nf"&gt;select:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;each&lt;/span&gt; &lt;span class="nf"&gt;endsWith:&lt;/span&gt; &lt;span class="s"&gt;'Restart !!'&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;collect:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateAndTime&lt;/span&gt; &lt;span class="nf"&gt;fromString:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt; &lt;span class="nf"&gt;first:&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nf"&gt;asUnixTime&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;do:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
    &lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSXMarkerDecoration&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;value:&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;

&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSHorizontalTick&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;labelConversion:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateAndTime&lt;/span&gt; &lt;span class="nf"&gt;fromUnixTime:&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;asTime&lt;/span&gt; &lt;span class="p"&gt;]).&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;addDecoration:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RSXLabelDecoration&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;
      &lt;span class="nf"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Heure'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;fontSize:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;padding:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nf"&gt;@&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le site de Pharo Smalltalk, qui permet de faire tourner ce code out of the box : &lt;a href="https://pharo.org/"&gt;https://pharo.org/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dysfonctionnement du jour : la "recette tardive"</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Thu, 11 May 2023 06:07:28 +0000</pubDate>
      <link>https://dev.to/sroccaserra/dysfonctionnement-du-jour-la-recette-tardive-23gd</link>
      <guid>https://dev.to/sroccaserra/dysfonctionnement-du-jour-la-recette-tardive-23gd</guid>
      <description>&lt;p&gt;Disclaimer: well, that’s just, like, my opinion, man.&lt;/p&gt;

&lt;p&gt;Pourquoi on s’est mis à utiliser des méthodes agiles autour de l’an 2000 ? Parce-que des dysfonctionnements systémiques étaient la norme, quasiment systématiquement, et un peu les mêmes partout. C’est pour répondre à ces dysfonctionnements qui étaient devenus des normes qu’on a changé de façon de penser et de travailler. (By the way, autre opinion gratuite en passant, c’est que “on” = “un faible pourcentage d’équipes”, même aujourd’hui, mais c’est un autre sujet.)&lt;/p&gt;

&lt;p&gt;C’est utile je trouve de connaître ces dysfonctionnements de systèmes, afin de les reconnaître et de les éviter. En effet, j’ai pu observer plusieurs fois que “appliquer” une “méthode” (agile ou pas) comme on suivrait une recette n’est pas la chose qui apporte des résultats, ce qui apporte des résultats c’est l’intention qu’on y met. Donc appliquer une méthode sans chercher à éviter les dysfonctionnement qu’elle est censé éviter c’est passer à côté du problème (cf les équipes qui deviennent “agiles” en calquant les mots “qui vont bien” —sprint, PO, user story, roadmap, etc. — sur un process dysfonctionnel sans trop changer ledit process ou sans s’attaquer aux dysfonctionnements).&lt;/p&gt;

&lt;p&gt;Du coup, je réfléchis à mettre par écrit des exemples de dysfonctionnements pour m’aider à expliquer au client / à la cliente pourquoi je lui demande de travailler d’une certaine façon, et pourquoi c’est important pour lui ou pour elle et pour les personnes qui vont utiliser le produit qu’on est en train de développer ensemble.&lt;/p&gt;

&lt;p&gt;Le dysfonctionnement du jour : la “recette tardive”. En quoi c’est un problème ?&lt;/p&gt;

&lt;p&gt;Ce que j’appelle “la recette tardive”, c’est quand dans un process de développement logiciel :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;il y a une étape “finale” qui “valide” qu’une tâche / un ticket / une story / un PBI (Scrumspeak pour Product Backlog Item) est “terminé”&lt;/li&gt;
&lt;li&gt;au lieu de recetter ces items au fil de l’eau (on dev → on recette le plus vite possible → terminé, etc.), on choisit de (ou donne notre accord pour) le faire plus tard, souvent parce-que “on n’a pas le temps maintenant”&lt;/li&gt;
&lt;li&gt;ces recettes non faites s’accumulent pendant plusieurs jours / semaines&lt;/li&gt;
&lt;li&gt;voilà, on a une “recette tardive”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Autre mécanisme de recette tardive observé :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“on a bien X et Y mais il manque encore Z, je ferai la recette quand il y aura tout pour être bien sûr”&lt;/li&gt;
&lt;li&gt;parfois, le “quand il y aura tout” = quand “l’application sera terminée”. On attend une livraison “finale”, livrée “clé en main” et on fera la “vraie recette” là dessus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alors, je me demandais pourquoi c’est un problème ? Quand on retarde la recette à plus tard voire au dernier moment au lieu de recetter au fur et à mesure, alors on met en risque le produit. J’ai mis ici quelques réflexions par écrit pour tenter de rendre visible cette mise en risque du produit. J’essaie de montrer un peu les enchaînements de conséquences que j’ai observées, pas forcément dans l’ordre, mais au ralentit.&lt;/p&gt;

&lt;p&gt;Quand on retarde la recette, alors :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on détecte tard des défauts qui datent de longtemps&lt;/li&gt;
&lt;li&gt;on perd l’empathie utilisateur : quand ce qu’on fait ne va pas en prod rapidement, on perd le contact avec les personnes qui ont besoin du produit&lt;/li&gt;
&lt;li&gt;on accumule le travail en cours&lt;/li&gt;
&lt;li&gt;plein de travail en cours ⇒ plus personne ne sait réellement ce qui reste à faire&lt;/li&gt;
&lt;li&gt;le pire c’est qu’on s’habitue à vivre avec un reste à faire flou, des tickets qui s’éternisent avant de passer en Done, et des utilisateurs tellement loins de nous dans le temps qu’ils ne sont même plus dans nos pensés ⇒ plus rien n’est important ⇒ ça ou autre chose, bof : en perdant l’objectif on perd la motivation&lt;/li&gt;
&lt;li&gt;en rendant flou le reste à faire, on perd complètement prise sur notre capacité à réaliser X en un temps Y&lt;/li&gt;
&lt;li&gt;comme on ne va pas se tourner les pouces en attendant la recette, on se dépêche de commencer plein d’autres sujets (~ unpopular opinion : et pourtant dans cette situation se tourner les pouces serait en réalité “moins pire” pour le résultat final)&lt;/li&gt;
&lt;li&gt;en commençant beaucoup de choses sans les terminer, on a l’impression d’aller vite car on est occupé à 100 %&lt;/li&gt;
&lt;li&gt;mais en réalité on ne fait que créer une illusion sur notre capacité à faire en nous maintenant dans un sentiment d’urgence sur la mauvaise chose, urgence de commencer pour montrer qu’on est sur le pont, au lieu de l’urgence de terminer ces sujets qui sont devenus “old news” et moins valorisés&lt;/li&gt;
&lt;li&gt;on s’endort jusqu’au réveil, la fin de l’illusion : quand une date importante arrive, on se rend compte qu’on a vécu au dessus de nos moyens&lt;/li&gt;
&lt;li&gt;on détecte les défauts au dernier moment, juste avant l’ouverture de service ⇒ ça met le produit en risque et les équipes en stress au plus mauvais moment. Gros crunch en mode cowboy (allez les requêtes SQL en prod, on y va 🤠) avec aucune garantie de résultat.&lt;/li&gt;
&lt;li&gt;en étant occupé à 100 % en permanence, on perd totalement notre capacité à gérer l’imprévu (note : la survenue de l’imprévu est paradoxalement la chose la plus certaine sur un projet)&lt;/li&gt;
&lt;li&gt;en étant occupé à 100 % en permanence, on rate complètement toutes les opportunités d’améliorer quoi que ce soit qui se présentent tous les jours&lt;/li&gt;
&lt;li&gt;en n’ayant pas le temps pour les améliorations, on ralentit notre vitesse de x % par mois — Attention ! Mefiat ! Beware ! Cave Canem ! Même un ralentissement de 1 % par mois c’est énorme car les intérêts cumulés c’est exponentiel 😨&lt;/li&gt;
&lt;li&gt;l’autre fin d’illusion sur notre capacité à faire, c’est quand l’ajout de nouvelles fonctionnalités devient si pénible qu’on accepte le big rewrite (quelle horreur). Mais si on ne change pas les conditions qui ont conduit à cette situation, on ne changera pas le résultat. Without changing the quality of the “building”, you can’t really change the quality of the thing built.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bref, je n’aime pas les recettes tardives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tPnbXD0c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qy4ep8rol1x4uq08kyi0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tPnbXD0c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qy4ep8rol1x4uq08kyi0.jpg" alt="Image description" width="450" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Découverte du langage Go, court retour d'expérience</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Sat, 16 Jul 2022 12:30:11 +0000</pubDate>
      <link>https://dev.to/sroccaserra/decouverte-du-langage-go-court-retour-dexperience-38a9</link>
      <guid>https://dev.to/sroccaserra/decouverte-du-langage-go-court-retour-dexperience-38a9</guid>
      <description>&lt;p&gt;Cette semaine j'ai fait un peu de Go et j'aime bien, voilà ce que j'ai observé en résolvant quelques problèmes d'Advent of Code.&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;C'est top d'avoir un langage curly brackets qui soit typé statiquement et qu'on peut lancer en ligne de commande aussi facilement que du JavaScript, c'est pas si fréquent que ça, et c'est ce que je recherchais en ce moment, c'est une belle découverte pour moi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plus de détails
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Un langage typé statiquement
&lt;/h4&gt;

&lt;p&gt;Go est un langage typé statiquement, ce qui fait qu'on a beaucoup de feedback en temps réel sur son code, impossible d'oublier un paramètre dans un appel de fonction ou de passer un paramètre du mauvais type par exemple. Ça va plus loin, car s'il y a une variable déclarée mais non inutilisée, le programme ne compile pas. J'apprécie, ça aide à la rigueur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;solve_01_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;huge&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ci-dessus on peut observer la signature de la fonction, &lt;code&gt;func solve_01_1(numbers []int) int&lt;/code&gt;, qui prend en entrée une liste d'entiers (&lt;code&gt;[]int&lt;/code&gt;) et qui renvoit un entier (&lt;code&gt;int&lt;/code&gt;). Du classique.&lt;/p&gt;

&lt;p&gt;On peut observer aussi le mécanisme d'inférence de type à la déclaration des variables bien pratique. Les variables &lt;code&gt;previous&lt;/code&gt; et &lt;code&gt;result&lt;/code&gt; sont déclarées sans type mais sont bien typées statiquement et vérifiées par le type checker. On n'arrive pas au niveau de Haskell qui peut inférer quasi tous les types d'un programme mais c'est déjà bien.&lt;/p&gt;

&lt;h4&gt;
  
  
  Exemple de Generics
&lt;/h4&gt;

&lt;p&gt;Le système de generics a l'air bien, j'ai pu m'en servir assez intuitivement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;///&lt;/span&gt;
&lt;span class="c"&gt;// Declaration&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetParsedLines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;///&lt;/span&gt;
&lt;span class="c"&gt;// Uses&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetInputLines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GetParsedLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetInputInts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GetParsedLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ParseInt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ci dessus on peut observer une utilisation des generics, avec la définition d'une fonction &lt;code&gt;GetParsedLines()&lt;/code&gt; paramétrable, et son utilisation avec deux types différents dans &lt;code&gt;GetInputLines()&lt;/code&gt; et &lt;code&gt;GetInputInts()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GetParsedLines()&lt;/code&gt; prend en argument une fonction &lt;code&gt;parse()&lt;/code&gt;. Cette fonction &lt;code&gt;parse()&lt;/code&gt; doit prendre en argument une &lt;code&gt;string&lt;/code&gt; et doit renvoyer une valeur de type &lt;code&gt;T&lt;/code&gt;, qui peut être n'importe quel type. La fonction &lt;code&gt;GetParsedLines&lt;/code&gt; déclare qu'elle renvoit une liste de &lt;code&gt;T&lt;/code&gt;, qui doit être le même type renvoyé par &lt;code&gt;parse()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;J'ai trouvé ça facile à découvrir, j'ai quasiment écrit cette fonction à l'instinct en m'aidant des erreurs du compilateur.&lt;/p&gt;

&lt;p&gt;On peut imposer des contraintes sur les types paramétriques, comme &lt;code&gt;comparable&lt;/code&gt; ou &lt;code&gt;Ordered&lt;/code&gt;, mais je n'en ai pas trouvé beaucoup, et je ne sais pas dans quelle mesure c'est extensible.&lt;/p&gt;

&lt;h4&gt;
  
  
  Go en ligne de commande
&lt;/h4&gt;

&lt;p&gt;C'est un langage typé statiquement qu'on peut lancer très facilement en ligne de commande 🎉, une super feature pour moi :) &lt;code&gt;go run monscript.go&lt;/code&gt; et hop. Ça compense le manque de REPL (il n'y a pas de REPL officiel 😔).&lt;/p&gt;

&lt;h4&gt;
  
  
  Un langage raisonnablement petit
&lt;/h4&gt;

&lt;p&gt;Le langage est petit, on peut lire une grosse partie de la doc sans se faire trop mal, j'apprécie beaucoup les petits langages (Scheme, Lua, Smalltalk ❤️). La contrepartie c'est qu'il peut manquer certaines choses dont on a l'habitude.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pas de &lt;code&gt;try&lt;/code&gt; / &lt;code&gt;catch&lt;/code&gt;, on récupère les erreurs en sortie de fonctions un peu comme en C&lt;/li&gt;
&lt;li&gt;Les collections sont limitées (pas de set ni de tuple par exemple, un peu comme en Lua - mais : on a quand même droit à une heap et une liste doublement chaînée en plus des maps et tableaux dynamiques)&lt;/li&gt;
&lt;li&gt;Pas de &lt;code&gt;map&lt;/code&gt; / &lt;code&gt;reduce&lt;/code&gt; / &lt;code&gt;filter&lt;/code&gt; génériques out of the box. Il y a bien quelques personnes qui tentent de fournir des genres de libs équivalentes à LoDash mais si j'ai bien compris les devs Go préfèrent tout faire à coup de boucles &lt;code&gt;for&lt;/code&gt; (voir le premier exemple ci-dessus), et pourquoi pas, on assume la simplicité des briques de base et un côté impératif.&lt;/li&gt;
&lt;li&gt;Pas d’héritage (mais du polymorphisme avec les interfaces)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encore une fois, l’avantage de ces omissions c’est que le langage reste petit, et avec un peu de créativité on s’en sort. Tant que j'ai des tableaux dynamiques, des hashmap et des fonctions du premier ordre je peux travailler en général.&lt;/p&gt;

&lt;h4&gt;
  
  
  Autres Features
&lt;/h4&gt;

&lt;p&gt;Le système d'interface est super souple, on dirait du duck typing en langage statique. Je n'ai pas vraiment pu me faire une opinion là dessus, ça a l'air pas mal pour développer de manière opportuniste tout en gardant les avantages du type checker.&lt;/p&gt;

&lt;p&gt;Le système d'erreur est bien pensé, ça compense un peu le manque d'exception.&lt;/p&gt;

&lt;p&gt;Je n'ai pas utilisé les killer features de concurrence (les goroutines), ça a l'air bien 🤷&lt;/p&gt;

&lt;p&gt;Le système de packages / modules m'a un peu mis des bâtons dans les roues, ça s'est mieux passé quand j'ai compris qu'il y avait un seul package par répertoire, et que les fichiers du package se partagent le même namespace pour la compilation. Si j'ai bien compris ça fait qu'on a un namespace par répertoire, que se partagent tous les fichiers du répertoire. Je ne suis pas fan de çe point, j'aime bien quand le scope de chaque fichier est isolé mais je peux vivre avec.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docs et outils
&lt;/h3&gt;

&lt;p&gt;Les docs sont bien, mais je suis le genre de personne qui trouve beaucoup de docs bien, je vous laisse vous faire votre opinion. C'est facile de trouver les docs de référence et plein d'article pour préciser des points du langage sur le blog Go (il y a un blog Go).&lt;/p&gt;

&lt;p&gt;La lib standard est pas mal, pas aussi fournie que celle de Python mais on y trouve quand même de quoi décoder des png par exemple.&lt;/p&gt;

&lt;p&gt;Le langage est livré avec pas mal d'outils, dont un formateur de code, et il y a même un language server officiel (gopls) ! Top, je peux faire des renames à tire larigot dans Vim 🎉 Globalement ça présage d'un langage bien outillé pour travailler. Dommage : le formateur &lt;code&gt;go fmt&lt;/code&gt; impose des tabs, dans mon éditeur ça passe quand je les affiche comme 4 espaces, mais github les affiche comme huit espaces, je trouve ça moche 🤷 Heureusement il y a un réglage perso pour la largeur des tabs dans github.&lt;/p&gt;

&lt;p&gt;C'est facile d'installer et de gérer des dépendances avec &lt;code&gt;go get&lt;/code&gt;, beaucoup plus simple qu'en Java par exemple pour comparer avec un autre langage curly brackets typé statiquement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Dans mon usage j'ai trouvé que ça ressemble à un Lua typé statiquement ou un meilleur TypeScript (des types non optionels, plus exigent, pas de point virgules, on peut le lancer facilement en ligne de commande sans installer et configurer des libs de transpilation, ça juste marche). Je comprends mieux l'intérêt du coup, en plus c'est moins compliqué que Rust et un peu dans le même esprit (ça compile en WebAssembly par exemple).&lt;/p&gt;

&lt;p&gt;J'en ressors avec l'impression qu'avec un peu plus d'expérience je pourrais très bien faire un projet côté serveur en Go, en plus de m'en servir comme un langage de script typé statiquement, je suis très content d'y avoir passé un peu de temps 👍🏼&lt;/p&gt;

&lt;h3&gt;
  
  
  Références
&lt;/h3&gt;

&lt;p&gt;Pour aller plus loin, plein de docs :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation ~ &lt;a href="https://go.dev/doc/"&gt;https://go.dev/doc/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Tour of Go ~ &lt;a href="https://go.dev/tour/list"&gt;https://go.dev/tour/list&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using Go Modules ~ &lt;a href="https://go.dev/blog/using-go-modules"&gt;https://go.dev/blog/using-go-modules&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to Write Go Code ~ &lt;a href="https://go.dev/doc/code"&gt;https://go.dev/doc/code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Go Programming Language Specification ~ &lt;a href="https://go.dev/ref/spec"&gt;https://go.dev/ref/spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go Modules Reference ~ &lt;a href="https://go.dev/ref/mod"&gt;https://go.dev/ref/mod&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Standard Library ~ &lt;a href="https://pkg.go.dev/std"&gt;https://pkg.go.dev/std&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions (FAQ) ~ &lt;a href="https://go.dev/doc/faq"&gt;https://go.dev/doc/faq&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Error Handling and Go ~ &lt;a href="https://go.dev/blog/error-handling-and-go"&gt;https://go.dev/blog/error-handling-and-go&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outils :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gopls ~ &lt;a href="https://pkg.go.dev/golang.org/x/tools/gopls"&gt;https://pkg.go.dev/golang.org/x/tools/gopls&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Random
&lt;/h3&gt;

&lt;p&gt;C’est quoi les langages curly brackets ? C'est les langages avec beaucoup de &lt;code&gt;{&lt;/code&gt; et de &lt;code&gt;}&lt;/code&gt; dans leur syntaxe, pour délimiter les scopes de fonctions, boucles for, if et autres switch, les classes ou les structures...&lt;br&gt;
Exemples de langages avec beaucoup de curly brackets : C, C++, Java, C#, JavaScript, TypeScript, Go, Rust.&lt;br&gt;
Exemples de langages avec peu de curly brackets : Python, Ruby, Lisp, Lua, Smalltalk, Haskell, Elixir.&lt;/p&gt;

&lt;p&gt;Je cherchais un langage typé statiquement que je pourrais lancer facilement en ligne de commande et pour lequel installer des dépendances serait simple. Un genre de croisement des avantages combinés de Java et de JavaScript. C'est un peu ce que j'ai trouvé en Go, je trouve ça chouette.&lt;/p&gt;

&lt;p&gt;J'aime pas forcément les curly brackets, je suis content d'en avoir trouvé un qui a ces avantages combinés. Il redore un peu le blason de la famille curly brackets.&lt;br&gt;
J'apprécie beaucoup Java pour sa robustesse et ses réfactos autos dans IntelliJ, sans doute C# me plairait aussi, mais pour lancer un script Java en ligne de commande, ou installer une lib en faisant du XML dans un .pom, je trouve ça un peu triste.&lt;br&gt;
J'apprécie JavaScript pour se lancer superfacilement en ligne de commande, et tout installer avec npm install, et aussi pour sa souplesse, mais dans une grosse base de code quand je vois une variable "user" au fin fond d'un appel de fonctions et que je suis incapable de dire quels champs il a et il n'a pas, je trouve ça un peu triste.&lt;br&gt;
Go a l'air de réconcilier un peu tout ça, sans doute qu'il apporte aussi son lot de problèmes.&lt;/p&gt;

&lt;p&gt;Dans la famille non curly brackets, j'avais déjà trouvé mon bonheur, avec Python et Ruby qui font quand même un poil plus de vérifications que JavaScript (par exemple le nombre d'arguments) et qui arrivent avec une lib standard bien fournie, et Haskell qui fonctionne très bien en ligne de commande. en plus de son système de types costaud.&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Comprendre un modèle métier est plus facile dans un langage à typage fort</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Sat, 14 Nov 2020 19:44:42 +0000</pubDate>
      <link>https://dev.to/sroccaserra/comprendre-un-modele-metier-est-plus-facile-dans-un-langage-a-typage-fort-k51</link>
      <guid>https://dev.to/sroccaserra/comprendre-un-modele-metier-est-plus-facile-dans-un-langage-a-typage-fort-k51</guid>
      <description>&lt;p&gt;Pour moi, comprendre un modèle métier précisément est plus difficile dans des langages typés dynamiquement (comme JavaScript, Python, Ruby) que dans des langages à typages forts (comme Java, C#, Haskell). &lt;/p&gt;

&lt;p&gt;Pour illustrer les difficultés que je peux avoir, voici un exemple qui utilise une modélisation un peu riche, tirée du livre &lt;a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional/"&gt;Domain Modeling Made Functional&lt;/a&gt; (chapitre 4 : &lt;em&gt;Understanding Types)&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dans un langage typé dynamiquement
&lt;/h2&gt;

&lt;p&gt;Dans un langage typé dynamiquement, quand on observe une fonction, il est souvent très difficile de savoir ce que peuvent et ne peuvent pas contenir ses arguments. Par exemple quand on voit une fonction comme :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isAuthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou même :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isAuthorized&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comment savoir avec rigueur ce que contiendront exactement les arguments de cette fonction quand elle sera appelée au runtime ? Est-ce que parfois le paramètre &lt;code&gt;method&lt;/code&gt; sera une simple string, un objet structuré, avec la bonne structure, autre ? Qu'est-ce qu'on va récupérer comme devise ?&lt;/p&gt;

&lt;p&gt;On peut se contenter de lire l'implémentation actuelle : ce qui est utilisé dans l'implémentation est un sous ensemble probablement suffisant si le code fonctionne. Mais quand on change ce code, sur quels autres champs peut-on s'appuyer ? Seront-ils présents dans tous les cas d'usage ?&lt;/p&gt;

&lt;p&gt;Peut-être qu'il y a une classe &lt;code&gt;Payment&lt;/code&gt; déclarée quelque-part, mais même si je la trouve, rien n'empêche le code d'ajouter ou de supprimer des champs avant l'appel de cette fonction, ou même d'utiliser un simple &lt;code&gt;Object&lt;/code&gt; qui a à peu près la bonne forme à la place, comme dans la deuxième version ci-dessus.&lt;/p&gt;

&lt;p&gt;En pratique on se retrouve souvent à utiliser grep sur tout le code pour explorer les appels de cette fonction, et à déboguer le code dans plusieurs cas d'usage si on veut avoir la réponse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dans un langage avec typage fort
&lt;/h2&gt;

&lt;p&gt;Pour montrer le niveau d'information apporté par une modélisation métier avec un typage fort, voici la même fonction déclarée en Haskell :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;isAuthorized&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="n"&gt;isAuthorized&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c1"&gt;-- ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La grosse différence est que dans ce cas je peux aller observer la déclaration du type &lt;code&gt;Payment&lt;/code&gt;, je n'ai pas à lancer grep sur tout le code ou à poser des points d'arrêt et autres &lt;code&gt;console.log&lt;/code&gt; un peu partout. La vérification de type me garantit que cette fonction sera toujours appelée avec une valeur de type &lt;code&gt;Payment&lt;/code&gt;, et que cette valeur aura toujours la bonne forme.&lt;/p&gt;

&lt;p&gt;Voici par exemple ce qu'on pourrait trouver comme déclaration riche du type &lt;code&gt;Payment&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;CardNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CardNumber&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;CheckNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CheckNumber&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;CardType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Visa&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Mastercard&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;CreditCardInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CreditCardInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cardType&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;CardType&lt;/span&gt;
                                     &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cardNumber&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;CardNumber&lt;/span&gt;
                                     &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;PaymentMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Cash&lt;/span&gt;
                   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Check&lt;/span&gt; &lt;span class="kt"&gt;CheckNumber&lt;/span&gt;
                   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Card&lt;/span&gt; &lt;span class="kt"&gt;CreditCardInfo&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;PaymentAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PaymentAmount&lt;/span&gt; &lt;span class="kt"&gt;Decimal&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;EUR&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;USD&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Payment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;PaymentAmount&lt;/span&gt;
                       &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Currency&lt;/span&gt;
                       &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;PaymentMethod&lt;/span&gt;
                       &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On peut observer que ce type est à la fois riche et définit très précisément. On est aussi certain en un coup d’œil de ce qu'il contient exactement au runtime. On sait qu'on va avoir :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un montant qui est un nombre décimal&lt;/li&gt;
&lt;li&gt;une devise soit en euros soit en dollars&lt;/li&gt;
&lt;li&gt;une méthode de paiement qui est à son tour précisément définie : c'est soit du cash, soit un chèque avec un numéro, soit une carte bancaire qui à son tour définit un type et un numéro de carte.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On peut aussi voir avec certitude tous les cas qu'on devra traiter lorsqu'on utilise une valeur de ce type. Paiement cash, par chèque, par carte, paiements en euro et en dollars.&lt;/p&gt;

&lt;p&gt;De même quand on remanie l'implémentation d'une fonction utilisant ce type, on connait précisément les éléments dont on dispose pour la nouvelle implémentation.&lt;/p&gt;

&lt;p&gt;Il est rare d'avoir autant de connaissances et ce niveau de certitude dans un langage typé dynamiquement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Les difficultés des langages à typage dynamique illustrées ici n'apparaissent pas toujours. Sur des projets de petite taille, avec deux ou trois personnes bien alignées qui réinjectent régulièrement leurs compréhension du métier dans le code (ce qui est une discipline très difficile à avoir), grâce par exemple à TDD et au refactoring systématique, on peut trouver du code facile à suivre.&lt;/p&gt;

&lt;p&gt;Et c'est vrai que malgré mes difficultés, j'apprécie beaucoup des langages comme Ruby, Smalltalk, Lisp, Python, et même Lua, Awk et JavaScript (qui décrochent le pompon en terme de permissivité ?). C'est dans ces langages que j'ai travaillé quasiment exclusivement depuis 2015.&lt;/p&gt;

&lt;p&gt;Cependant, pour mes prochains projets je souhaite retrouver un typage fort afin de travailler avec plus de confort la modélisation du métier dans le code. Et en bonus, afin d'avoir le plus d'expressivité possible, je souhaite pouvoir profiter :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;des &lt;a href="https://en.wikipedia.org/wiki/Tagged_union"&gt;types sommes&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;du &lt;a href="https://en.wikipedia.org/wiki/Pattern_matching"&gt;filtrage par motif&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;de l'&lt;a href="https://en.wikipedia.org/wiki/Type_inference"&gt;inférence de type&lt;/a&gt; (disponible depuis &lt;a href="https://en.wikipedia.org/wiki/ML_(programming_language)"&gt;ML&lt;/a&gt;, soit bientôt 50 ans ?).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je crois que c'est le cas pour Kotlin, Rust, Haskell, F# / OCaml, Elm, PureScript, Reason, ReScript ce qui laisse pas mal de choix.&lt;/p&gt;

&lt;p&gt;Ce qui est certain, c'est que si je participe au démarrage d'un projet en JavaScript ou Node.js, je vais militer pour partir sur TypeScript dès le départ.&lt;/p&gt;




&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@spacexuan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Crystal Kwok&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/pipes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;.&lt;/p&gt;

</description>
      <category>design</category>
      <category>functional</category>
      <category>types</category>
      <category>programming</category>
    </item>
    <item>
      <title>Quelques références sur le couplage dans le code</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Sat, 31 Oct 2020 23:27:44 +0000</pubDate>
      <link>https://dev.to/sroccaserra/quelques-references-sur-le-couplage-dans-le-code-9g5</link>
      <guid>https://dev.to/sroccaserra/quelques-references-sur-le-couplage-dans-le-code-9g5</guid>
      <description>&lt;p&gt;Récemment j'ai fait un petit tour des tables des matières d'ouvrages classiques pour trouver des références sur le couplage dans le code. Voici ce que j'ai récolté, sans ordre de préférence.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pragmatic Programmer
&lt;/h3&gt;

&lt;p&gt;Tout d'abord, &lt;a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/"&gt;The Pragmatic Programmer&lt;/a&gt; est une super référence sur le sujet.&lt;/p&gt;

&lt;p&gt;Notamment dans la 2e édition, le topic 28, Decoupling, pp. 130-137.&lt;/p&gt;

&lt;p&gt;Pour vous donner envie de le lire (et ça me donne super envie de le relire !!), ça parle de :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structural Rigidity,&lt;/li&gt;
&lt;li&gt;Tip 44 : Decoupled code is easier to change,&lt;/li&gt;
&lt;li&gt;Train wrecks,&lt;/li&gt;
&lt;li&gt;Tip 45 : Tell, don’t ask,&lt;/li&gt;
&lt;li&gt;The law of Demeter,&lt;/li&gt;
&lt;li&gt;Tip 46 : Don’t chain method calls&lt;/li&gt;
&lt;li&gt;The evils of globalization&lt;/li&gt;
&lt;li&gt;Tip 47 : Avoid global data&lt;/li&gt;
&lt;li&gt;Tip 48 : If it’s important enough to be global, wrap it in an API&lt;/li&gt;
&lt;li&gt;Inheritance adds coupling&lt;/li&gt;
&lt;li&gt;Again, it’s all about change&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Refactoring
&lt;/h3&gt;

&lt;p&gt;Dans &lt;a href="https://martinfowler.com/books/refactoring.html"&gt;Refactoring&lt;/a&gt;, il y a les code smells opposés “Divergent Change” et “Shotgun Surgery” (p. 76 de la 2ème édition), et "Message Chain" (p. 81) avec les refactorings associés.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Complete
&lt;/h3&gt;

&lt;p&gt;Dans &lt;a href="https://www.informit.com/store/code-complete-9780735619678"&gt;Code Complete&lt;/a&gt; :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Loose coupling: Loose coupling means designing so that you hold connections among different parts of a program to a minimum. Use the principles of good abstractions in class interfaces, encapsulation, and information hiding to design classes with as few interconnections as possible. Minimal connectedness minimizes work during integration, testing, and maintenance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et le chapitre Design Building Blocks: Heuristics, dont l’heuristique Keep Coupling Loose, p. 100, qui parle de :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coupling Criteria (size, visibility, flexibility)&lt;/li&gt;
&lt;li&gt;Kinds of Coupling (simple data parameter coupling, simple object coupling, object parameter coupling, semantic coupling)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et aussi l’heuristique “Aim for strong cohesion”, p. 105.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.informit.com/store/clean-architecture-a-craftsmans-guide-to-software-structure-9780134494166"&gt;Clean Architecture&lt;/a&gt; a un chapitre Component Cohesion, un chapitre Component Coupling et un chapitre Independance, qui parle de Decoupling Layers, Decoupling Use Cases et Decoupling Mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working Effectively with Legacy Code
&lt;/h3&gt;

&lt;p&gt;Dans &lt;a href="https://www.informit.com/store/working-effectively-with-legacy-code-9780131177055"&gt;Working Effectively with Legacy Code&lt;/a&gt; il y a le chapitre 25, Dependency-Breaking Techniques, qui donne des techniques pour découpler des classes suffisamment pour pouvoir les tester.&lt;/p&gt;

&lt;h3&gt;
  
  
  Articles
&lt;/h3&gt;

&lt;p&gt;Il y a aussi l’article sur le pattern Big Ball of Mud que j’aime bien :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.laputan.org/mud/"&gt;http://www.laputan.org/mud/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette conférence récente d’&lt;a href="https://twitter.com/Lilobase"&gt;Arnaud Lemaire&lt;/a&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.lilobase.me/votre-application-a-besoin-de-son-jardin-secret-attention-a-la-localite-de-linformation/"&gt;https://www.lilobase.me/votre-application-a-besoin-de-son-jardin-secret-attention-a-la-localite-de-linformation/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et coïncidence, &lt;a href="https://twitter.com/KentBeck/status/1322545224220991488"&gt;ce tweet&lt;/a&gt; d'aujourd'hui de Kent Beck :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Coupling vs cohesion. &lt;/p&gt;

&lt;p&gt;Coupling:&lt;br&gt;
  • Can be hard to find&lt;br&gt;
  • Can be really hard &amp;amp;/| expensive to eliminate. &lt;/p&gt;

&lt;p&gt;Cohesive sub-elements are:&lt;br&gt;
  • Usually easy to find&lt;br&gt;
  • Cheap to achieve, &amp;amp;&lt;br&gt;
  • Help you find de-coupling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;C’était chouette de faire ce petit tour de tables des matières dans ces ouvrages, maintenant il me faut du temps pour les lire ou relire, et mettre en pratique 😯 Bonne lecture, bonne pratique de vos apprentissages !&lt;/p&gt;

&lt;p&gt;Crédit photo : &lt;a href="https://pixabay.com/fr/photos/nerfs-cellules-s%C3%A9pia-dendrites-3115722/"&gt;https://pixabay.com/fr/photos/nerfs-cellules-sépia-dendrites-3115722/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cleancode</category>
      <category>design</category>
      <category>books</category>
      <category>programming</category>
    </item>
    <item>
      <title>Découverte de OCaml</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Fri, 30 Oct 2020 21:47:40 +0000</pubDate>
      <link>https://dev.to/sroccaserra/decouverte-de-ocaml-1ib7</link>
      <guid>https://dev.to/sroccaserra/decouverte-de-ocaml-1ib7</guid>
      <description>&lt;p&gt;Cette semaine j'avais envie d'explorer un nouveau langage. Je souhaitais avoir un typage fort et des fonctions &lt;a href="https://fr.wikipedia.org/wiki/Curryfication"&gt;currifiées&lt;/a&gt; par défaut : j'ai essayé OCaml, et j'ai été agréablement surpris.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kata Bowling en OCaml
&lt;/h3&gt;

&lt;p&gt;Pour découvrir le langage, je me suis lancé sur une implémentation du &lt;a href="https://codingdojo.org/kata/Bowling/"&gt;kata bowling&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Frame&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Frame&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Frame&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Frame&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_last_frame&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_strike&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;r3&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_spare&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;failwith&lt;/span&gt; &lt;span class="s2"&gt;"wrong number of rolls"&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_strike&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_spare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;is_last_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="nc"&gt;Frame&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le répo github est ici, la liste des commits montre le cheminement :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sroccaserra/bowling-game-kata-ocaml"&gt;https://github.com/sroccaserra/bowling-game-kata-ocaml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ce que j'ai apprécié
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quand on connait un peu Haskell, on se sent vite à la maison&lt;/li&gt;
&lt;li&gt;La &lt;a href="https://ocaml.org/learn/index.fr.html"&gt;documentation en français&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Il y a des &lt;a href="https://ocaml.org/learn/tutorials/guidelines.html"&gt;guidelines&lt;/a&gt; et des &lt;a href="https://ocaml.org/docs/cheat_sheets.html"&gt;cheatsheets&lt;/a&gt; pour démarrer&lt;/li&gt;
&lt;li&gt;Le &lt;a href="https://ocaml.org/learn/tutorials/functional_programming.fr.html"&gt;paradigme fonctionnel&lt;/a&gt; et le pattern matching&lt;/li&gt;
&lt;li&gt;En OCaml, il n'y a que des &lt;a href="https://fsharpforfunandprofit.com/posts/expressions-vs-statements/"&gt;expressions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ne pas avoir à répéter le nom de la fonction pour chaque pattern&lt;/li&gt;
&lt;li&gt;Le pattern matching implicite avec &lt;code&gt;function&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Les paramètres nommés et avec valeurs par défaut (voir ci-dessus, &lt;code&gt;?(f=Frame 1)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Je n'ai pas essayé, mais je suis curieux de l'aspect orienté objet&lt;/li&gt;
&lt;li&gt;L'inférence de type et le typage fort, c'est chouette d'être aidé par un compilateur&lt;/li&gt;
&lt;li&gt;Avoir une erreur de compilation par défaut pour les patterns non exhaustifs et les variables non utilisées&lt;/li&gt;
&lt;li&gt;Le système de build (&lt;a href="https://github.com/ocaml/dune"&gt;dune&lt;/a&gt;), je n'ai pas eu de mal à le prendre en main (pour ce tout petit projet)&lt;/li&gt;
&lt;li&gt;C'est facile de lancer les tests en mode watch (&lt;code&gt;dune runtest -w&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Installer des libs avec opam, ça fonctionne&lt;/li&gt;
&lt;li&gt;C'est facile de visualiser les erreurs de compilation à la volée dans Vim&lt;/li&gt;
&lt;li&gt;Les sources de gros projets sont dispos pour s'inspirer (&lt;a href="https://github.com/janestreet"&gt;JaneStreet&lt;/a&gt; et &lt;a href="https://github.com/mirage"&gt;Mirage&lt;/a&gt; par exemple)&lt;/li&gt;
&lt;li&gt;OCaml a des variantes plus ou moins populaires, comme F# (une base de OCaml sous .NET), et Reason /  ReScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ce que j'ai moins apprécié
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;del&gt;On ne peut pas suivre la step down rule (définir la fonction appelante au dessus de la fonction appelée) si j'ai bien compris&lt;/del&gt; &amp;lt;-- en fait si, avec &lt;code&gt;and&lt;/code&gt;, voir la fonction &lt;code&gt;is_strike&lt;/code&gt; ci-dessus&lt;/li&gt;
&lt;li&gt;Devoir répéter le pattern pour chaque guarde &lt;code&gt;when&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Des symbols un peu plus présents qu'en Haskell (&lt;code&gt;::&lt;/code&gt; pour &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;@@&lt;/code&gt; pour &lt;code&gt;$&lt;/code&gt;, les &lt;code&gt;~&lt;/code&gt; devant les paramètres nommés)&lt;/li&gt;
&lt;li&gt;Le &lt;code&gt;;;&lt;/code&gt; pour finir une instruction dans le repl&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Kata Bowling en Haskell
&lt;/h3&gt;

&lt;p&gt;Pour explorer les ressemblances, j'ai fait le même kata en Haskell :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Bowling&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Roll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score'&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;score'&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
  &lt;span class="kt"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;isLastFrame&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;isStrike&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;isSpare&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="n"&gt;r2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;score'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s"&gt;"Wrong number of rolls"&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;isStrike&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;isSpare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Roll&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="n"&gt;isLastFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Frame&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le répo github est ici, la liste des commits montre le cheminement :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sroccaserra/bowling-game-kata-haskell"&gt;https://github.com/sroccaserra/bowling-game-kata-haskell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Je suis content d'avoir découvert OCaml, je pense en refaire plus tard, par exemple pour explorer l'orientation objet. Ou peut-être découvrir F# pour explorer encore un autre langage de ce type ?&lt;/p&gt;

&lt;p&gt;Note : je ne suis un expert ni en OCaml, ni en Haskell. Je suis preneur d'avis pour améliorer tout ça.&lt;/p&gt;

</description>
      <category>ocaml</category>
      <category>tdd</category>
      <category>functional</category>
      <category>kata</category>
    </item>
    <item>
      <title>Pair / Mob programming à distance avec tmux et ssh</title>
      <dc:creator>Sébastien Roccaserra</dc:creator>
      <pubDate>Mon, 12 Oct 2020 11:01:47 +0000</pubDate>
      <link>https://dev.to/sroccaserra/pair-mob-programming-avec-tmux-et-ssh-4ccm</link>
      <guid>https://dev.to/sroccaserra/pair-mob-programming-avec-tmux-et-ssh-4ccm</guid>
      <description>&lt;p&gt;Quand on veut faire du pair programming à distance en n’utilisant que le terminal, on peut utiliser tmux et ssh (les invités n’ont besoin que d’un client ssh), avec un google meet pour la voix uniquement.&lt;/p&gt;

&lt;p&gt;Fonctionnalités : dans tmux, on peut ouvrir plusieurs fenêtres (un vim et un shell par exemple) qui sont toutes partagées. Tous les participants peuvent soit observer, soit prendre la main (le clavier a un léger lag pour les personnes à distance, à voir sur la durée si c’est ok -- voir Mosh dans les références si c'est un problème).&lt;/p&gt;

&lt;p&gt;Avantage : ce setup est très léger et facile à utiliser quand on n’a pas besoin de montrer son navigateur aux autres. Fini les écrans flous et les ordinateurs qui rament à cause du partage d'écran.&lt;/p&gt;

&lt;p&gt;Avertissement : si vous ne connaissez pas bien ssh, tmux, votre shell et un bon éditeur en ligne de commande comme Vim ou Emacs, il vaut mieux se familiariser avec ces outils avant de proposer des sessions à distance à vos collègues.&lt;/p&gt;

&lt;p&gt;En passant, je pense qu'apprendre ces outils est un très bon investissement. Ce sont des outils puissant, répandus et stables dont vous aurez l'usage pendant des dizaines d'années. (Voir les références en fin d'article pour des recommandations sur comment apprendre Vim et tmux.)&lt;/p&gt;

&lt;p&gt;Note : il y a un easy mode, tmate semble très bien et plus simple : &lt;a href="https://tmate.io/"&gt;https://tmate.io/&lt;/a&gt;. Si vous êtes déjà à l’aise avec tmux et la configuration de ssh, le mode d’emploi ci-dessous ne devrait pas être tellement plus compliqué.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prérequis
&lt;/h3&gt;

&lt;p&gt;Les invités n’ont besoin que d’un client ssh.&lt;/p&gt;

&lt;p&gt;Pour l’hôte de la session, avoir un ngrok ou équivalent, et un tmux opérationnel (brew install tmux ?) et apprendre un peu à l’utiliser (il y a un crash course en bas).&lt;/p&gt;

&lt;h3&gt;
  
  
  Ajout des clés publiques des invités
&lt;/h3&gt;

&lt;p&gt;La première fois uniquement, l'hôte ajoute les clés publiques des invités dans ~/.ssh/authorized_keys, sur ce modèle :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;command="/usr/local/bin/tmux attach -t shared" ssh-rsa AABBCC...123== michel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note : la commande peut varier en fonction de votre installation de tmux et de si votre shell peut le trouver sans être un login shell.&lt;/p&gt;

&lt;p&gt;Note : j'ai eu besoin plusieurs fois de vérifier les clés publiques des invités, voir dans la section Caveats pour un exemple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lancer la session
&lt;/h3&gt;

&lt;p&gt;L'hôte lance ngrok (qui va indiquer un numéro de port et une url en réponse) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ngrok tcp 22
...
Forwarding                    tcp://0.tcp.ngrok.io:12345 -&amp;gt; localhost:22
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L'hôte lance une session tmux nommée "shared" (ça doit être le même nom que dans la commande ssh spécifiée pour les invités dans le fichier authorized_keys) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tmux new -s shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Partager la commande pour se connecter
&lt;/h3&gt;

&lt;p&gt;Les invités peuvent maintenant se connecter à votre session tmux par ssh en lançant cette commande que vous pouvez leur partager. Dans la ligne ci-dessous, remplacer le nom d’utilisateur par votre nom d’utilisateur Linux, et remplacer le port et le hostname par ce que donne ngrok (ne pas mettre "tcp://" devant le hostname) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh -p 12345 user@0.tcp.ngrok.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Utiliser tmux pendant la session
&lt;/h3&gt;

&lt;p&gt;Sur macOS, utiliser Cmd + et Cmd - (iTerm2) pour avoir une taille de fonte qui donne une taille d'écran confortable pour tout le monde. Pro tip : ce sont les invités qui adaptent leur taille de police pour correspondre à celle de l'hôte, afin d'avoir une référence commune.&lt;/p&gt;

&lt;p&gt;tmux crash course (utiliser le préfix avant la touche, Ctrl-b par défaut) :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;? pour lister les raccourcis&lt;/li&gt;
&lt;li&gt;% pour ouvrir un pane à droite&lt;/li&gt;
&lt;li&gt;" pour ouvrir un pane en dessous&lt;/li&gt;
&lt;li&gt;z pour zoomer et revenir&lt;/li&gt;
&lt;li&gt;x pour fermer un pane&lt;/li&gt;
&lt;li&gt;[ pour passer en scroll mode (q pour quitter)&lt;/li&gt;
&lt;li&gt;q pour numéroter les panes&lt;/li&gt;
&lt;li&gt;M-Right agrandir de 5 vers la droite (fonctionne avec Up Down et Left)&lt;/li&gt;
&lt;li&gt;:resize-pane -R 20 # pour étendre le pane de 20 caractère à droite (marche avec U D L R)&lt;/li&gt;
&lt;li&gt;{ et } pour échanger les panes&lt;/li&gt;
&lt;li&gt;c pour créer une fenêtre&lt;/li&gt;
&lt;li&gt;&amp;amp; pour fermer une fenêtre&lt;/li&gt;
&lt;li&gt;w pour lister les fenêtre&lt;/li&gt;
&lt;li&gt;option + double click pour sélectionner par mots&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;Les utilisateurs voient leur police de caractères et leurs couleurs de terminal.&lt;/p&gt;

&lt;p&gt;Quand un observateur clique sur sa fenêtre, ça peut modifier le pane qui a le focus pour l'hôte.&lt;/p&gt;

&lt;p&gt;Sur macOS on a observé que cat ~/.ssh/id_rsa.pub | pbcopy peut manger la fin de la clé publique, penser à la vérifier. Pour vérifier une clé publique, on peut faire dans bash :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;ssh-rsa AAAAM...F6VRP5&lt;span class="o"&gt;==&lt;/span&gt; bob@bob.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;dans fish :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;ssh-rsa AAAAM...F6VRP5&lt;span class="o"&gt;==&lt;/span&gt; bob@bob.com | psub&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Voir aussi
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pour apprendre Vim :

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vim.fandom.com/wiki/Tutorial"&gt;https://vim.fandom.com/wiki/Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pragprog.com/titles/dnvim2/practical-vim-second-edition/"&gt;Practical Vim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Pour apprendre tmux : &lt;a href="https://pragprog.com/titles/bhtmux2/tmux-2/"&gt;tmux 2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.hamvocke.com/blog/remote-pair-programming-with-tmux/"&gt;&lt;/a&gt;&lt;a href="https://www.hamvocke.com/blog/remote-pair-programming-with-tmux/"&gt;https://www.hamvocke.com/blog/remote-pair-programming-with-tmux/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mosh.org/"&gt;&lt;/a&gt;&lt;a href="https://mosh.org/"&gt;https://mosh.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tmate.io/"&gt;&lt;/a&gt;&lt;a href="https://tmate.io/"&gt;https://tmate.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>pairprogramming</category>
      <category>remote</category>
      <category>tmux</category>
      <category>shell</category>
    </item>
  </channel>
</rss>
