<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Alexandre Petit</title><link href="https://me.petitapetit.io/" rel="alternate"/><link href="https://me.petitapetit.io/feeds/all.atom.xml" rel="self"/><id>https://me.petitapetit.io/</id><updated>2026-03-22T00:00:00+01:00</updated><entry><title>Simplicité &gt; Facilité — et pourquoi c'est encore plus vrai avec l'IA</title><link href="https://me.petitapetit.io/simple" rel="alternate"/><published>2026-03-22T00:00:00+01:00</published><updated>2026-03-22T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2026-03-22:/simple</id><summary type="html">&lt;p&gt;Il y a un talk de 2011 que je regarde une fois par an. Rich Hickey, le créateur de Clojure, sur scène à Strange Loop. Le titre : &lt;em&gt;Simple Made Easy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Sa thèse tient en une phrase : simple et facile, ce n'est pas la même chose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple&lt;/strong&gt; vient du latin &lt;em&gt;simplex …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;Il y a un talk de 2011 que je regarde une fois par an. Rich Hickey, le créateur de Clojure, sur scène à Strange Loop. Le titre : &lt;em&gt;Simple Made Easy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Sa thèse tient en une phrase : simple et facile, ce n'est pas la même chose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple&lt;/strong&gt; vient du latin &lt;em&gt;simplex&lt;/em&gt; — un seul pli. Quelque chose de simple n'est pas entremêlé. Chaque pièce fait une chose. On peut la comprendre, la retirer, la remplacer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Facile&lt;/strong&gt; vient de l'anglo-normand &lt;em&gt;aisé&lt;/em&gt; — à portée de main. Quelque chose de facile est proche de ce qu'on connaît. Un &lt;code&gt;npm install&lt;/code&gt;, un CRM tout-en-un, un framework qui fait tout. Pas besoin de réfléchir. Ça marche.&lt;/p&gt;
&lt;p&gt;Le piège, c'est qu'on confond les deux.&lt;/p&gt;
&lt;h2&gt;Le château en LEGO et le château tricoté&lt;/h2&gt;
&lt;p&gt;C'est l'image la plus forte de Hickey.
&lt;img alt="" src="_assets/tricot-vs-lego.png"&gt;&lt;/p&gt;
&lt;p&gt;Le château tricoté est une seule pièce. Tout est lié. Tirez un fil, tout bouge. Ajoutez une tour ? Impossible sans tout défaire. C'est complexe — au sens étymologique. &lt;em&gt;Complexus&lt;/em&gt; : tissé ensemble.&lt;/p&gt;
&lt;p&gt;Le château en LEGO est fait de briques indépendantes. Ajoutez une tour, retirez un mur, changez une couleur. Chaque brique a sa propre existence. C'est simple.&lt;/p&gt;
&lt;p&gt;Lequel est plus facile à construire ? Le tricoté, peut-être — une pelote et des aiguilles suffisent. Lequel est plus facile à &lt;em&gt;modifier&lt;/em&gt; ? Le LEGO, sans aucun doute.&lt;/p&gt;
&lt;h2&gt;Ce qu'on fait quand c'est difficile&lt;/h2&gt;
&lt;p&gt;Face à un problème difficile, le réflexe est de chercher une solution facile. Un outil qui fait tout. Un framework qui gère les cas limites à votre place. Une dépendance qui résout le problème en une ligne.&lt;/p&gt;
&lt;p&gt;Le problème : chaque solution facile apporte de la complexité invisible.&lt;/p&gt;
&lt;p&gt;Un ORM comme SQLAlchemy masque le SQL. Vous gagnez du temps au départ. Mais le jour où vous devez optimiser une requête, vous vous battez contre l'abstraction au lieu de parler à la base. L'outil censé vous aider est devenu un obstacle.&lt;/p&gt;
&lt;p&gt;Un CRM tout-en-un comme Twenty vous donne une interface en 5 minutes. Mais il impose son schéma, sa logique, ses conventions. Le jour où vous voulez faire autrement, vous travaillez contre l'outil.&lt;/p&gt;
&lt;p&gt;Cette semaine, j'ai choisi NocoDB + PostgreSQL pour mon CRM. Ça demande plus d'effort à mettre en place. Mais c'est l'outil qui s'adapte à mes données — pas l'inverse.&lt;/p&gt;
&lt;p&gt;Hickey résume ça en une phrase : &lt;em&gt;"Simplifying means making more things, not fewer."&lt;/em&gt; Simplifier, c'est séparer les responsabilités. Préférer 3 briques indépendantes à 1 bloc qui fait tout.&lt;/p&gt;
&lt;h2&gt;L'IA rend tout facile&lt;/h2&gt;
&lt;p&gt;Avec l'IA, créer du code est facile. Un prompt, et vous avez un fichier de 500 lignes. Un agent, et votre projet a une architecture en 10 minutes.&lt;/p&gt;
&lt;p&gt;Mais facile n'est pas simple.&lt;/p&gt;
&lt;p&gt;Une étude de METR (2025), randomisée et contrôlée, a mesuré l'impact de l'IA sur 16 développeurs seniors travaillant sur des dépôts open-source de plus d'un million de lignes. Résultat : &lt;strong&gt;les développeurs prennent 19% plus de temps avec l'IA&lt;/strong&gt;. Tout en croyant aller 24% plus vite.&lt;/p&gt;
&lt;p&gt;Le rapport DORA de Google confirme : chaque tranche de 25% d'adoption IA s'accompagne d'une &lt;strong&gt;baisse de 7.2% de la stabilité des livraisons&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Un article d'Elektor Magazine résume le phénomène : &lt;em&gt;"2025 made it cheaper to create software. It did not make it cheaper to own software."&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Le problème n'est pas l'IA. Le problème, c'est de construire sans penser. Un dev sur dev.to raconte : sur un bug, l'IA proposait un changement de schéma DB + migrations + 15 fichiers. La solution humaine : 10 fichiers, 0 changement DB, 1 heure. &lt;em&gt;"5 minutes of architectural thinking prevented weeks of unnecessary complexity."&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;5 minutes de réflexion architecturale. C'est le prix de la simplicité.&lt;/p&gt;
&lt;p&gt;Comprendre le problème. Identifier les cas limites. Décider ce qui ne doit &lt;em&gt;pas&lt;/em&gt; être construit. Aucun outil ne fait ça à votre place.&lt;/p&gt;
&lt;h2&gt;Ce que ça change en pratique&lt;/h2&gt;
&lt;p&gt;La simplicité est un choix quotidien. Elle demande plus d'effort au départ. Mais elle paie sur le long terme.&lt;/p&gt;
&lt;p&gt;Cette semaine, j'ai migré mon serveur d'OpenBSD vers Debian. Pas de Docker, pas de Kubernetes, pas d'orchestrateur. Nginx, Postfix, Dovecot, PostgreSQL, une API Python. Chaque brique est indépendante. Je comprends chaque ligne de configuration.&lt;/p&gt;
&lt;p&gt;C'est plus difficile que de lancer un &lt;code&gt;docker-compose up&lt;/code&gt;. Mais si un service tombe, je sais exactement où regarder. Si je veux changer de fournisseur mail, je copie un fichier de config. Mes données sont à moi, dans des formats standards, pour les 20 prochaines années.&lt;/p&gt;
&lt;p&gt;Derek Sivers dit : &lt;em&gt;"You need a bicycle, not a jumbo jet."&lt;/em&gt; Apprenez à conduire. Ne prenez pas le taxi. La dépendance a un coût invisible.&lt;/p&gt;
&lt;h2&gt;Le vrai ennemi&lt;/h2&gt;
&lt;p&gt;La complexité ne se présente jamais comme la complexité. Elle se présente comme la facilité.&lt;/p&gt;
&lt;p&gt;Un &lt;code&gt;npm install&lt;/code&gt; de plus. Un service managé qui évite de configurer. Un framework qui pense à votre place. Chaque choix est rationnel. L'accumulation est le problème.&lt;/p&gt;
&lt;p&gt;À l'ère de l'IA, ce mécanisme s'accélère. Il n'a jamais été aussi facile de créer. Il n'a jamais été aussi important de choisir la simplicité.&lt;/p&gt;</content></entry><entry><title>Comment je me suis fais des amis au Vietnam</title><link href="https://me.petitapetit.io/friends" rel="alternate"/><published>2026-02-18T00:00:00+01:00</published><updated>2026-02-18T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2026-02-18:/friends</id><summary type="html">&lt;p&gt;Au Vietnam, j’ai mené une expérience intentionnelle pour développer mes compétences sociales.&lt;/p&gt;
&lt;p&gt;J'ai vécu quatre ans à Caen sans me faire de véritables amis.
Mon temps était consacré à ma partenaire et à nos familles. 
On s'est séparé. 
Je me sens seul, déprimé, et je veux construire des relations …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Au Vietnam, j’ai mené une expérience intentionnelle pour développer mes compétences sociales.&lt;/p&gt;
&lt;p&gt;J'ai vécu quatre ans à Caen sans me faire de véritables amis.
Mon temps était consacré à ma partenaire et à nos familles. 
On s'est séparé. 
Je me sens seul, déprimé, et je veux construire des relations épanouissantes.&lt;/p&gt;
&lt;p&gt;Je pars de deux intuitions : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la qualité de notre vie dépend largement de la qualité de nos relations &lt;/li&gt;
&lt;li&gt;les relations se créent quand on investit sincèrement ses centres d’intérêt dans des communautés physiques&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je pense que les compétences sociales comptent autant que les compétences techniques. 
Et je souhaite être plus à l'aise dans les situations sociales : conférences, entreprise, réunions familiales.&lt;/p&gt;
&lt;h3&gt;Le point de départ&lt;/h3&gt;
&lt;p&gt;J'ai d'abord passé trois semaines en auberge.&lt;/p&gt;
&lt;p&gt;Dans ce cadre, rencontrer de nouvelles personnes était presque trop facile.&lt;/p&gt;
&lt;p&gt;Lorsque j'ai pris un appartement, le jeu s'est corsé.&lt;/p&gt;
&lt;p&gt;J’ai essayé de rencontrer des gens via Tinder.&lt;/p&gt;
&lt;p&gt;J'ai eu dix fois plus de matchs qu'en France.&lt;/p&gt;
&lt;p&gt;Mais l’expérience a été mauvaise :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;conversations peu intéressantes&lt;/li&gt;
&lt;li&gt;stress important lors du seul rendez-vous que j’ai fait&lt;/li&gt;
&lt;li&gt;sentiment d’artificialité dans les interactions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je me suis aussi rendu compte d’un problème plus profond.
Je sous-investissais mes centres d’intérêt.
Et j’essayais de rencontrer des gens en dehors de ce qui me faisait vraiment vibrer.&lt;/p&gt;
&lt;p&gt;J’ai décidé de tester une autre approche : rencontrer des gens en vivant honnêtement mes passions.&lt;/p&gt;
&lt;p&gt;J'ai désinstallé Tinder, pris un abonnement dans un studio de danse, et je me suis jeté à l'eau.&lt;/p&gt;
&lt;h3&gt;Réalisation&lt;/h3&gt;
&lt;p&gt;Mon travail de préparation a consisté à :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;étudier cinq livres sur les relations et la conversation&lt;/li&gt;
&lt;li&gt;suivre deux formations en ligne sur les compétences sociales&lt;/li&gt;
&lt;li&gt;rédiger un &lt;a href="conversation"&gt;conversation-résumé&lt;/a&gt; pour préparer mes réponses aux questions fréquentes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'ai mis en place d’habitudes sociales comme 
aborder une nouvelle personne chaque jour,
participer à des événements sociaux type English Meetups,
proposer d'aller manger ensemble après les cours de danse.&lt;/p&gt;
&lt;p&gt;J'ai rejoins des communautés locales : groupe de méditation, Brazilian Zouk Crew, running club.&lt;/p&gt;
&lt;p&gt;Objectif : dépasser ma peur d'aller vers l'autre et développer des amitiés.&lt;/p&gt;
&lt;p&gt;Après quelques mois, j’avais construit un petit cercle d’amis à Saigon, principalement autour de la danse.
Nous nous retrouvions régulièrement après les cours ou pour aller manger ensemble.&lt;/p&gt;
&lt;h3&gt;Apprentissages&lt;/h3&gt;
&lt;p&gt;J'ai cartographié tout ce que j'ai appris sur le sujet dans cette &lt;a href="_assets/making-friends.png"&gt;mindmap&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En synthèse, trois facteurs jouent un rôle majeur dans la création d’amitiés :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1 — Temps partagé&lt;/strong&gt; : 
les relations se construisent surtout grâce aux interactions répétées. 
Viser des interactions amicales, même brèves, et régulières avec les gens.
Une activité régulière (sport, danse, atelier) facilite cela.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2 — Self-disclosure&lt;/strong&gt; : 
les relations progressent lorsque chacun partage des choses personnelles. 
Une bonne conversation équilibre curiosité pour l’autre et partage de soi. 
L'équilibre est très important. 
Les deux personnes avec lesquelles j'ai le plus connecté sont les personnes auxquelles je me suis le plus ouvert (Dạ Lan, Kevin).
Et cela a marché car j'ai aussi été à leur écoute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3 — Contexte&lt;/strong&gt; :
Les rencontres fonctionnent mieux dans des espaces où les gens partagent déjà un intérêt commun.
Les applications de rencontre créent souvent des interactions artificielles.&lt;/p&gt;
&lt;h3&gt;Le principe à retenir&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Investis dans tes centres d’intérêt, pas dans les plateformes.&lt;/p&gt;
&lt;/blockquote&gt;</content></entry><entry><title>7 mois au Vietnam - ce que j'y ai trouvé</title><link href="https://me.petitapetit.io/vietnam" rel="alternate"/><published>2026-02-18T00:00:00+01:00</published><updated>2026-02-18T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2026-02-18:/vietnam</id><summary type="html">&lt;p&gt;&lt;img alt="" src="_assets/saigon.png"&gt;&lt;/p&gt;
&lt;p&gt;Je viens de passer 7 mois au Vietnam.&lt;/p&gt;
&lt;p&gt;Il y a un an : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je sors d'une relation de 4 ans (une histoire sur 15 ans) &lt;/li&gt;
&lt;li&gt;Je viens de perdre 100k€ sur mon premier investissement immobilier&lt;/li&gt;
&lt;li&gt;J'ai peu de confiance en moi&lt;/li&gt;
&lt;li&gt;Je suis socialement isolé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je ne me projetais plus …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;img alt="" src="_assets/saigon.png"&gt;&lt;/p&gt;
&lt;p&gt;Je viens de passer 7 mois au Vietnam.&lt;/p&gt;
&lt;p&gt;Il y a un an : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je sors d'une relation de 4 ans (une histoire sur 15 ans) &lt;/li&gt;
&lt;li&gt;Je viens de perdre 100k€ sur mon premier investissement immobilier&lt;/li&gt;
&lt;li&gt;J'ai peu de confiance en moi&lt;/li&gt;
&lt;li&gt;Je suis socialement isolé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je ne me projetais plus en France.&lt;/p&gt;
&lt;p&gt;J'ai décidé de partir.&lt;/p&gt;
&lt;h3&gt;Préparation&lt;/h3&gt;
&lt;p&gt;Pour préparer ce voyage, j'ai :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;échangé avec ~10 expatriés francophones&lt;/li&gt;
&lt;li&gt;pris 1 cours d'anglais par semaine pendant 6 mois avec un Vietnamien&lt;/li&gt;
&lt;li&gt;lu 3 livres sur le pays (roman, contes, analyse économique et politique)&lt;/li&gt;
&lt;li&gt;pris des notes sur 4h de documentaire (période 1850-1976)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lire m'a donné du contexte.&lt;/p&gt;
&lt;p&gt;La réalité était encore différente.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;J'arrive à Ho Chi Minh City (Saigon) le 26 juin.&lt;/p&gt;
&lt;p&gt;On est en pleine saison des pluies.&lt;br&gt;
Chaque averse est comme une fête.&lt;/p&gt;
&lt;p&gt;Je passe 3 semaines en auberge.&lt;br&gt;
J'y fais 30 rencontres marquantes.&lt;/p&gt;
&lt;p&gt;Puis je m'installe à Bình Thạnh.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5 minutes de MAR Studio&lt;/li&gt;
&lt;li&gt;15 minutes du centre-ville&lt;/li&gt;
&lt;li&gt;2 minutes du canal&lt;/li&gt;
&lt;li&gt;Le seul expat du coin&lt;/li&gt;
&lt;li&gt;Plein de street food au pied de l'immeuble&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je tombe amoureux du quartier.&lt;/p&gt;
&lt;h3&gt;Immersion&lt;/h3&gt;
&lt;p&gt;Je cherchais un pays qui contraste avec la France.&lt;br&gt;
Je découvre que l'on a 100 ans d'histoire commune.&lt;br&gt;
Mais tout est différent.&lt;/p&gt;
&lt;p&gt;La nourriture est différente.&lt;br&gt;
Le rapport au soleil est différent.&lt;br&gt;
Les codes de conduite sont différents.&lt;/p&gt;
&lt;p&gt;Personne ne marche.&lt;br&gt;
Il fait trop chaud.&lt;br&gt;
Et les trottoirs sont occupés par la street food.&lt;br&gt;
Tables en plastique, tabourets pour enfant.&lt;/p&gt;
&lt;p&gt;La ville est pensée pour les scooters.&lt;br&gt;
Les voitures sont minoritaires et se trouvent bloquées.&lt;br&gt;
Dans des embouteillages, dans les rues sinueuses.&lt;/p&gt;
&lt;p&gt;Les motos s'insèrent sans regarder.&lt;br&gt;
Il est commun que deux roues remontent en longeant le trottoir.&lt;br&gt;
Personne ne sert à droite.&lt;/p&gt;
&lt;p&gt;Les gens transportent leur vie sur les scooters.&lt;br&gt; 
Des escabeaux sont empilés sur des aspirateurs, fixés par des tendeurs.&lt;br&gt;
Des familles circulent à quatre : les parents, deux bébés.&lt;br&gt;
Certaines voies sont réservées au scooters.&lt;br&gt;
Il y a des parkings dédiés aux scooters.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Le rythme de la ville est différent.&lt;br&gt;
Les personnes âgées font de la gym douce au levé du soleil.&lt;br&gt;
Le son des parleurs raisonne encore dans ma tête.&lt;br&gt;
"Bánh Mì Heo Quay, Bánh Mì Heo Quay".&lt;br&gt;
Tous les matins vers 6h.&lt;/p&gt;
&lt;p&gt;C'est un joyeux chaos.&lt;br&gt;
Avec une forme d'organisation.&lt;br&gt;
Et qui dégage un grand sentiment de liberté.&lt;/p&gt;
&lt;h3&gt;Ce que j'ai changé&lt;/h3&gt;
&lt;p&gt;À Caen, en 4 ans, je ne m'étais construit aucun cercle proche. &lt;/p&gt;
&lt;p&gt;Au Vietnam, j'ai testé de faire différemment : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vivre en auberge (contexte hyper social)&lt;/li&gt;
&lt;li&gt;saisir chaque opportunité d'engager la conversation&lt;/li&gt;
&lt;li&gt;rejoindre des communautés plutôt que faire seul&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'ai rejoint un running club, un groupe de méditation, je me suis lancé dans la danse.&lt;/p&gt;
&lt;h3&gt;Rythme de vie&lt;/h3&gt;
&lt;p&gt;Pendant 7 mois : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 à 6 sessions de danse par semaine (House, Hip hop, Zouk, Bachata)&lt;/li&gt;
&lt;li&gt;2 à 3 sorties running par semaine&lt;/li&gt;
&lt;li&gt;méditation régulière au bord du canal&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/coda"&gt;travail en remote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'ai voyagé au Vietnam : Saigon, Bến Tre, Delta du Mékong, Hanoi, Hà Giang, Ninh Bình, Đà Nẵng, Phú Quốc, Đà Lạt. À Taïwan : Taipei, Jiufen, Shifen. Et j'ai mis un pied au Cambodge : Moc Bai.&lt;/p&gt;
&lt;p&gt;Mais le coeur était Saigon.&lt;/p&gt;
&lt;p&gt;Je ne voulais pas enchaîner les destinations.&lt;br&gt;
Je voulais me poser et vivre la ville.&lt;/p&gt;
&lt;h3&gt;Les cercles&lt;/h3&gt;
&lt;p&gt;Les lieux comptent. Les rencontres ont donné de la profondeur.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Auberge&lt;/strong&gt; : Huy, Tu, Oanh, Daisy, Leong, Karsten, Dominique, Bayron, Dorian, Ellen, Laura, Florian, Abdula de Bahrein et le bois d'agar, Alex de Bonn, Max (k.j. dan), Damsith, Mayong, Abbas, Jasmin, Inès, Cris, Naman à Pho Quynh, Samuel, Nguyen, Yume, Lee, Bao, Captain Kim, Orange le gecko.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Danse (MAR Studio, Zouk Crew)&lt;/strong&gt; : Dạ Lan, Marc, Minh, Dany, Huy, Thao, Noémie, Banh, Lam, Son Clown, Flower, Supes, Olya, Snoopp, Alina, Tery, Dora, Ha, Ray, Melvin, Chi Daep, Hip, Phaï Fan, Camille.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Running club&lt;/strong&gt; : Trung, Hew, Dong, Hoi, Aelita, Duy, Kitty, Krishna, Mapple, Young, Minh, Sheng, Sarim, Uyen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Méditation&lt;/strong&gt; : Oanh, Thành.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La vie&lt;/strong&gt; : Châu, Kevin, Harshanki, Martin C, Martin A, Jeanne, Laurent, Sophie, Alexandre, Bruno, Benjamin, Phi, Ivan, Artem à la PyCon, Paul le patissier, Marc M, Taeki, Mustafa, Zou, Sam de Chine, Cheolsoo, Kénaël, Yuliya, Akash, Oleg, Son et Vy (trek), Filip, Milan, Dasha, les german kids, Czech granny, Huyen, l'équipe de Sweet Home, la dame qui me vendait des noix de cajou.&lt;/p&gt;
&lt;h3&gt;Aujourd'hui&lt;/h3&gt;
&lt;p&gt;Je suis de retour en France.&lt;/p&gt;
&lt;p&gt;Je garde les visages.&lt;br&gt;
La couleur des rues sous la pluie.&lt;br&gt;
Les parleurs de la street food.&lt;/p&gt;
&lt;p&gt;Et surtout : &lt;/p&gt;
&lt;p&gt;Dạ Lan.&lt;/p&gt;
&lt;p&gt;La danse.&lt;/p&gt;
&lt;p&gt;Je n'avais jamais osé avant.&lt;/p&gt;</content></entry><entry><title>Donner 5 jours de cours sur le Machine Learning à 17 étudiants</title><link href="https://me.petitapetit.io/coda" rel="alternate"/><published>2026-02-17T00:00:00+01:00</published><updated>2026-02-17T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2026-02-17:/coda</id><summary type="html">&lt;p&gt;J'ai donné 5 jours de cours sur la Data Science et le Machine Learning à des alternants en Bac+3.&lt;/p&gt;
&lt;p&gt;Il y a six mois, je ne connaissais rien au Machine Learning.&lt;br&gt;
Je n'avais jamais donné de cours.&lt;/p&gt;
&lt;p&gt;Pourquoi accepter ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Je cherchais une raison de consacrer du temps à ce …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;J'ai donné 5 jours de cours sur la Data Science et le Machine Learning à des alternants en Bac+3.&lt;/p&gt;
&lt;p&gt;Il y a six mois, je ne connaissais rien au Machine Learning.&lt;br&gt;
Je n'avais jamais donné de cours.&lt;/p&gt;
&lt;p&gt;Pourquoi accepter ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Je cherchais une raison de consacrer du temps à ce sujet&lt;/li&gt;
&lt;li&gt;Je voulais vérifier une hypothèse : enseigner est une arme pour apprendre&lt;/li&gt;
&lt;li&gt;Je préférais répondre à une demande réelle plutôt que de chercher un projet abstrait&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Yoan m'a proposé la mission.&lt;br&gt;
Il m'a fait confiance.&lt;br&gt;
J'ai accepté.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Voici comment ça c'est passé.&lt;/p&gt;
&lt;h3&gt;Aout : exploration libre&lt;/h3&gt;
&lt;p&gt;Lecture d'un livre général.&lt;br&gt;
Cours en ligne de Stanford.&lt;br&gt;
Implémentation from scratch des algorithmes principaux : régression linéaire, régression logistique, arbres, random forest, réseaux neuronaux.&lt;/p&gt;
&lt;p&gt;J'ai me suis exposé au sujet.&lt;br&gt;
Mais je n'ai pas avancé sur le projet.&lt;/p&gt;
&lt;p&gt;Je n'ai pas fait d'application réelle.&lt;br&gt;
Je me sens perdu dans l'immensité du sujet.&lt;br&gt;
Je sors de cette phase vidé d'énergie.&lt;/p&gt;
&lt;h3&gt;Septembre, Octobre, Novembre : Pause&lt;/h3&gt;
&lt;p&gt;Je voyage en Asie.
J'avance sur d'autres projets.&lt;/p&gt;
&lt;h3&gt;Décembre : Cadrage&lt;/h3&gt;
&lt;p&gt;Je repars des objectifs pédagogiques et établis un plan pour les 5 jours :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jour 1 : Data Science (Exploratory Data Analysis, Data Visualization)&lt;/li&gt;
&lt;li&gt;Jour 2 : Régression&lt;/li&gt;
&lt;li&gt;Jour 3 : Classification&lt;/li&gt;
&lt;li&gt;Jour 4 : NLP&lt;/li&gt;
&lt;li&gt;Jour 5 : Vision&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je rassemble dix livres sur la data science.&lt;br&gt;
Je les parcours.&lt;br&gt;
Je sélectionne les chapitres qui m'intéressent.&lt;/p&gt;
&lt;p&gt;J'explore des data sets.&lt;/p&gt;
&lt;p&gt;Après 4 semaines, une seule journée est prête.&lt;br&gt;
Mais cette fois-ci, c'est concret.
J'ai produis.
J'ai pratiqué.&lt;br&gt;
Chaque lecture nourrit le cours.&lt;/p&gt;
&lt;h3&gt;Janvier : Maturation du processus&lt;/h3&gt;
&lt;p&gt;En avançant, une structure apparaît.&lt;/p&gt;
&lt;p&gt;Chaque journée suit le même rythme : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le matin : théorie + exercices&lt;/li&gt;
&lt;li&gt;L'après-midi : projet + présentations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En préparant la 4ème journée, mon processus devient clair. &lt;/p&gt;
&lt;p&gt;Pour le thème &lt;em&gt;traitement du language naturel&lt;/em&gt; (NLP), ça a été : &lt;/p&gt;
&lt;h5&gt;1. Capturer la "big picture"&lt;/h5&gt;
&lt;p&gt;Taper "NLP" sur Youtube&lt;br&gt;
Regarder les 10 vidéos les plus vues. &lt;br&gt;
Prendre des notes sous forme de mindmap.&lt;/p&gt;
&lt;p&gt;C'est incroyable pour identifier les contours de la discipline et des ressources&lt;/p&gt;
&lt;h5&gt;2. Choisir un livre de référence&lt;/h5&gt;
&lt;p&gt;Youtube donne de l'intuition.&lt;br&gt;
Un livre donne de la profondeur.&lt;br&gt;
Pour la NLP : &lt;em&gt;Practical Natural Language Processing&lt;/em&gt;&lt;/p&gt;
&lt;h5&gt;3. Prendre des notes&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;concepts clés&lt;/li&gt;
&lt;li&gt;idées d'exercices&lt;/li&gt;
&lt;li&gt;idées de projets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Brainstormer sur ces trois points après-coup est laborieux.&lt;br&gt;
Quand on sait quoi chercher, collecter au fil de l'eau est sans effort.&lt;/p&gt;
&lt;h5&gt;4. Écrire un notebook théorie&lt;/h5&gt;
&lt;p&gt;Partir du problème&lt;br&gt;
Introduire chaque concept par nécessité.&lt;br&gt;
Démontrer. &lt;br&gt;
Le notebook me sert de script pour le tableau. &lt;br&gt;
Je le distribue ensuite.&lt;/p&gt;
&lt;h5&gt;5. Construire un notebook projet&lt;/h5&gt;
&lt;p&gt;Donner la trame imaginée à ChatGPT et lui demander de générer un notebook.&lt;br&gt;
Tout reprendre : questionner, simplifier, restructurer. &lt;br&gt;
Cela accélère le démarrage.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Pour chaque journée, je produis trois livrables clés : &lt;br&gt; 
un notebook théorie, un notebook exercices, un projet.&lt;/p&gt;
&lt;p&gt;Préparer m'oblige à clarifier.&lt;br&gt;
Expliquer révèle les zones de flou.&lt;br&gt;
Je plus de questions que d'habitude.&lt;/p&gt;
&lt;h3&gt;Février : Semaine de cours&lt;/h3&gt;
&lt;p&gt;J'ai animé une classé de 17 étudiants. &lt;/p&gt;
&lt;p&gt;Pendant la préparation : plusieurs montées d'angoisse.&lt;br&gt;
Pendant la semaine : du calme.&lt;/p&gt;
&lt;p&gt;Mes objectifs :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;susciter de la curiosité&lt;/li&gt;
&lt;li&gt;créer un cadre d'expérimentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une leçon ressort : la compréhension vient de la pratique.  &lt;/p&gt;
&lt;p&gt;Un concept expliqué n’est pas un concept assimilé.&lt;br&gt;
Plusieurs fois, après avoir présenté une notion, j’ai entendu : &lt;br&gt;
"C’est quoi ce truc ?"&lt;/p&gt;
&lt;p&gt;Un projet ancre davantage qu’une slide.&lt;/p&gt;
&lt;p&gt;Un truc qui a bien fonctionné : varier les formats.&lt;/p&gt;
&lt;p&gt;Slides, tableau, live coding, code à trous, projets, exposés étudiants.&lt;br&gt;
Quand un format devient routinier, l'attention baisse.&lt;br&gt;
Changer relance la dynamique.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;En six mois, je suis passé de zéro en Machine Learning à cinq jours de cours structurés.&lt;/p&gt;
&lt;p&gt;Enseigner impose un niveau d'exigence que l'auto-apprentissage n'atteint pas toujours.&lt;/p&gt;
&lt;p&gt;Je ne veux pas enseigner à temps plein.&lt;br&gt;
Mais je veux continuer à le faire.&lt;br&gt;
Au moins une fois par an.&lt;/p&gt;
&lt;p&gt;Pour les rencontres humaines.&lt;br&gt;
Et parce qu'enseigner est une méthode d'apprentissage puissante.&lt;/p&gt;</content></entry><entry><title>Mes intentions pour 2026</title><link href="https://me.petitapetit.io/2026" rel="alternate"/><published>2025-12-31T00:00:00+01:00</published><updated>2025-12-31T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-12-31:/2026</id><summary type="html">&lt;h3&gt;Vision à 3 ans&lt;/h3&gt;
&lt;p&gt;C'est flou. 
Je n'ai pas de vision à trois ans. &lt;/p&gt;
&lt;p&gt;Je peux être heureux salarié. Un CDI apporte une forme de tranquillité d'esprit, de la stabilité dans les revenus.
C'est la façon la plus simple de me remettre en mouvement professionnellement.
Je peux exploiter mon temps …&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Vision à 3 ans&lt;/h3&gt;
&lt;p&gt;C'est flou. 
Je n'ai pas de vision à trois ans. &lt;/p&gt;
&lt;p&gt;Je peux être heureux salarié. Un CDI apporte une forme de tranquillité d'esprit, de la stabilité dans les revenus.
C'est la façon la plus simple de me remettre en mouvement professionnellement.
Je peux exploiter mon temps libre pour créer et expérimenter.
Et je peux trouver un job qui me permet d'apprendre de nouvelles choses (vente, marketing, enseignement, restauration).&lt;/p&gt;
&lt;p&gt;Une fois en mouvement, je peux créer une activité.
C'est-à-dire, juste, me lancer.
Vivre, rencontrer des gens.
Identifier des besoins. 
Apporter des solutions.&lt;/p&gt;
&lt;p&gt;Mon plan à 3 ans est peut-être le suivant : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;continuer d'explorer&lt;/li&gt;
&lt;li&gt;continuer le Zouk et la danse consciente&lt;/li&gt;
&lt;li&gt;me donner à fond sur les projets qui m'excitent (danse, méditation, apprendre le Vietnamien, développer des apps)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je ne sais pas où je veux être. 
Mais je sais comment je veux me sentir.&lt;/p&gt;
&lt;h3&gt;Ma mission principale en 2026&lt;/h3&gt;
&lt;p&gt;→ &lt;strong&gt;Trouver une nouvelle source de revenus.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pour retrouver ma place dans la société par le travail.&lt;br&gt;
Pour financer les expériences qui m'enthousiasment (Rio Zouk Immersion ?).&lt;br&gt;
Et pour investir, bâtir dans la durée.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Je me sens perdu entre trois possibilités : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;créer une activité (avec &lt;a href="https://perspectives.ac/"&gt;Perspective&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;reprendre une entreprise (avec &lt;a href="https://tgimguild.com/fr/"&gt;TGIM&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;prendre un job classique&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mon intuition : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;trouver un job &lt;/li&gt;
&lt;li&gt;mettre de l'argent de côté (40%)&lt;/li&gt;
&lt;li&gt;être frugal&lt;/li&gt;
&lt;li&gt;dire non à ce qui ne m'inspire pas&lt;/li&gt;
&lt;li&gt;centrer mon énergie sur une sélection réduite de side projects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mes valeurs principales pour 2026&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Simplicité&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Posséder peu, chercher à m'alléger plutôt qu'à accumuler&lt;/li&gt;
&lt;li&gt;Partir du besoin et prendre un chemin simple pour y parvenir&lt;/li&gt;
&lt;li&gt;Tendre à une vie simple (un bureau, un toit, à manger — it's all what I need)&lt;/li&gt;
&lt;li&gt;Dire non plus souvent &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Playfulness (espièglerie)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suivre mon intuition, être diligent, saisir les opportunités qui se présentent&lt;/li&gt;
&lt;li&gt;Choisir l'option qui ouvre sur le plus de fun et d'aventure plutôt que de courir après la vague &lt;/li&gt;
&lt;li&gt;Faire moi-même plutôt que d'utiliser des libs externes&lt;/li&gt;
&lt;li&gt;Voir les obstacles comme des opportunités&lt;/li&gt;
&lt;li&gt;Ne pas attendre d'être prêt. (L'action crée l'information)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Honnêteté&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Être honnête avec moi-même&lt;/li&gt;
&lt;li&gt;Poser mes limites — et les tenir&lt;/li&gt;
&lt;li&gt;Consacrer du temps à ce que j'aime (pas à ce que je pense être censé faire)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Partager une newsletter hebdomadaire&lt;/li&gt;
&lt;li&gt;Publier des articles de fin de projet&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mes objectifs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Professionnels et financiers&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trouver une nouvelle source de revenus&lt;/li&gt;
&lt;li&gt;Me remettre en mouvement professionnellement&lt;/li&gt;
&lt;li&gt;Reconstituer un fond d'urgence (10-20k) pour retrouver de la sérénité&lt;/li&gt;
&lt;li&gt;Commencer à investir en DCA en cryptos + trackers (100€/semaine) pour construire sur le long terme&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Personnels&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Développer mon écosystème d'app personnelles&lt;/li&gt;
&lt;li&gt;Lancer une newsletter personnelle&lt;/li&gt;
&lt;li&gt;Continuer d'apprendre à danser (Zouk, Go through) — embarquer des gens avec moi ?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ma journée idéale en 2026&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Lever à 6-7h&lt;/li&gt;
&lt;li&gt;Méditation (15 min)&lt;/li&gt;
&lt;li&gt;Pratique de danse consciente (15 min)&lt;/li&gt;
&lt;li&gt;Petit déjeuner (salé)&lt;/li&gt;
&lt;li&gt;8h - 12h — deep work&lt;/li&gt;
&lt;li&gt;Pause déjeuner&lt;/li&gt;
&lt;li&gt;14h - 18h — deep work&lt;/li&gt;
&lt;li&gt;Social time (danse, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Ce que je souhaite + vivre, expérimenter, ressentir en 2026&lt;/h4&gt;
&lt;p&gt;Paix. Concentration. Joie. Amour. Amusement. Interactions humaines.&lt;/p&gt;
&lt;h4&gt;Ce que je souhaite moins vivre, expérimenter, ressentir en 2026&lt;/h4&gt;
&lt;p&gt;Hésitation, confusion.&lt;/p&gt;
&lt;h4&gt;Comment j'aimerais me souvenir de cette année dans 20 ans&lt;/h4&gt;
&lt;p&gt;L'année du sable.&lt;br&gt;
L'année du lâcher prise.&lt;br&gt;
Structuré à l'extérieur.&lt;br&gt;
Libre à l'intérieur.&lt;/p&gt;</content></entry><entry><title>Ma rétrospective de 2025</title><link href="https://me.petitapetit.io/2025" rel="alternate"/><published>2025-12-30T00:00:00+01:00</published><updated>2025-12-30T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-12-30:/2025</id><summary type="html">&lt;h2&gt;In a nutshell&lt;/h2&gt;
&lt;p&gt;A year of movement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I sold my house.&lt;/li&gt;
&lt;li&gt;I moved to Vietnam.&lt;/li&gt;
&lt;li&gt;I met Dạ Lan.&lt;/li&gt;
&lt;li&gt;I started dancing.&lt;/li&gt;
&lt;li&gt;I worked on myself.&lt;/li&gt;
&lt;li&gt;I ran a podcast.&lt;/li&gt;
&lt;li&gt;I interviewed Derek Sivers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I stepped away from my career and professional goals.&lt;/p&gt;
&lt;p&gt;In 2026, I want to build again …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;In a nutshell&lt;/h2&gt;
&lt;p&gt;A year of movement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I sold my house.&lt;/li&gt;
&lt;li&gt;I moved to Vietnam.&lt;/li&gt;
&lt;li&gt;I met Dạ Lan.&lt;/li&gt;
&lt;li&gt;I started dancing.&lt;/li&gt;
&lt;li&gt;I worked on myself.&lt;/li&gt;
&lt;li&gt;I ran a podcast.&lt;/li&gt;
&lt;li&gt;I interviewed Derek Sivers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I stepped away from my career and professional goals.&lt;/p&gt;
&lt;p&gt;In 2026, I want to build again — business, money, structure.&lt;/p&gt;
&lt;h2&gt;Analyse de mes objectifs&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Vendre ma maison et solder mes dettes&lt;/strong&gt;. Objectif accompli à 100%. Je n'y pense déjà plus. J'ai perdu 100k€. Mais aujourd'hui, je n'ai plus de dettes et je me sens léger. Je suis reconnaissant de mes parents pour leur soutien et de m'avoir hébergé presque six mois. À lui seul, cet objectif fait mon année.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lancer un podcast autour du refactoring&lt;/strong&gt;. I did it. Je me suis lancé sans sur-réfléchir. J'ai tourné un premier épisode, puis un deuxième ... Chaque interview était préparée en profondeur. J'ai été pro dans ma prise de contact. 100% des interviewés ont dit oui. Cela m'a permis de faire des rencontres (Nicolas, Adrien), de nourrir des relations existantes (Yoan) et d'interviewer de mon &lt;em&gt;role model&lt;/em&gt; (Derek Sivers). J'ai travaillé ma prise de parole en publique. J'ai publié les épisodes. Je n'ai pas atteint l'objectif d'enregistrer 24 épisodes. J'ai tout lâché en arrivant au Vietnam. Mais je pense que j'en avais besoin. C'était une façon de me plonger à 100% dans l'expérience de voyage. Je ne regrette pas ce choix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passer +4 mois au Vietnam&lt;/strong&gt;. Objectif accompli. J'ai sauté le pas. J'ai préparé le voyage en contactant des personnes sur place. J'ai fait mes vaccins. J'ai pris mon billet. Et go.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Être à l'aise pour tenir une conversation en anglais&lt;/strong&gt;. Je ne suis pas bilingue, mais je parle anglais tous les jours. Mon vocabulaire s'est enrichi. Je n'ai plus peur d'engager une conversation. J'ai mis en place une routine avec Anki. Je suis content de ma progression.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trouver une source de revenu pour 2026&lt;/strong&gt;. Objectif complètement mis de côté. Deux raisons. D'abord, vouloir être pleinement immergé au Vietnam. Ensuite d'être engagé dans la préparation d'un cours pour février. J'ai choisi de décrocher du travail pour m'explorer. J'entre en 2026 avec peu d'argent de côté et sans rentrées. Trouver une source de revenu va être ma priorité.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;M'aimer&lt;/strong&gt;. J'ai fait des progrès. J'ai travaillé ma confiance en moi. Je viens de terminer No More Mister Nice Guy. Ce livre m'a aidé à identifier des narrations négatives que j'avais intégrées sur moi-même. Ce n'est pas parfait, mais je suis sur la bonne voie.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Retrospective par trimestres&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Janvier&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je rends mon appartement à Rouen et j'emménage chez mon père (again)&lt;/li&gt;
&lt;li&gt;Je démarre des cours d'anglais avec Châu — mon premier point de contact avec le Vietnam&lt;/li&gt;
&lt;li&gt;J'échange avec six francophones qui vivent au Vietnam (Martin C., Jeanne, Laurent, Martin A., Benjamin, Bruno)&lt;/li&gt;
&lt;li&gt;Je reprends la course à pied — courir dans le froid et la brume m'inspire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Février&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je commence un accompagnement business avec Florent&lt;/li&gt;
&lt;li&gt;J'enregistre le premier épisode de Spaghetti to Bento (avec Nicolas)&lt;/li&gt;
&lt;li&gt;J'échange avec d'autres dev au sujet du refactoring (Julien, François), je suis heureux de catchup avec Pierrick.&lt;/li&gt;
&lt;li&gt;Je visite Auschwitz et Wieliczka Salt Mine avec Johnny (mon psy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Mars&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je m'occupe de ma santé avant d'aller au Vietnam (vaccins, hernie, poignet)&lt;/li&gt;
&lt;li&gt;Je passe une semaine au ski en famille — so good, I'm so grateful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Avril&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je vends la maison (signature de l'acte définitif). Ça y est.&lt;/li&gt;
&lt;li&gt;J'achète mon billet pour Saigon le lendemain.&lt;/li&gt;
&lt;li&gt;J'annonce au boulot que je pars fin juin.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Mai&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;J'enregistre un épisode avec Yoan et un avec Jonathan&lt;/li&gt;
&lt;li&gt;Je fais tout à vélo — y compris descendre en ville&lt;/li&gt;
&lt;li&gt;Je rencontre Yuuki&lt;/li&gt;
&lt;li&gt;60 ans de Mam à Clécy — I love this place&lt;/li&gt;
&lt;li&gt;60 ans de Dad à la maison — toujours aussi mal à l'aise pour danser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Juin&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je trie mes affaires, je me sépare de mes livres et donne 100% de mes vinyles à Matt&lt;/li&gt;
&lt;li&gt;Je vais à AlpesCraft et en profite pour voir Olivier, Clément&lt;/li&gt;
&lt;li&gt;Je suis de retour à temps pour l'anniversaire de Grégoire — j'ai beaucoup d'affection pour cette branche de la famille. Passer du temps avec eux me ramène en enfance.&lt;/li&gt;
&lt;li&gt;Je passe deux jours à Civray avec Matthias&lt;/li&gt;
&lt;li&gt;Je passe du temps à Paris, je vois lolo et Blandine&lt;/li&gt;
&lt;li&gt;Soirée pizza au panorama de Bonsecours avec Jason&lt;/li&gt;
&lt;li&gt;Je termine ma mission le 20 juin, juste avant le premier jour de l'été&lt;/li&gt;
&lt;li&gt;Je vais au feu de la Saint-Jean avec Mam&lt;/li&gt;
&lt;li&gt;Je fais une randonnée à vélo avec Dad, Alice, Christophe et Brigitte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je décolle pour Saigon le 26 avec mon ordi, mon micro, un jean et trois T-shirts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Juillet&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je passe 3 semaine à The Common Room Project. L'auberge est sur Bui Vien. Je me lie d'amitié avec Tu, Karsten, Dorian, Bayron, Dominique, Tora, Laura, Nicolas, Leong. J'y rencontre plus de monde que ces trois dernières années. J'aime ce lieu mais j'ai besoin d'un endroit calme pour travailler. Je teste un appart-hôtel à Bình Thạnh. Quartier local. Emplacement idéal. J'adore. &lt;/li&gt;
&lt;li&gt;J'achète un scooter à Bao&lt;/li&gt;
&lt;li&gt;Je prends des cours de prise de parole en public avec Harshanki&lt;/li&gt;
&lt;li&gt;J'interview Derek Sivers — l'interview ne se passe pas comme je l'espérais. Je perds cinq minutes de ma bande. Il quitte l'interview de façon précipitée. Je suis blessé, mais en même temps profondément reconnaissant d'avoir pu passer deux heures avec lui. Le contenu de l'interview est excellent.&lt;/li&gt;
&lt;li&gt;Je m'inscris sur Tinder. J'ai 30 fois plus de match qu'en France. Je fais quelques dates. Je suis mal à l'aise. Le processus me frustre. Je réalise que je sous-investis mes centres d'intérêts. Je désinstalle l'app et décide de rencontrer des personnes via mes centres d'intérêts. Je dépasse ma peur et je commence la danse. Je rejoins un running club.&lt;/li&gt;
&lt;li&gt;Je commence à prendre des cours de danse à Mar Studio (House). Je rencontre Marc, Lam.&lt;/li&gt;
&lt;li&gt;Je cours avec Founders Running Club Saigon les samedi à 6h30. Je rencontre Trung, Dong, Hoi, Hew, Kitty.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Aout&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dạ Lan me propose de prendre un verre au Smoothie bar. La même semaine, elle m'introduit au Zouk Crew. On passe une soirée à partager de nos vies au bord de la piscine de Tropical Garden. Elle me demande si je peux lui apprendre à conduire un scooter. I'm hooked.&lt;/li&gt;
&lt;li&gt;Je commence à apprendre le Zouk&lt;/li&gt;
&lt;li&gt;J'arrête le café&lt;/li&gt;
&lt;li&gt;Je participe à ma première sociale. Je ne connais aucun move, mais je suis excité.&lt;/li&gt;
&lt;li&gt;J'étudie le machine learning (livres, cours en ligne de Andrew Ng)&lt;/li&gt;
&lt;li&gt;Je me lie d'amitié avec Kevin, Huy et Thao. Je rencontre Lawrence dans le lobby.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Septembre&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;J'avoue à Dạ Lan que je développe des sentiments pour elle.&lt;/li&gt;
&lt;li&gt;Je passe 10 jours à Taïwan pour la PyCon et pour renouveler mon Visa. J'apprends quelques mots de chinois (Nirao, Chechie). Je fais un peu de tourisme. Je book un Night Market tour, je visite Jiufen and Shifen, j'explore la ville à vélo. Je rencontre Ivan et Mustapha, je fais du shopping, je mange dans un 7-eleven. Je découvre les Peanut Ice Cream Rolls, les beef-noodle soup et les taro-balls. Mais mon énergie est à autre chose. Je n'ai qu'une envie d'être avec à Saigon, de passer du temps avec Dạ Lan et d'avancer sur mes projets.&lt;/li&gt;
&lt;li&gt;Je voyage au Vietnam avec Corentin (Hanoï, Ha Giang, Ninh Binh, Da Nang, Saigon, Phu Quoc). Je rencontre Sam dans le train pour Da Nang. Je rencontre Akash dans la chambre d'auberge. On parle méditation. Il me relate son expérience avec Vipassana — ça a l'air fou. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Octobre&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On se met officiellement ensemble (le 10/10)&lt;/li&gt;
&lt;li&gt;Je rencontre des personnes qui méditent au bord du canal. Je me joins à eux&lt;/li&gt;
&lt;li&gt;Je ne me présente pas au run Garmin 10K — mais je cours 10km de mon côté la semaine qui suit&lt;/li&gt;
&lt;li&gt;J'arrête les cours de House pour aller davantage aux cours de Hip-hop&lt;/li&gt;
&lt;li&gt;On passe trois nuits à Da Lat, au Mơ Stay - Forest Resort — le lieu est incroyable. On n'en sortira qu'une fois, pour aller chasser les nuages au petit matin à Cau Dat Hill. On aura mis un pied à Da Lat juste à l'aller, lorsqu'on a fait un arrêt pour déjeuner avec le chauffeur du shuttle bus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Novembre&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je commence à apprendre le Vietnamien&lt;/li&gt;
&lt;li&gt;Je développe Monk (My Own Anki). D'abord en web app, puis en app mobile native&lt;/li&gt;
&lt;li&gt;Je me mets enfin à travailler sur la formation — fini de procrastiner&lt;/li&gt;
&lt;li&gt;Je me mets au Cà Phê Sữa đá. Lire : je reprends le café&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Décembre&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je passe les fêtes de fin d'année en république Tchèque avec Dạ Lan. Je rencontre ses parents, son frère, sa soeur et Milan, les german kids, le grand frère de son père. Je suis admiratif de ce qu'ils ont bâti ici.&lt;/li&gt;
&lt;li&gt;Je profite des vacances pour lire (en particulier Go Through, No More Mister Nice Guy), courir et dormir&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Leçons et réflexions principales&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Leçons que je retiens de 2025&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Essaie d'entrer en phase d'exploration avec un pipeline vide : être engagé sur le projet de machine learning pour février 2026 m'a empêché de connecter pleinement avec mon expérience au Vietnam. &lt;/li&gt;
&lt;li&gt;Maintien un &lt;em&gt;work in progress&lt;/em&gt;* bas : avoir trop de cartes dans mon deck de Vietnamien a réduit ma réactivité à apprendre de nouvelles tournures, plus adaptées aux situations que je rencontrais&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ce dont je suis le plus fier de cette année :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avoir commencé la danse&lt;/li&gt;
&lt;li&gt;m'être lancé à partir au Vietnam&lt;/li&gt;
&lt;li&gt;avoir soldé mes dettes accumulées avec la maison&lt;/li&gt;
&lt;li&gt;avoir développé Monk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Point sur les objectifs que je n’ai-je pas accomplis :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trouver une source de revenu pour 2026 → plus que jamais d'actualité.&lt;/li&gt;
&lt;li&gt;Enregistrer 24 épisodes de podcast. J'ai mis en pause le projet après cinq interviews. Bouger au Vietnam a changé ma dynamique. J'ai lâché mon focus tech sur le refactoring. Ce projet n'était plus en phase avec mon énergie. Il n'est pas directement d'actualité en 2026. Mais si je trouve un nouvel axe de focus, alors cela m'intéresserait de le relancer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ce pour quoi je le + de reconnaissance à la fin de cette année :&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dạ Lan — tout ce que l'on a partagé ensemble&lt;/li&gt;
&lt;li&gt;Les personnes que j'ai rencontrées sur internet et au Vietnam&lt;/li&gt;
&lt;li&gt;Avoir vécu +6 mois au Vietnam&lt;/li&gt;
&lt;li&gt;Mon scooter qui nous a transporté en sécurité dans les rues de Saigon&lt;/li&gt;
&lt;li&gt;Avoir voyagé trois semaines avec mon frère&lt;/li&gt;
&lt;li&gt;Mon ordinateur pour les innombrables services qu'il me rend&lt;/li&gt;
&lt;li&gt;Les livres que j'ai lus et qui m'ont inspirés&lt;/li&gt;
&lt;li&gt;Avoir trouvé une activité qui résonne intensément avec moi : la danse&lt;/li&gt;
&lt;li&gt;Mes séances de méditation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Ce qui me génère de l’énergie, de la joie :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Danser.&lt;/li&gt;
&lt;li&gt;Coder des programmes.&lt;/li&gt;
&lt;li&gt;Méditer.&lt;/li&gt;
&lt;li&gt;Écrire (journaling).&lt;/li&gt;
&lt;li&gt;Ne rien faire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ce qui me prend de l’énergie :&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Échouer à poser mes limites. Faire plaisir aux autres au détriment de mes besoins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ce que je ne fais pas à cause de la peur :&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Me lancer en indépendant.
Reprendre une boite.
Me dédier 100% à la danse.&lt;/p&gt;
&lt;h2&gt;Livres lu&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;No More Mr Nice Guy&lt;/li&gt;
&lt;li&gt;Steal Like An Artist&lt;/li&gt;
&lt;li&gt;Go Through - The conscious dance method to go through your blocages and find your creative potential&lt;/li&gt;
&lt;li&gt;Python Data Science Handbook (2nd édition)&lt;/li&gt;
&lt;li&gt;Love for Imperfect Things&lt;/li&gt;
&lt;li&gt;Guide pratique du tantrisme moderne&lt;/li&gt;
&lt;li&gt;The Relationship Handbook&lt;/li&gt;
&lt;li&gt;Finite and Infinite Games&lt;/li&gt;
&lt;li&gt;Histoires inavouables&lt;/li&gt;
&lt;li&gt;Elles se rendent pas compte&lt;/li&gt;
&lt;li&gt;Au Contraire! Figuring out the French&lt;/li&gt;
&lt;li&gt;Artificial Intelligence - A guide for thinking humans&lt;/li&gt;
&lt;li&gt;The Art of Witty Banter&lt;/li&gt;
&lt;li&gt;Better Small Talk - Talk to Anyone, Avoid Awkwardness, Generate Deep Conversations, and Make Real Friends&lt;/li&gt;
&lt;li&gt;30 Contes du Vietnam&lt;/li&gt;
&lt;li&gt;Vietnam — Rising Dragon&lt;/li&gt;
&lt;li&gt;The Power of Less&lt;/li&gt;
&lt;li&gt;Au-Dedans&lt;/li&gt;
&lt;li&gt;Gift From the Sea&lt;/li&gt;
&lt;li&gt;The Legacy Code Programmer's Toolbox&lt;/li&gt;
&lt;li&gt;Pour Que Chantent Les Montagnes&lt;/li&gt;
&lt;li&gt;Several Short Sentences About Writing&lt;/li&gt;
&lt;li&gt;The Art of the Interview&lt;/li&gt;
&lt;li&gt;On Writing Well&lt;/li&gt;
&lt;li&gt;Arabe du future&lt;/li&gt;
&lt;li&gt;Working Effectively with Legacy Code&lt;/li&gt;
&lt;li&gt;Legacy Code - First Aid Kit&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Top 50 musiques les plus écoutées&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rang&lt;/th&gt;
&lt;th&gt;Artiste&lt;/th&gt;
&lt;th&gt;Titre&lt;/th&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Too Insistent (Trentemøller Remix)&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Dust It Off&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Yanns&lt;/td&gt;
&lt;td&gt;Clic clic pan pan&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Desire&lt;/td&gt;
&lt;td&gt;Don't Call&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;ROSÉ&lt;/td&gt;
&lt;td&gt;APT.&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Despair, Hangover &amp;amp; Ecstasy&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Landrick&lt;/td&gt;
&lt;td&gt;Refúgio&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;KOMPROMAT&lt;/td&gt;
&lt;td&gt;De mon âme à ton âme&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Connan Mockasin&lt;/td&gt;
&lt;td&gt;Forever Dolphin Love - Erol Alkan Rework&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Sparks&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Rouge Gorge&lt;/td&gt;
&lt;td&gt;Aventure&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;ascendant vierge&lt;/td&gt;
&lt;td&gt;Lotus Noir&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Odezenne&lt;/td&gt;
&lt;td&gt;Bleu fuchsia&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Odezenne&lt;/td&gt;
&lt;td&gt;Hardcore&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Wolfgang Amadeus Mozart&lt;/td&gt;
&lt;td&gt;Requiem, K. 626: Lacrimosa&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;ascendant vierge&lt;/td&gt;
&lt;td&gt;Ce monde où tu n'existes pas&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Silly Boy Blue&lt;/td&gt;
&lt;td&gt;Cecilia&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Ryan.B&lt;/td&gt;
&lt;td&gt;No Reason&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Trustful Hands&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Miracles (Back in Time)&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;Liv del Estal&lt;/td&gt;
&lt;td&gt;Space Invaders&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Chappell Roan&lt;/td&gt;
&lt;td&gt;Good Luck, Babe!&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;Mansfield.TYA&lt;/td&gt;
&lt;td&gt;Une danse de mauvais goût&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Silly Boy Blue&lt;/td&gt;
&lt;td&gt;The Fight&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;Burial&lt;/td&gt;
&lt;td&gt;Archangel&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;Liv del Estal&lt;/td&gt;
&lt;td&gt;Amour Scandaleux&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;ascendant vierge&lt;/td&gt;
&lt;td&gt;Faire et refaire&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;DJ Seinfeld&lt;/td&gt;
&lt;td&gt;Now U Do&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;Malik Djoudi&lt;/td&gt;
&lt;td&gt;Sous garantie&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;Kking Kong&lt;/td&gt;
&lt;td&gt;Tarraxo da Meia Noite&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;Robin S&lt;/td&gt;
&lt;td&gt;Show Me Love&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;ELOI&lt;/td&gt;
&lt;td&gt;Aquarius&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;Fred again..&lt;/td&gt;
&lt;td&gt;Baby again..&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;Rouge Gorge&lt;/td&gt;
&lt;td&gt;Quel idiot&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;Metro Boomin&lt;/td&gt;
&lt;td&gt;Creepin' (with The Weeknd &amp;amp; 21 Savage)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;Odezenne&lt;/td&gt;
&lt;td&gt;Souffle le vent&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;ascendant vierge&lt;/td&gt;
&lt;td&gt;Slowlita&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;Fred again..&lt;/td&gt;
&lt;td&gt;Kammy (like i do)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;Fred again..&lt;/td&gt;
&lt;td&gt;just stand there&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;ascendant vierge&lt;/td&gt;
&lt;td&gt;XOXO&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;Keep Your Lips Sealed&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;Swimming Paul&lt;/td&gt;
&lt;td&gt;Fading&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;The Dø&lt;/td&gt;
&lt;td&gt;A Mess Like This&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;Aventura&lt;/td&gt;
&lt;td&gt;Por un Segundo&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;td&gt;Léonie Pernet&lt;/td&gt;
&lt;td&gt;Butterfly&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;The Knife&lt;/td&gt;
&lt;td&gt;Silent Shout&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;Moderat&lt;/td&gt;
&lt;td&gt;Reminder&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;Contrefaçon&lt;/td&gt;
&lt;td&gt;Arp&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;Frankie Knuckles&lt;/td&gt;
&lt;td&gt;Your Love&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;Artemas&lt;/td&gt;
&lt;td&gt;i like the way you kiss me&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content></entry><entry><title>What kind of music do you listen to?</title><link href="https://me.petitapetit.io/music" rel="alternate"/><published>2025-08-16T00:00:00+02:00</published><updated>2025-08-16T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-08-16:/music</id><summary type="html">&lt;h2&gt;Why this question&lt;/h2&gt;
&lt;p&gt;Part of improving conversations rely on being prepared to answer &lt;a href="conversation"&gt;common questions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;"What kind of music do you listen to?" is a one of them.&lt;/p&gt;
&lt;p&gt;And we answer it so badly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"A bit of everything" &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, thank you bro. What do you want me to do with …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Why this question&lt;/h2&gt;
&lt;p&gt;Part of improving conversations rely on being prepared to answer &lt;a href="conversation"&gt;common questions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;"What kind of music do you listen to?" is a one of them.&lt;/p&gt;
&lt;p&gt;And we answer it so badly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"A bit of everything" &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, thank you bro. What do you want me to do with this answer.&lt;/p&gt;
&lt;p&gt;I've always struggled to answer it. I know there is a trend. But it's hard to discern, and even harder to describe.&lt;/p&gt;
&lt;h2&gt;How to answer it&lt;/h2&gt;
&lt;p&gt;First.&lt;/p&gt;
&lt;p&gt;It's true that you listen to a bit of everything. But that is not the point. This kind of question shouldn't be taken too litarally. The point is just to move the conversation. Just bring something interesting and relatable.&lt;/p&gt;
&lt;p&gt;Second.&lt;/p&gt;
&lt;p&gt;Let's ask some help to ChatGPT.&lt;/p&gt;
&lt;p&gt;Retrieve your favorite songs from spotify.&lt;/p&gt;
&lt;p&gt;Then ask ChatGPT:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How would you describe the music I listen to? Here is my favorite tracks of the last two years on spotify. [PASTE THE TRACKLIST]&lt;/li&gt;
&lt;li&gt;Can you create a one-sentence that I can drop in a short conversation?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;So ... what kind of music do I listen?&lt;/h2&gt;
&lt;p&gt;It evolved over time. When 10 years old, I was more into RnB (Mario Winans, Usher, Willy Denzey). I moved to rap (Sniper, Booba), then rock with bands like Linkin Park, Placebo. At 15, I was in love with Club House (Eryc Prydz, Steve Angello). I had an electronic rock phase with bands like Justice, Nasser, The Bloody Beetroots. I dove into the pop-electronic blends of 2 many dj's. I discovered minimal techno. Then I moved to paris. The techno scene was booming. I was in awe with deep techno, post-punk and new wave revival. When I left Paris, I came back to pop alt. I love french electronics with voices. What I listen today is a blend of all these influences.&lt;/p&gt;
&lt;p&gt;One-liners:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Beats you can dance and cry at the same time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Stuff that's somewhere between the club and inside your head. With a french twist.&lt;/li&gt;
&lt;li&gt;A mix of intense emotional electronic music and french experimental pop&lt;/li&gt;
&lt;li&gt;Electronic music you can dance and get lost in. Plus some french stuff&lt;/li&gt;
&lt;li&gt;Dark, dreamy electronic and French experiments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In 5 adjectives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Atmospheric&lt;/li&gt;
&lt;li&gt;Intense&lt;/li&gt;
&lt;li&gt;Nostalgic&lt;/li&gt;
&lt;li&gt;Provocative&lt;/li&gt;
&lt;li&gt;Emotional&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trends: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dark, emotional, atmospheric electronics dance&lt;/strong&gt;: Burial, Bicep, Mathew Jonson, Freg again. Layered, textured beats - both collective and introspective. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;French experiments and electro-pop&lt;/strong&gt;: Ascendant Vierge, Sexy Sushi, ELOI, Yoa, Zaho de Sagazan, Odezenne, The Knife, The Do. A love for experimental French voices that mix poetry, irony, and dark energy. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern latin grooves&lt;/strong&gt;: Rosalia, DJ Kakah, Landrick. Sensual and physical. Music you live with your body and with others, not just thinking in your head.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Icons &amp;amp; timeless songs&lt;/strong&gt;: Mylène Farmer, Madonna, Grace Jones, Portishead. Big classics that carry an emotional punch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avant-garde and modern classical&lt;/strong&gt;: Philip Glass, Michael Nyman, Max Richter, Brian Eno, Steve Reigh, Sofiane Pamart. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Playful detours&lt;/strong&gt;: Dionysos, Doc Gynéco, 113, Johan Papaconstantino, Yanns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Me in 12 tracks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Burial - Archangel&lt;/li&gt;
&lt;li&gt;Fred again.. - Kammy (like I do)&lt;/li&gt;
&lt;li&gt;Bicep - Apricots&lt;/li&gt;
&lt;li&gt;Ascendant Vierge - Slowlita&lt;/li&gt;
&lt;li&gt;The Art of Noise - Moments in Love&lt;/li&gt;
&lt;li&gt;The Knife - Silent Shout&lt;/li&gt;
&lt;li&gt;Portishead - The rip&lt;/li&gt;
&lt;li&gt;Eloi - jtm de ouf (electro-pop, new french chanson)&lt;/li&gt;
&lt;li&gt;DJ Kakah - Call Me&lt;/li&gt;
&lt;li&gt;Landrick - Refugio&lt;/li&gt;
&lt;li&gt;Rosalia - LA FAMA&lt;/li&gt;
&lt;li&gt;Michael Nyman - String Quartet NO. 1: fig. H&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>Pourquoi j'ai donné 95 % de ce que je possédais</title><link href="https://me.petitapetit.io/stuff" rel="alternate"/><published>2025-07-01T00:00:00+02:00</published><updated>2025-07-01T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-07-01:/stuff</id><summary type="html">&lt;!-- 
V1 rédigée le 2025-05-13 en mode intention. 
V2 rédigée le 2026-03-10 en mode bilan
--&gt;

&lt;p&gt;J'ai tendance à collectionner.&lt;/p&gt;
&lt;p&gt;J'ai collectionné les images Dragon Ball Z.&lt;br&gt;
J'ai collectionné les &lt;a href="vinyles"&gt;vinyles&lt;/a&gt;.&lt;br&gt;
J'ai collectionné les livres.&lt;/p&gt;
&lt;p&gt;À un moment, je passais plus de temps en librairie à chercher de nouveaux livres qu'à lire.&lt;/p&gt;
&lt;p&gt;J'ai collectionné les concepts dans ma base de connaissances.&lt;/p&gt;
&lt;p&gt;Ces dernières années, j'ai accumulé …&lt;/p&gt;</summary><content type="html">&lt;!-- 
V1 rédigée le 2025-05-13 en mode intention. 
V2 rédigée le 2026-03-10 en mode bilan
--&gt;

&lt;p&gt;J'ai tendance à collectionner.&lt;/p&gt;
&lt;p&gt;J'ai collectionné les images Dragon Ball Z.&lt;br&gt;
J'ai collectionné les &lt;a href="vinyles"&gt;vinyles&lt;/a&gt;.&lt;br&gt;
J'ai collectionné les livres.&lt;/p&gt;
&lt;p&gt;À un moment, je passais plus de temps en librairie à chercher de nouveaux livres qu'à lire.&lt;/p&gt;
&lt;p&gt;J'ai collectionné les concepts dans ma base de connaissances.&lt;/p&gt;
&lt;p&gt;Ces dernières années, j'ai accumulé beaucoup d'objets.&lt;/p&gt;
&lt;h3&gt;Le moment charnière&lt;/h3&gt;
&lt;p&gt;En avril, j’ai vendu ma maison.&lt;/p&gt;
&lt;p&gt;J'ai traversé plusieurs années compliquées financièrement.
La vente m’a coûté environ 100 000 €.
Mais ce jour-là, quelque chose s’est desserré.&lt;/p&gt;
&lt;p&gt;Je suis sorti de l’enfermement dans lequel je m’étais mis.&lt;/p&gt;
&lt;p&gt;Le lendemain, j’ai acheté un aller simple pour le Vietnam.&lt;/p&gt;
&lt;h3&gt;Pourquoi donner mes objets&lt;/h3&gt;
&lt;p&gt;Voici 13 raisons pour lesquelles j'ai laissé aller 95 % de ce que je possédais. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Voyager léger&lt;/strong&gt;&lt;br&gt;
Je partais au Vietnam dans un mois.&lt;br&gt;
Je ne savais pas où je serai dans 6 mois.   &lt;/p&gt;
&lt;p&gt;Je voulais partir sans laisser derrière moi un tas d’affaires non réglées.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Créer plutôt que consommer&lt;/strong&gt;&lt;br&gt;
Je veux optimiser ma vie pour créer.&lt;br&gt;
Pas pour accumuler.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Laisser les objets vivre&lt;/strong&gt;&lt;br&gt;
Je ne les utilise plus.&lt;/p&gt;
&lt;p&gt;Mais quelqu'un d'autre pourrait.&lt;/p&gt;
&lt;p&gt;Je veux que ces objets vivent.&lt;br&gt;
Je veux qu'ils apportent de la joie à d'autres, comme ils m'en ont apporté. &lt;/p&gt;
&lt;p&gt;Je préfère les voir vivre ailleurs plutôt que de les garder enfermés chez moi.&lt;/p&gt;
&lt;p&gt;Je veux m'en séparer &lt;em&gt;parce que j'y tiens&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Chercher à l'intérieur plutôt qu'à l'extérieur&lt;/strong&gt;&lt;br&gt;
Je préfère chercher ce dont j’ai besoin à l’intérieur de moi plutôt que de courir après des objets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Trop de choses gêne les mouvements&lt;/strong&gt;&lt;br&gt;
Posséder trop de choses, c’est comme porter :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un t-shirt&lt;/li&gt;
&lt;li&gt;un sous-pull&lt;/li&gt;
&lt;li&gt;un pull&lt;/li&gt;
&lt;li&gt;un pull oversize&lt;/li&gt;
&lt;li&gt;un coupe-vent&lt;/li&gt;
&lt;li&gt;une doudoune&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tu es prêt à tout.&lt;/p&gt;
&lt;p&gt;Mais tu ne peux plus bouger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Centrer mon attention sur ce qui importe&lt;/strong&gt; &lt;br&gt;
Tous ces objets rivalisent pour attirer mon attention.&lt;br&gt;
Je veux me concentrer sur ceux qui comptent vraiment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Clarifier mes priorités&lt;/strong&gt;&lt;br&gt;
Quand j'ai trop de choses, je ne discerne plus ce qui est important.&lt;br&gt;
Laisser aller m'aide à garder les idées claires.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Fermer un chapitre&lt;/strong&gt;&lt;br&gt;
Ces objets étaient liés à des espoirs anciens.&lt;/p&gt;
&lt;p&gt;Les vinyles : l’espoir de trouver une place dans le milieu de la musique.&lt;/p&gt;
&lt;p&gt;Les livres : l’espoir de devenir artiste numérique.&lt;/p&gt;
&lt;p&gt;Les garder entretenait ces espoirs.&lt;/p&gt;
&lt;p&gt;Les laisser partir me permet d’ouvrir un nouveau chapitre.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. Garder la mémoire autrement&lt;/strong&gt; &lt;br&gt;
Les objets documentent mon passé. &lt;/p&gt;
&lt;p&gt;Mais je n'ai pas besoin de les garder physiquement. &lt;/p&gt;
&lt;p&gt;Je peux conserver des photos, des notes, des souvenirs.&lt;br&gt;
Je peux m'y reconnecter par la pensée. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10. Arrêter de chercher à prouver ma valeur&lt;/strong&gt;&lt;br&gt;
Accumuler ces objets a parfois été une façon de prouver ma valeur. &lt;/p&gt;
&lt;p&gt;Aujourd’hui je préfère me définir par ce que je fais et ce que je suis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11. Donner mon attention aux personnes&lt;/strong&gt;&lt;br&gt;
Je veux donner mon attention aux personnes.&lt;br&gt;
Moins aux objets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12. Le coût de la peur de manquer&lt;/strong&gt;&lt;br&gt;
&lt;a href="vaucelles"&gt;le FOMO m'a coûté 100k€&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13. Réduire mes besoins&lt;/strong&gt;&lt;br&gt;
Moins j’ai besoin de choses, plus je suis libre.&lt;/p&gt;
&lt;!--
**Raison 14** : 
J'espère que cela va inspirer des personnes autour de moi. Par exemple mes parents. Et que cela les aidera eux-aussi à aller de l'avant et à être plus heureux.

Cette raison est privée. 
Je la garde pour moi.
--&gt;

&lt;h3&gt;Ce que j'ai gardé&lt;/h3&gt;
&lt;p&gt;Je n’ai pas tout donné.&lt;/p&gt;
&lt;p&gt;J’ai gardé quelques objets.&lt;/p&gt;
&lt;p&gt;Des outils :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un bureau&lt;/li&gt;
&lt;li&gt;un écran&lt;/li&gt;
&lt;li&gt;une paire d’enceintes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Des archives personnelles : une boîte de carnets&lt;/p&gt;
&lt;p&gt;Un objet relationnel : une lampe fabriquée par mon ex&lt;/p&gt;
&lt;p&gt;Un objet de liberté : mon vélo et mon équipement de bikepacking.&lt;/p&gt;
&lt;h3&gt;Comment je m'y suis pris&lt;/h3&gt;
&lt;p&gt;En faire ma priorité et bloquer du temps pour cela.&lt;/p&gt;
&lt;p&gt;Approche inspirée de Marie Kondo.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tout sortir&lt;/li&gt;
&lt;li&gt;toucher chaque objet&lt;/li&gt;
&lt;li&gt;écouter ce que je ressens&lt;/li&gt;
&lt;li&gt;remercier et laisser partir&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je prenais aussi des photos de certains objets (comme les &lt;a href="vinyles"&gt;vinyles&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Et je les donnais à des personnes qui en feraient quelque chose.&lt;/p&gt;
&lt;h3&gt;Au final&lt;/h3&gt;
&lt;p&gt;Je suis parti au Vietnam avec :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un jean&lt;/li&gt;
&lt;li&gt;un short&lt;/li&gt;
&lt;li&gt;trois t-shirts&lt;/li&gt;
&lt;li&gt;mon micro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;basta.&lt;/p&gt;
&lt;p&gt;C’était largement suffisant pour commencer le chapitre suivant.&lt;/p&gt;</content></entry><entry><title>How to improve expressiveness in a flux/redux Dash</title><link href="https://me.petitapetit.io/dash-with-redux-improved" rel="alternate"/><published>2025-05-26T00:00:00+02:00</published><updated>2025-05-26T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-26:/dash-with-redux-improved</id><summary type="html">&lt;p&gt;We've seen &lt;a href="dash-to-redux"&gt;how to use the flux/redux architecture with Dash&lt;/a&gt;.
Then &lt;a href="dash-with-redux"&gt;how to refactor a common dash app, to a dash app that use a flux/redux pattern. Without breaking it&lt;/a&gt;.
Let's dig on how to improve expressiveness and type-safety in a flux/redux architecture with Dash.&lt;/p&gt;
&lt;p&gt;The idea …&lt;/p&gt;</summary><content type="html">&lt;p&gt;We've seen &lt;a href="dash-to-redux"&gt;how to use the flux/redux architecture with Dash&lt;/a&gt;.
Then &lt;a href="dash-with-redux"&gt;how to refactor a common dash app, to a dash app that use a flux/redux pattern. Without breaking it&lt;/a&gt;.
Let's dig on how to improve expressiveness and type-safety in a flux/redux architecture with Dash.&lt;/p&gt;
&lt;p&gt;The idea is to replace the couple (action's enum, payload) by class actions.&lt;/p&gt;
&lt;h2&gt;The demo app&lt;/h2&gt;
&lt;p&gt;I developed a minimal app that compare the two approaches.&lt;/p&gt;
&lt;p&gt;It's a single-page app that allows you to send a message to a city:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dash-with-redux-improved-animated.gif"&gt;&lt;/p&gt;
&lt;p&gt;The code is available &lt;a href="https://github.com/petitapetitio/dash-with-redux-improved"&gt;here&lt;/a&gt;. It contains 2 apps: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an implementation with an action's enum and a payload (&lt;a href="https://github.com/petitapetitio/dash-with-redux-improved/tree/main/before"&gt;before&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;an implementation with an actions classes (&lt;a href="https://github.com/petitapetitio/dash-with-redux-improved/tree/main/after"&gt;after&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Breaking down of the implementation&lt;/h2&gt;
&lt;p&gt;On the state management side,&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# state.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;SETUP_CITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SETUP_CITY&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;becomes&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# state.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SetupCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&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;SetupCity&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;SetupCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SetupCity&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;SetupCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetupCity&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The payload parameter disappear. 
Its content is now embodied in the action.&lt;/p&gt;
&lt;p&gt;You don't access the payload's values through hardcoded values anymore (like &lt;code&gt;payload["options"]&lt;/code&gt;). 
You reference the action's attributes (like &lt;code&gt;action.options&lt;/code&gt;).
It allows :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;linters to alert you when you miss-typed on the read part&lt;/li&gt;
&lt;li&gt;linters to alert you when you miss-typed on the "create" part&lt;/li&gt;
&lt;li&gt;automated refactorings&lt;/li&gt;
&lt;li&gt;auto-completion&lt;/li&gt;
&lt;li&gt;typing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the callback side,&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;selected_country&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country&amp;quot;&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

    &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_cities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&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;selected_country&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SETUP_CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&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;state&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;send-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SEND_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SETUP_CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&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;state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;becomes&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SetupCity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SelectCountry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SelectCity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SetMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;selected_country&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country&amp;quot;&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

    &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_cities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&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;selected_country&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SelectCountry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetupCity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&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;state&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;send-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SelectCountry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetupCity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SetMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset&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;state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Usage of hard-coded values is reduced, and actions factory methods brings expressiveness.&lt;/p&gt;</content></entry><entry><title>How to refactor a common Dash app to flux/redux</title><link href="https://me.petitapetit.io/dash-to-redux" rel="alternate"/><published>2025-05-25T00:00:00+02:00</published><updated>2025-05-25T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-25:/dash-to-redux</id><summary type="html">&lt;p&gt;We've seen &lt;a href="dash-to-redux"&gt;how to use the flux/redux architecture with Dash&lt;/a&gt;. 
The next question is: how to refactor a common dash app, to a dash app that use a flux/redux pattern. 
Without breaking it. 
Let's answer it.&lt;/p&gt;
&lt;p&gt;Our starting point is a minimal application. 
It contains a single page …&lt;/p&gt;</summary><content type="html">&lt;p&gt;We've seen &lt;a href="dash-to-redux"&gt;how to use the flux/redux architecture with Dash&lt;/a&gt;. 
The next question is: how to refactor a common dash app, to a dash app that use a flux/redux pattern. 
Without breaking it. 
Let's answer it.&lt;/p&gt;
&lt;p&gt;Our starting point is a minimal application. 
It contains a single page and 3 UI components whose behaviors are chained:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dash-to-redux-application.png"&gt;&lt;/p&gt;
&lt;p&gt;A typical scenario is : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You select a country &lt;/li&gt;
&lt;li&gt;In reaction to this action, dash populate the options of the city dropdown&lt;/li&gt;
&lt;li&gt;You select a city&lt;/li&gt;
&lt;li&gt;In reaction to this action, dash enable the submit button&lt;/li&gt;
&lt;li&gt;You submit your choice&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the code:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dash&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dash_bootstrap_components&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dbc&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;external_stylesheets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BOOTSTRAP&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;France&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Paris&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Lyon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Marseille&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;USA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;New York&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Los Angeles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Chicago&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;Japan&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Tokyo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Kyoto&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Osaka&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Select Location&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mb-4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Country&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dropdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Select a country&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;clearable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;City&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dropdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Select a city&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;clearable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mt-3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;dbc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s2"&gt;&amp;quot;Submit&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mt-3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;result-output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;margin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;16px&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_city&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;result-output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;children&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_submit_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;You selected &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Initializing the scaffolding&lt;/h2&gt;
&lt;p&gt;Create a file &lt;code&gt;state.py&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;

&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Reducer for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; isn&amp;#39;t implemented.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;app.py&lt;/code&gt;, add the store that will hold the state in the layout:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Select Location&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mb-4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then create the downstream callback at the bottom of &lt;code&gt;app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code we added has no impact on the behavior of the app. And we are ready to put the reduce in action.&lt;/p&gt;
&lt;h2&gt;Transitioning callbacks one by one&lt;/h2&gt;
&lt;p&gt;For each callback, the idea is to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Put the "Output fields" in the state&lt;/li&gt;
&lt;li&gt;Declare the state as a "State input" of the callback&lt;/li&gt;
&lt;li&gt;Add the state as parameter of the callback&lt;/li&gt;
&lt;li&gt;Create an action - and implement it's reducer &lt;/li&gt;
&lt;li&gt;Compute the reduced state in the callback &lt;/li&gt;
&lt;li&gt;Move the "Output fields" of the callback in the downstream callback &lt;/li&gt;
&lt;li&gt;Implement the selectors in the downstream callback &lt;/li&gt;
&lt;li&gt;Declare the state as an Output of the callback &lt;/li&gt;
&lt;li&gt;Return the reduced state&lt;/li&gt;
&lt;li&gt;Remove the State inputs that aren't used anymore in the callback&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It's easier to start from the beginning of the flow 
- because what comes next builds on top what comes before. 
So let's start with the first callback: &lt;code&gt;on_select_country&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a reminder, this is our callback:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Introduce the output fields in the state and initialize the values as they are defined in the DOM:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&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;Add the state as a "State input" of the callback:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- here&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;#         and here&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;#                                    vvvvvvvvvvv&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create an action:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Implement it's reducer (you can copy what is done in the callback):&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;payload&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&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;Then compute the reduced state in the callback:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CITIES_BY_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- NEW LINE&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We're almost done. The app doesn't use the state yet - but it's still working.&lt;/p&gt;
&lt;h2&gt;Branching&lt;/h2&gt;
&lt;p&gt;This time, we won't avoid breaking the application. Let's be succinct:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move the outputs of the callback in the downstream callback&lt;/li&gt;
&lt;li&gt;Implement the selectors in the downstream callback&lt;/li&gt;
&lt;li&gt;Add the state as the output of the callback&lt;/li&gt;
&lt;li&gt;Return the reduced state&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_select_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SELECT_COUNTRY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selected_country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&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;Run the app: &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;app.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first callback now use the state and a flux/redux architecture.&lt;/p&gt;
&lt;!--
Faux
## Cleaning

An interesting effect of this architecture is that we don't have to define the defaults in the dom anymore. We just need to define them in the centralised state. 
--&gt;

&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dropdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Select a city&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- can be removed&lt;/span&gt;
    &lt;span class="n"&gt;clearable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&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;Now, repeat the operation for the other callbacks.&lt;/p&gt;
&lt;h1&gt;Closing thoughts&lt;/h1&gt;
&lt;p&gt;You will need to mark allow duplicate : Output("state", "data", allow_duplicate=True),&lt;/p&gt;
&lt;p&gt;There is no easy way to get the debugging comfort provided by redux dev tool. But you can log inside the reducer and in the downstream callback to collect insights of what's going on.&lt;/p&gt;
&lt;p&gt;On callback that takes decision based on the state of the DOM, you can finally remove these inputs:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;- not necessary anymore&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;    &lt;span class="c1"&gt;# &amp;lt;- not necessary anymore&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_submit_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DISPLAY_RESULT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This visualisation compare the callback flow of the two architectures.
&lt;img alt="" src="_assets/dash-to-redux-unfolded.jpg"&gt;
It makes obvious that the flux/redux architecture is more involved - but it scales better. For simple use cases, the dash flow is fine. For rich use cases, you need to break things down in order to allow local reasoning and clear intent.&lt;/p&gt;
&lt;p&gt;Because Python as real types, we could even use commands. This way:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SelectCity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;reduce&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SelectCity&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&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;This provides several advantages:
- easier refactoring of the payloards
- enables type checking
- less hard coded values&lt;/p&gt;
&lt;p&gt;But: 
- this is maybe going to far
- people familiar with redux will understand more easily the current way it's done&lt;/p&gt;
&lt;p&gt;I think that using dash this way is pushing it too far. 
Dash is fine for fast prototyping. 
When the app get richer, it becomes unmanageable. 
Summoning the flux/redux is a way to break it down, and add more clarity. 
But if you plan for a complex app, better starting with react directly.&lt;/p&gt;
&lt;p&gt;It's the trap with such frameworks. 
It makes things easier in the beginning. 
This allows you to go faster then.
But when complexity, you start fighting with it. 
The architectural patterns that made sens for prototyping may not be relevant at scale. 
You try to make the pattern evolve, but these frameworks are opinionated. 
And you start spending more and more time trying to tweak it to fit your needs.&lt;/p&gt;</content></entry><entry><title>How to use the flux/redux architecture with Dash</title><link href="https://me.petitapetit.io/dash-with-redux" rel="alternate"/><published>2025-05-24T00:00:00+02:00</published><updated>2025-05-24T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-24:/dash-with-redux</id><summary type="html">&lt;p&gt;Have you ever said&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dash @*&amp;amp;#?!. With React it's simpler.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;?&lt;/p&gt;
&lt;p&gt;If yes, this article will interest you. &lt;/p&gt;
&lt;p&gt;Here is how to use the flux/redux architecture with Dash.&lt;/p&gt;
&lt;p&gt;I've developed a minimal application that shows the important concepts. 
You can get the code at &lt;a href="https://github.com/petitapetitio/dash-with-redux"&gt;https://github.com/petitapetitio/dash-with-redux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Have you ever said&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dash @*&amp;amp;#?!. With React it's simpler.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;?&lt;/p&gt;
&lt;p&gt;If yes, this article will interest you. &lt;/p&gt;
&lt;p&gt;Here is how to use the flux/redux architecture with Dash.&lt;/p&gt;
&lt;p&gt;I've developed a minimal application that shows the important concepts. 
You can get the code at &lt;a href="https://github.com/petitapetitio/dash-with-redux"&gt;https://github.com/petitapetitio/dash-with-redux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to introducing flux/redux, it also shows how to instanciate dash pages in a way that allow dependency injection and reduce usage of global variables.&lt;/p&gt;
&lt;h1&gt;Breaking down the architecture&lt;/h1&gt;
&lt;p&gt;The global mechanism can be seen this way:
&lt;img alt="" src="_assets/dash-to-redux-dash-vs-redux.jpg"&gt;&lt;/p&gt;
&lt;p&gt;In common Dash, the layout triggers callbacks. Callbacks do computations, and returns what needs to be modified in the layout. &lt;/p&gt;
&lt;p&gt;In flux/redux Dash, the layout triggers callback too. Callback compute the next state by dispatching an action. The action is handled by a reducer. The callback return the new state. And a downstream callback catch any state modification, in order to update the layout. &lt;/p&gt;
&lt;p&gt;It involves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;States&lt;/strong&gt;. A state is stored in a &lt;code&gt;dcc.Store&lt;/code&gt;. The main state is in the over-arching layout, and each page can have its local state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reducers&lt;/strong&gt;. A reducer is a pure function that compute the next state. It don't have side effects, it don't depend on global variables, and it don't mutate its inputs.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actions&lt;/strong&gt;. An action comes with a payload and tell the reducer how to transform the state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selectors&lt;/strong&gt;. A selector is an abstraction over the state. It allows to compute values from the state. It allows reusability, expressiveness, and decoupling from the state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downstream Callbacks&lt;/strong&gt;. It's the callback that listen for state updates, and update the layout in consequence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an overview of how this concepts are implemented.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/petitapetitio/dash-with-redux/blob/main/app/pages/city.py"&gt;app/pages/city.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;app.pages.city_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;register_page_city&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;register_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;city&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-layout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- the page&amp;#39;s state&lt;/span&gt;
                &lt;span class="o"&gt;...&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;population-input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;comment-input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualize-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- the page&amp;#39;s downstream callback&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;country&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-dropdown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;population&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;comment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submit-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualize-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;disabled&amp;quot;&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;&lt;a href="https://github.com/petitapetitio/dash-with-redux/blob/main/app/pages/city_state.py"&gt;app/pages/city_state.py&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- The page&amp;#39;s initial state&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;population&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&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="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- The page&amp;#39;s actions &lt;/span&gt;
    &lt;span class="n"&gt;SET_POPULATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- The page&amp;#39;s reducer&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_POPULATION&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;population&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Reducer for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; isn&amp;#39;t implemented.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The callback flow looks this way:
&lt;img alt="" src="_assets/dash-to-redux-unfolded.jpg"&gt;&lt;/p&gt;
&lt;p&gt;In common dash: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user actions triggers callbacks&lt;/li&gt;
&lt;li&gt;callbacks return values of what change in the DOM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the flux/redux pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user actions triggers callbacks&lt;/li&gt;
&lt;li&gt;callbacks computes the next state value by giving actions to a reducer&lt;/li&gt;
&lt;li&gt;the new state is stored in the DOM&lt;/li&gt;
&lt;li&gt;a downstream callback catch any state modification and update the DOM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The right-side of the flux/redux Dash unfolding highlights the downstream callback. &lt;/p&gt;
&lt;h1&gt;Analysing the benefits and the drawbacks&lt;/h1&gt;
&lt;p&gt;Let's start with the drawbacks.&lt;/p&gt;
&lt;p&gt;A first one is that you write more code. It's the price to pay to break things down. For tiny apps, it may be not worth it. But when your app gets rich, or you want to perform live validation on forms and provide early feedback on every move, it shows it's interest.&lt;/p&gt;
&lt;p&gt;Another drawback is that there is no selective rendering. When a part of the state is updated, all the outputs of the downstream callback are refreshed.&lt;/p&gt;
&lt;p&gt;Among the benefits : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The core value of this pattern rely on the action and the reducer&lt;/li&gt;
&lt;li&gt;It allows to concentrate the logic into the reducer and have thin callbacks&lt;/li&gt;
&lt;li&gt;It brings testability (the reducer is a pure function)&lt;/li&gt;
&lt;li&gt;Actions are composable (again, because the reducer is pure)&lt;/li&gt;
&lt;li&gt;It decouple the state from the layout. This provides a better separation of concerns, and thus improve readability/scalability.&lt;/li&gt;
&lt;li&gt;Actions emphasis the intent, the &lt;em&gt;what&lt;/em&gt; rather than &lt;em&gt;how&lt;/em&gt;. If a callback leads to several actions, you see clearly how they are composed (&lt;a href="https://github.com/petitapetitio/dash-with-redux/blob/47a9033dae007bb2cea0c69a0f7d15ecd1e81bb0/app/pages/city.py#L169C9-L172C26"&gt;example&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Functions names can focus on the event that triggers them. The intent is expresse by the action.&lt;/li&gt;
&lt;li&gt;It's the &lt;em&gt;functional core, imperative shell&lt;/em&gt; principle, applied to the front-end&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They are pros and cons. 
It's not a silver bullet. &lt;/p&gt;
&lt;p&gt;For a simple dash application, it won't be worth it. &lt;/p&gt;
&lt;p&gt;If you get to a point you find yourself struggling with the logic of the app, this approach will help you to write more expressive code and facilitate local reasoning.&lt;/p&gt;
&lt;p&gt;If you know from start you are building a rich app, you may want to start with another technology from the beginning. I'm very open to change my mind. Please prove me I'm wrong. But if find it lacks building custom components easily.&lt;/p&gt;
&lt;h1&gt;Edge-cases&lt;/h1&gt;
&lt;h2&gt;How to handle navigation&lt;/h2&gt;
&lt;p&gt;In common Dash, navigating to a new page can be done in several ways.&lt;/p&gt;
&lt;p&gt;By updating the &lt;code&gt;href&lt;/code&gt; attribute of a &lt;code&gt;dcc.Location&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Somewhere in the layout:&lt;/span&gt;
    &lt;span class="c1"&gt;# html.Button(id=&amp;quot;go-to-page2-btn&amp;quot;, children=&amp;quot;Go to page 2&amp;quot;)&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;go-to-page2-btn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_go_to_page2_clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&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_clicks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;page2&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By putting a &lt;code&gt;dcc.Location&lt;/code&gt; in the &lt;code&gt;children&lt;/code&gt; property of a DOM element&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Somewhere in the layout:&lt;/span&gt;
    &lt;span class="c1"&gt;# html.Button(id=&amp;quot;go-to-page2-btn&amp;quot;, children=&amp;quot;Go to page 2&amp;quot;),&lt;/span&gt;
    &lt;span class="c1"&gt;# html.Div(id=&amp;quot;dummy&amp;quot;)&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dummy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;children&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;go-to-page2-btn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_go_to_page2_clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&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_clicks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;redirect-location&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;page2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By setting the &lt;code&gt;href&lt;/code&gt; property of a dash bootstrap component's button:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;dbc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;go&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;page2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With flux/redux approach, navigation can be centralized in the state:&lt;/p&gt;
&lt;p&gt;It brings uniformity, and it works well. But you need to be careful of a few things.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/petitapetitio/dash-with-redux/blob/main/app/pages/main_layout.py"&gt;app/pages/main_layout.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dash&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;app.pages.main_state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;register_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- Put a Location in the main layout&lt;/span&gt;
            &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;main-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;session&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- create a session-scoped state&lt;/span&gt;
            &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;page-container&amp;quot;&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="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    You want to be able to access the app from a url. &lt;/span&gt;
&lt;span class="sd"&gt;    The trick here is to capture the location&amp;#39;s href when it changes. &lt;/span&gt;
&lt;span class="sd"&gt;    And only when it really changes.&lt;/span&gt;
&lt;span class="sd"&gt;    You don&amp;#39;t want to update the state when the callback is triggered with the value that is already in the state. &lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;main-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;main-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_page_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;href&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;href&amp;quot;&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    You control the location here, in the downstream callback&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;--  be careful, allow_duplicate=True is important&lt;/span&gt;
        &lt;span class="c1"&gt;# ... other outputs ...&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;main-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;href&amp;quot;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/petitapetitio/dash-with-redux/blob/main/app/pages/main_state.py"&gt;app/pages/main_state.py&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;urllib.parse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;app.city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;

&lt;span class="n"&gt;INITIAL_STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;base&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&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="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;SET_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;

        &lt;span class="c1"&gt;# resolve the url&lt;/span&gt;
        &lt;span class="c1"&gt;# /city -&amp;gt; http://127.0.0.1:8050/city&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;base&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;

        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&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;state&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;base&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scheme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;netloc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# ... other actions ...&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Reducer for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; isn&amp;#39;t implemented.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also, it's &lt;strong&gt;all or nothing&lt;/strong&gt;. If you start managing navigation through the state, every move should go through the state. You cannot combine it with using a &lt;code&gt;dbc.Button("go", href="page2")&lt;/code&gt; to navigate: the next state's update of the main layout will take you back to the previous page (ie the location that the state contains).&lt;/p&gt;
&lt;p&gt;As an experience, try to break the demo app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delete the callback &lt;code&gt;app/pages/main_layout.py::on_page_load&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start the app with &lt;code&gt;python run.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Visit &lt;code&gt;http://127.0.0.1:8050/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Add a city&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;See the tragic&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;How to debounce user inputs&lt;/h2&gt;
&lt;p&gt;Because Dash batch user inputs, you receive incomplete inputs when the user type fast and your server is slow:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNqdUk1vnDAQ_SvWnAnia2HxIYcklxyaS1Spqri4ZhasgE2NnYSs9r93gN3uKm0TqT7gj_dm3pth9iBNjcBhxJ8etcQ7JRor-kozWoOwTkk1CO3Y1xHtn6831rz8FbgTY_uI9nnGVnROcHV9fYzgzE0DsgpEBSt-BIhyjuVsRF1fss7YO6IfauGQjW7-OnOK-VD77ZT1Hzh-gttPcHd28HF17n_Ku8j-YOjVqqZ1zOzYZdy9dmh7rJWwE3sWnceRdepp6TyVHyw7zgd0MmTCksaTGgasWe0XpR_CyVbphgny2pG8llNYaQigsaoGvhPdiAH0JCPmO-xnT1R8iz0l5nTcGYsj2Q1WZNeZF9nSsMzofq2gAunJ8crvjTbOaPy2tuVQ6QPJ0Vx9N6YH7qwnQWt80_6WX_tznN4TRXhnHictT3dqgzP2yzrxy-BTGvoFaG-N1w54kieLDvA9vAKPkyKMk6wsijiOt3GeBTABz9MwjzZplmzzrCyjJM0OAbwtzqJwW0abTRFFRZxFZZomh1_Onxmx"&gt;&lt;img alt="" src="https://mermaid.ink/img/pako:eNqdUk1vnDAQ_SvWnAnia2HxIYcklxyaS1Spqri4ZhasgE2NnYSs9r93gN3uKm0TqT7gj_dm3pth9iBNjcBhxJ8etcQ7JRor-kozWoOwTkk1CO3Y1xHtn6831rz8FbgTY_uI9nnGVnROcHV9fYzgzE0DsgpEBSt-BIhyjuVsRF1fss7YO6IfauGQjW7-OnOK-VD77ZT1Hzh-gttPcHd28HF17n_Ku8j-YOjVqqZ1zOzYZdy9dmh7rJWwE3sWnceRdepp6TyVHyw7zgd0MmTCksaTGgasWe0XpR_CyVbphgny2pG8llNYaQigsaoGvhPdiAH0JCPmO-xnT1R8iz0l5nTcGYsj2Q1WZNeZF9nSsMzofq2gAunJ8crvjTbOaPy2tuVQ6QPJ0Vx9N6YH7qwnQWt80_6WX_tznN4TRXhnHictT3dqgzP2yzrxy-BTGvoFaG-N1w54kieLDvA9vAKPkyKMk6wsijiOt3GeBTABz9MwjzZplmzzrCyjJM0OAbwtzqJwW0abTRFFRZxFZZomh1_Onxmx?type=png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This has nothing to do with the flux/redux approach. But it's a problem you may encounter when working with Dash. It can be solved with debouncing: &lt;code&gt;dcc.Input(id="comment-input", debounce=0.5)&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning : &lt;a href="https://github.com/plotly/dash/blob/dev/CHANGELOG.md#2120---2023-08-14"&gt;dcc.Input accepts number for it's debounce argument since 2.12.0&lt;/a&gt;, but depending on the versions, it's expressed in secondes or in milliseconds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;How to integrate Dash Bootstrap Components&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Dash Bootstrap Components&lt;/em&gt; is a library of Bootstrap components for dash app. It offers components like a &lt;a href="https://www.dash-bootstrap-components.com/docs/components/toast/"&gt;toasts&lt;/a&gt;, &lt;a href="https://www.dash-bootstrap-components.com/docs/components/modal/"&gt;modals&lt;/a&gt; and &lt;a href="https://www.dash-bootstrap-components.com/docs/components/"&gt;many more&lt;/a&gt;, that are easy to integrate with dash apps.&lt;/p&gt;
&lt;p&gt;Components like modal and toast have a built-in close mechanism. The trick is that if you control opening from the state, don't forget to catch the "on_close" to sync the state. Else, it will open again on the next state update :).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If your modal's header set the &lt;code&gt;close_button&lt;/code&gt; property to &lt;code&gt;True&lt;/code&gt;, and catch when your modal &lt;code&gt;is_open&lt;/code&gt; property becomes &lt;code&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If your toast set the &lt;code&gt;dismissable&lt;/code&gt; property and displays a close button, catch &lt;code&gt;n_dismiss&lt;/code&gt; updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to handle time-consuming updates&lt;/h2&gt;
&lt;p&gt;A major interest of dash is to create graphs. &lt;/p&gt;
&lt;p&gt;Let's say you have a button &lt;code&gt;visualize&lt;/code&gt;.
When you click, it fetch data from an api.
And you want to plot the data.&lt;/p&gt;
&lt;p&gt;We have 2 options: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;store the graph data in the page state, and generate the graph in the downstream callback.&lt;/li&gt;
&lt;li&gt;generate the graph in the DOM callback and return the figure without using the state (like in a common dash app)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The state can only hold serializable information. 
You cannot put the figure object in the state. &lt;/p&gt;
&lt;p&gt;You can put the graph data in the state, and create the graph in the downstream callback. But this will recreate the graph on each state update. Creating a plot with 1M points takes 1.5 seconds on my computer. Recreating the graph on each interaction will slow the application. The graph will disappear and reappear — even when unrelated information changes. This is confusing for the user.&lt;/p&gt;
&lt;p&gt;My recommendation is to not put the graph generation in the downstream callback.&lt;/p&gt;
&lt;p&gt;Instead of:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="c1"&gt;# The DOM callback&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualize-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_visualize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&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_clicks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_fake_air_quality_over_time_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_VISUALISATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ys&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# The downstream callback&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&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="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;comment-input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualization-div&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;children&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city:on_update_state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&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="n"&gt;_generate_visualization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualization&amp;quot;&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;Prefer the common dash approach:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualization-div&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;children&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualize-button&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n_clicks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_click_visualize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_clicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&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_clicks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_fake_air_quality_over_time_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scatter&lt;/span&gt;&lt;span class="p"&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;xs&lt;/span&gt;&lt;span class="p"&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;ys&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_xaxes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_yaxes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Air quality&amp;quot;&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;dcc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The lesson here is that you don't need to put everything in the state. The more consistent your architecture is, the better. But you can combine the flux/redux approach with the common dash approach. In some special cases like this one, it's better to avoid the shared state.&lt;/p&gt;
&lt;h2&gt;How to execute actions when the page opens&lt;/h2&gt;
&lt;p&gt;One way is to implement a &lt;strong&gt;layout factory function&lt;/strong&gt; (instead of a &lt;strong&gt;layout value&lt;/strong&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_layout_factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Perform initialization based on city&amp;#39;s id value&lt;/span&gt;
    &lt;span class="c1"&gt;# if visiting http://127.0.0.1:8050/city      then id is None&lt;/span&gt;
    &lt;span class="c1"&gt;# if visiting http://127.0.0.1:8050/city?id=0 then id is 0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&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="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;register_page_city&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;register_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;city&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_layout_factory&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;The limit of this approach is that &lt;u&gt;you cannot access the state of the main layout&lt;/u&gt;. And you cannot update it as an output.&lt;/p&gt;
&lt;p&gt;A simple way to execute actions when the page opens is to listen a
 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event"&gt;DOMContentLoaded&lt;/a&gt;-like event. Give an id to your page's layout, and listen its &lt;code&gt;children&lt;/code&gt; property:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;register_page_city&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;register_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;city&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-layout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- give an id to the page&amp;#39;s layout&lt;/span&gt;
            &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Add a city&amp;quot;&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="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-layout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;children&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- listen to the page&amp;#39;s layout `children` property update&lt;/span&gt;
        &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;# prevent_initial_call=True, &amp;lt;-- don&amp;#39;t prevent initial call&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_page_load&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="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;city_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id=&amp;quot;&lt;/span&gt;&lt;span class="p"&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;if&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="c1"&gt;# perform any page initialization task based on the city_id&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another approach consist in listening to the location's component &lt;code&gt;href&lt;/code&gt; updates. This one involves parsing the URL to check if it match the URL of the current page. This is more error-prone. You need a guard so you won't initialize page A when you load page B. But it works too:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;city-state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;prevent_initial_call&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on_page_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;main_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&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;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITY&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;dash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_update&lt;/span&gt;

        &lt;span class="n"&gt;city_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id=&amp;quot;&lt;/span&gt;&lt;span class="p"&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;if&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="c1"&gt;# perform any page initialization task&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content></entry><entry><title>Vinyles (2013-2017)</title><link href="https://me.petitapetit.io/vinyles" rel="alternate"/><published>2025-05-17T00:00:00+02:00</published><updated>2025-05-17T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-17:/vinyles</id><summary type="html">&lt;p&gt;Here is the music I collected between 2013 and 2017. &lt;/p&gt;
&lt;p&gt;Some I bought in shops.&lt;br&gt;
Some are second hand from flea markets.&lt;br&gt;
Some are from my parents.&lt;br&gt;
Some I took to my friends — and I don't know why I have them.&lt;br&gt;
Some aren’t here — I guess my friends kept …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here is the music I collected between 2013 and 2017. &lt;/p&gt;
&lt;p&gt;Some I bought in shops.&lt;br&gt;
Some are second hand from flea markets.&lt;br&gt;
Some are from my parents.&lt;br&gt;
Some I took to my friends — and I don't know why I have them.&lt;br&gt;
Some aren’t here — I guess my friends kept them by mistake.&lt;/p&gt;
&lt;p&gt;Some are deep.&lt;br&gt;
some are cheesy.&lt;br&gt;
Some are sharp, avant-garde.&lt;br&gt;
And some are commercial soup.&lt;/p&gt;
&lt;p&gt;I loved them all.&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2137.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2137.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2138.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2138.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2139.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2139.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2140.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2140.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2141.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2141.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2142.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2142.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2143.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2143.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2144.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2144.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2145.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2145.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2146.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2146.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2147.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2147.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2148.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2148.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2149.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2149.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2150.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2150.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2151.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2151.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2152.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2152.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2153.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2153.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2154.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2154.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2155.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2155.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2156.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2156.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2157.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2157.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2158.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2158.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2159.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2159.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2160.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2160.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2161.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2161.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2162.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2162.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2163.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2163.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2164.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2164.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2165.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2165.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2166.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2166.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2167.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2167.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2168.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2168.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2169.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2169.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2170.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2170.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2171.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2171.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2172.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2172.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2173.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2173.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2174.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2174.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2175.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2175.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2176.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2176.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2177.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2177.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2178.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2178.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2179.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2179.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2180.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2180.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2181.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2181.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2182.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2182.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2183.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2183.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2184.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2184.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2185.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2185.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2186.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2186.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2187.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2187.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2188.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2188.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2189.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2189.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2190.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2190.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2191.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2191.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2193.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2193.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2194.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2194.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2195.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2195.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2196.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2196.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2197.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2197.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2198.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2198.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2199.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2199.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2200.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2200.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2201.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2201.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2202.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2202.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2203.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2203.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2204.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2204.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2205.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2205.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2206.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2206.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2207.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2207.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2208.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2208.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2209.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2209.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2210.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2210.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2211.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2211.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2212.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2212.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2213.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2213.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2214.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2214.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2215.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2215.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2216.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2216.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2217.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2217.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2218.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2218.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2219.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2219.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2220.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2220.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2221.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2221.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2222.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2222.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2223.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2223.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2224.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2224.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2225.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2225.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2226.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2226.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2227.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2227.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2228.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2228.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2229.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2229.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2231.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2231.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2232.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2232.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2233.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2233.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2234.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2234.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2235.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2235.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2236.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2236.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2237.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2237.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2238.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2238.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2239.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2239.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2240.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2240.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2241.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2241.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2242.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2242.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2243.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2243.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2244.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2244.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2245.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2245.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2246.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2246.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2247.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2247.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2248.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2248.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2249.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2249.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2250.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2250.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2251.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2251.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2252.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2252.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2253.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2253.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2254.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2254.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2255.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2255.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2256.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2256.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2257.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2257.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2258.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2258.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2259.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2259.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2260.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2260.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2261.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2261.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2262.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2262.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2263.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2263.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2264.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2264.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2265.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2265.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2266.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2266.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2267.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2267.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2268.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2268.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2269.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2269.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2270.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2270.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2271.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2271.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2272.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2272.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2273.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2273.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2274.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2274.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2275.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2275.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2276.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2276.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2277.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2277.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2278.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2278.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2279.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2279.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2280.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2280.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2281.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2281.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2282.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2282.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2283.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2283.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2320.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2320.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2321.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2321.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2322.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2322.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2323.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2323.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2324.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2324.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2325.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2325.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2326.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2326.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2327.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2327.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2328.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2328.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2329.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2329.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2330.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2330.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2331.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2331.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2332.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2332.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2333.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2333.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2334.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2334.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2335.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2335.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2336.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2336.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2337.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2337.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2338.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2338.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2339.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2339.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2340.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2340.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2341.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2341.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2342.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2342.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2343.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2343.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2344.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2344.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2345.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2345.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2346.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2346.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2347.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2347.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2348.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2348.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2349.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2349.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2350.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2350.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2351.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2351.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2352.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2352.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2353.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2353.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2354.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2354.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2355.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2355.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2356.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2356.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2357.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2357.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2358.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2358.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2359.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2359.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2360.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2360.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2361.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2361.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2362.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2362.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2363.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2363.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2364.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2364.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2365.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2365.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2366.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2366.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2367.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2367.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2368.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2368.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2369.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2369.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2370.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2370.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2371.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2371.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2372.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2372.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2373.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2373.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2374.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2374.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2375.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2375.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2376.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2376.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2377.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2377.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2378.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2378.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2379.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2379.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2380.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2380.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2381.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2381.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2382.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2382.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2383.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2383.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2385.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2385.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2386.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2386.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2387.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2387.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2388.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2388.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2389.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2389.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2390.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2390.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2391.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2391.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2392.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2392.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2393.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2393.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2394.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2394.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2395.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2395.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2396.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2396.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2399.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2399.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2400.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2400.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2401.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2401.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2402.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2402.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2403.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2403.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2404.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2404.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2405.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2405.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2406.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2406.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2407.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2407.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2408.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2408.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2409.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2409.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2410.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2410.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2411.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2411.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2412.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2412.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2413.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2413.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2414.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2414.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2415.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2415.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2416.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2416.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2417.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2417.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2418.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2418.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2419.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2419.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2420.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2420.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2421.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2421.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2422.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2422.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2423.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2423.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2424.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2424.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2425.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2425.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2426.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2426.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2427.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2427.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2428.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2428.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2429.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2429.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2430.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2430.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2431.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2431.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2432.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2432.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2433.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2433.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2434.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2434.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2435.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2435.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2436.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2436.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2437.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2437.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2438.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2438.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2439.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2439.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2440.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2440.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2441.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2441.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2442.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2442.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2443.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2443.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2444.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2444.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2445.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2445.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2446.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2446.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2448.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2448.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2449.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2449.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2450.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2450.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2451.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2451.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2452.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2452.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2453.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2453.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2454.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2454.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2455.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2455.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2456.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2456.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2457.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2457.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2458.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2458.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2459.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2459.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2460.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2460.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2461.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2461.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2462.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2462.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2463.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2463.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2464.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2464.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2465.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2465.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2466.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2466.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2467.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2467.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2468.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2468.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2469.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2469.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2470.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2470.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2471.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2471.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2472.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2472.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2473.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2473.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2474.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2474.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2475.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2475.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2476.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2476.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2477.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2477.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2478.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2478.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2479.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2479.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2480.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2480.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2481.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2481.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2482.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2482.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2483.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2483.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2484.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2484.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2485.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2485.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2486.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2486.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2487.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2487.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2488.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2488.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2489.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2489.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2490.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2490.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2491.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2491.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2492.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2492.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2493.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2493.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2494.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2494.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2495.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2495.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2496.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2496.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2497.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2497.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2498.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2498.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2499.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2499.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2500.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2500.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2501.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2501.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2502.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2502.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2503.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2503.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2504.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2504.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2505.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2505.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2506.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2506.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2507.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2507.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2508.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2508.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2509.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2509.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2510.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2510.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2511.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2511.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2512.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2512.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2513.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2513.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2514.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2514.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2515.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2515.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2516.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2516.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2517.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2517.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2518.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2518.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2519.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2519.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2520.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2520.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2521.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2521.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2522.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2522.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2523.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2523.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2524.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2524.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2525.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2525.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2526.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2526.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2527.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2527.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2528.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2528.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2529.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2529.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2530.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2530.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2531.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2531.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2532.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2532.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2533.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2533.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2534.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2534.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2535.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2535.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2536.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2536.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2537.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2537.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2538.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2538.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2539.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2539.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2540.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2540.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2541.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2541.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2542.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2542.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2543.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2543.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2544.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2544.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2545.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2545.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2546.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2546.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2547.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2547.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2548.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2548.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2549.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2549.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2550.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2550.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2551.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2551.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2552.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2552.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2553.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2553.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2554.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2554.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2555.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2555.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2556.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2556.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2557.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2557.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2558.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2558.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2559.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2559.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2560.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2560.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2561.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2561.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2562.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2562.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2563.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2563.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2564.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2564.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2565.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2565.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2566.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2566.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2567.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2567.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2568.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2568.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2569.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2569.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2570.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2570.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2571.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2571.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2572.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2572.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2573.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2573.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2574.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2574.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2575.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2575.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2576.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2576.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2577.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2577.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2578.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2578.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2579.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2579.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2580.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2580.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2581.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2581.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2595.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2595.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2596.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2596.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2597.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2597.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2598.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2598.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2599.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2599.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2600.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2600.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2601.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2601.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2602.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2602.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2603.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2603.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2604.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2604.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2605.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2605.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2606.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2606.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2607.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2607.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2608.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2608.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2609.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2609.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2610.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2610.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2611.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2611.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2612.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2612.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2613.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2613.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2614.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2614.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2615.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2615.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2616.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2616.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2617.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2617.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2618.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2618.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2619.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2619.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2620.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2620.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2621.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2621.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2622.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2622.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2623.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2623.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2624.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2624.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2625.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2625.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2626.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2626.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2627.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2627.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2628.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2628.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2629.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2629.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2630.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2630.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2631.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2631.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2632.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2632.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2633.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2633.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2634.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2634.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2635.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2635.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2636.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2636.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2637.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2637.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2638.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2638.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2639.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2639.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2640.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2640.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2641.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2641.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2642.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2642.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2643.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2643.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2644.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2644.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2645.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2645.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2646.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2646.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2647.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2647.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2648.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2648.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2649.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2649.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2650.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2650.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2651.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2651.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2652.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2652.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2653.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2653.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2654.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2654.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2655.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2655.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2656.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2656.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2657.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2657.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2658.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2658.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2659.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2659.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2660.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2660.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2661.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2661.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2662.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2662.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2663.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2663.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2664.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2664.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2665.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2665.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2666.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2666.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2667.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2667.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2668.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2668.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2669.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2669.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2670.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2670.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2671.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2671.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2672.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2672.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2673.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2673.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2674.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2674.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2675.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2675.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2676.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2676.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2677.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2677.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2678.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2678.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2679.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2679.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2680.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2680.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2681.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2681.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2682.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2682.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2683.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2683.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2684.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2684.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2685.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2685.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2686.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2686.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2687.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2687.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2688.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2688.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2689.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2689.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2690.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2690.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2691.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2691.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2692.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2692.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2693.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2693.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2694.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2694.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2695.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2695.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2696.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2696.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2697.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2697.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2698.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2698.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2699.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2699.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2700.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2700.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2701.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2701.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2702.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2702.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2703.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2703.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2704.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2704.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2705.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2705.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2706.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2706.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2707.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2707.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2708.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2708.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2709.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2709.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2710.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2710.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2711.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2711.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2712.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2712.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2713.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2713.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2714.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2714.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2715.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2715.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2716.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2716.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2717.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2717.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2718.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2718.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2720.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2720.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2721.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2721.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2722.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2722.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2723.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2723.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2724.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2724.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2725.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2725.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2726.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2726.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2727.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2727.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2728.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2728.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2729.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2729.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2730.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2730.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2731.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2731.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2732.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2732.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2733.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2733.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2734.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2734.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2735.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2735.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2736.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2736.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2737.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2737.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2738.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2738.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2739.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2739.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2740.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2740.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2741.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2741.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2742.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2742.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2743.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2743.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2744.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2744.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2745.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2745.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2746.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2746.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2747.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2747.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2748.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2748.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2749.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2749.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2750.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2750.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2751.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2751.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2752.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2752.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2753.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2753.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2754.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2754.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2755.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2755.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2756.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2756.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2757.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2757.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2758.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2758.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2759.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2759.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2760.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2760.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2761.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2761.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2762.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2762.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2763.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2763.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2764.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2764.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2765.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2765.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2766.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2766.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2767.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2767.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2768.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2768.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2769.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2769.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2770.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2770.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2771.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2771.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2772.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2772.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2773.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2773.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2774.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2774.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2775.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2775.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2776.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2776.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2777.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2777.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2778.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2778.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2779.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2779.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2780.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2780.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2781.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2781.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2782.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2782.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2783.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2783.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2784.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2784.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2785.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2785.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2786.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2786.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2787.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2787.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2788.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2788.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2789.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2789.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2790.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2790.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2791.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2791.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2792.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2792.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2793.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2793.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2794.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2794.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2795.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2795.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2796.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2796.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2797.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2797.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2798.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2798.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2799.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2799.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2800.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2800.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2801.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2801.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2802.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2802.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2803.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2803.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2804.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2804.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2805.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2805.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2806.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2806.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2807.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2807.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2808.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2808.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2809.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2809.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2810.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2810.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2811.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2811.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2812.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2812.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2813.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2813.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2814.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2814.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2815.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2815.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2816.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2816.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2817.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2817.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2818.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2818.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2819.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2819.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2820.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2820.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2821.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2821.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2822.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2822.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2823.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2823.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2824.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2824.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2825.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2825.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2826.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2826.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2827.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2827.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2828.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2828.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2829.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2829.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2830.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2830.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2831.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2831.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2832.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2832.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2833.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2833.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2834.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2834.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2835.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2835.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2836.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2836.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2837.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2837.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2838.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2838.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2839.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2839.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2840.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2840.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2841.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2841.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2842.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2842.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2843.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2843.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2844.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2844.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2845.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2845.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2846.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2846.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2847.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2847.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2848.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2848.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2849.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2849.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2850.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2850.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2851.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2851.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2852.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2852.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2853.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2853.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2854.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2854.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2855.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2855.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2856.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2856.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2857.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2857.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2858.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2858.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2859.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2859.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2860.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2860.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2861.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2861.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2862.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2862.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2863.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2863.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2864.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2864.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2865.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2865.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2866.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2866.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2867.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2867.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2868.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2868.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2869.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2869.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2870.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2870.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2871.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2871.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2872.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2872.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2873.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2873.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2874.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2874.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2875.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2875.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2876.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2876.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2877.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2877.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2878.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2878.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2879.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2879.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2880.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2880.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2881.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2881.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2882.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2882.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2883.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2883.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2884.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2884.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2885.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2885.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2886.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2886.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2887.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2887.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2888.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2888.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2889.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2889.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2890.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2890.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2891.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2891.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2892.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2892.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2893.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2893.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2894.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2894.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2895.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2895.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2896.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2896.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2897.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2897.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2898.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2898.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2899.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2899.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2900.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2900.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2901.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2901.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2902.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2902.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2903.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2903.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2904.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2904.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2905.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2905.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2906.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2906.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2907.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2907.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2908.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2908.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2909.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2909.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2910.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2910.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2911.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2911.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2912.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2912.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2913.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2913.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2914.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2914.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2915.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2915.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2916.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2916.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2917.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2917.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2918.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2918.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2919.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2919.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2920.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2920.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2921.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2921.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2922.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2922.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2923.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2923.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2924.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2924.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2925.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2925.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2926.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2926.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2927.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2927.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2928.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2928.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2929.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2929.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2930.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2930.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2931.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2931.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2932.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2932.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2933.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2933.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2934.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2934.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2935.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2935.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2936.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2936.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2937.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2937.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2938.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2938.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2939.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2939.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2940.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2940.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2941.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2941.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2942.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2942.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2943.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2943.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2944.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2944.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2945.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2945.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2946.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2946.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2947.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2947.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2948.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2948.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2949.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2949.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2950.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2950.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2951.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2951.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2952.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2952.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2953.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2953.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2954.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2954.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2955.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2955.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2956.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2956.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2957.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2957.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2958.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2958.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2959.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2959.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2960.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2960.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2961.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2961.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2962.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2962.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2963.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2963.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2964.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2964.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2965.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2965.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2966.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2966.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2967.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2967.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2968.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2968.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2969.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2969.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2970.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2970.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2971.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2971.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2972.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2972.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2973.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2973.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2974.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2974.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2976.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2976.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2977.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2977.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2978.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2978.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2979.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2979.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2980.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2980.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2981.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2981.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2982.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2982.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2983.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2983.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2984.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2984.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2985.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2985.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2986.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2986.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2987.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2987.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2988.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2988.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2989.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2989.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2990.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2990.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2991.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2991.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2992.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2992.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2993.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2993.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2994.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2994.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2995.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2995.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2996.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2996.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2997.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2997.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2998.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2998.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_2999.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_2999.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3001.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3001.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3002.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3002.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3003.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3003.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3004.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3004.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3005.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3005.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3006.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3006.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3007.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3007.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3008.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3008.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3009.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3009.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3010.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3010.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3011.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3011.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3012.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3012.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3013.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3013.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3014.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3014.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3015.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3015.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3016.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3016.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3017.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3017.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3018.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3018.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3019.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3019.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3020.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3020.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3021.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3021.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3022.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3022.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3023.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3023.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3024.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3024.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3025.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3025.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3026.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3026.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3027.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3027.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3028.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3028.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3029.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3029.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3030.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3030.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3031.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3031.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3032.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3032.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3033.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3033.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3034.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3034.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3035.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3035.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3036.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3036.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3037.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3037.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3038.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3038.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3039.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3039.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3040.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3040.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3041.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3041.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3042.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3042.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3043.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3043.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3044.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3044.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3045.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3045.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3046.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3046.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3047.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3047.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3048.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3048.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3049.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3049.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3050.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3050.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3051.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3051.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3052.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3052.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3053.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3053.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3054.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3054.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3055.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3055.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3056.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3056.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3057.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3057.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3058.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3058.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3059.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3059.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3060.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3060.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3061.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3061.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3062.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3062.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3081.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3081.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3082.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3082.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3083.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3083.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3084.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3084.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3085.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3085.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3086.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3086.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3087.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3087.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3088.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3088.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3089.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3089.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3090.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3090.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3091.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3091.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3092.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3092.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3093.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3093.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3094.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3094.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3095.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3095.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3096.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3096.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3097.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3097.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3098.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3098.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3099.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3099.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3100.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3100.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3101.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3101.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3102.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3102.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3103.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3103.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3104.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3104.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3105.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3105.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3106.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3106.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3107.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3107.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3108.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3108.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3109.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3109.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3110.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3110.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3111.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3111.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3112.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3112.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3113.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3113.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3114.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3114.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3115.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3115.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3116.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3116.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3117.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3117.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3118.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3118.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3119.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3119.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3120.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3120.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3121.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3121.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3122.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3122.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3123.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3123.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3124.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3124.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3125.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3125.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3126.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3126.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3127.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3127.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3378.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3378.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3379.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3379.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3380.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3380.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3381.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3381.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3382.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3382.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3383.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3383.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3384.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3384.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3385.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3385.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3386.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3386.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3387.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3387.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3388.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3388.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3389.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3389.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3390.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3390.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3391.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3391.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3392.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3392.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3393.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3393.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3394.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3394.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3395.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3395.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3396.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3396.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3397.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3397.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3398.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3398.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3399.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3399.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3400.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3400.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3401.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3401.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3402.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3402.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3439.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3439.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3440.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3440.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3441.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3441.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3442.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3442.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3443.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3443.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/vinyles/IMG_3444.JPG'&gt;&lt;img src='_assets/vinyles_mini/IMG_3444.JPG'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content></entry><entry><title>My dream home</title><link href="https://me.petitapetit.io/dh" rel="alternate"/><published>2025-05-12T00:00:00+02:00</published><updated>2025-05-12T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-12:/dh</id><summary type="html">&lt;p&gt;My dream home took many forms over the years.&lt;/p&gt;
&lt;p&gt;I see habitat as a creative space. 
When I was a child, I loved building cabins. 
Any furniture were finding a function in the elevation of my house. 
Desk served as a foundation. 
Books helped to hold a quilt and make …&lt;/p&gt;</summary><content type="html">&lt;p&gt;My dream home took many forms over the years.&lt;/p&gt;
&lt;p&gt;I see habitat as a creative space. 
When I was a child, I loved building cabins. 
Any furniture were finding a function in the elevation of my house. 
Desk served as a foundation. 
Books helped to hold a quilt and make a roof. &lt;/p&gt;
&lt;p&gt;I've long dreamt of having a big house, like a mansion. 
I like the mystery it conveys. 
I like that you can explore it. 
I like how beautiful it is.&lt;/p&gt;
&lt;p&gt;I've dreamt of a house at the mountain: 
disconnected from civilisation, 
with large glasses opening on the forest, 
a fireplace for the winter 
and a hiking paths to escape during the summer. 
A place when I could meditate for hours, 
listen to birds, 
find silence. &lt;/p&gt;
&lt;p&gt;At the age of 31, eager to own a house, I bought an appartement. 
It had nothing I dreamt of. 
It was on the ground floor. 
l could hear people talking on the street — and they could see me. 
It was dark. 
I could touch the ceil just by raising my hand. 
Rooms where all under 16 square meters and organised in series. &lt;/p&gt;
&lt;p&gt;I lost a lot of money in this venture.
It cost me some nights and a romantic relationship. 
But it cured my vanity.&lt;/p&gt;
&lt;p&gt;Today, my dream home has the following attributes : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a personal room with enough space for a desk&lt;/li&gt;
&lt;li&gt;an internet connection so I can work with the rest of the world&lt;/li&gt;
&lt;li&gt;a bathroom with a shower and water closet&lt;/li&gt;
&lt;li&gt;two curved plates, two forks, two knifes, and two spoons&lt;/li&gt;
&lt;li&gt;a bed&lt;/li&gt;
&lt;li&gt;the absence of anything I don't need&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don't want to buy a house. &lt;/p&gt;
&lt;p&gt;I would be happy to rent it, and pay for the flexibility.&lt;/p&gt;
&lt;p&gt;This way I can move anywhere else whenever I want. &lt;/p&gt;
&lt;p&gt;And I'm happy to go back at my parents house when I return.&lt;/p&gt;</content></entry><entry><title>I'm happy I didn't make a PhD</title><link href="https://me.petitapetit.io/phd" rel="alternate"/><published>2025-05-12T00:00:00+02:00</published><updated>2025-05-12T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-05-12:/phd</id><summary type="html">&lt;p&gt;I'm happy I didn't make a PhD.&lt;/p&gt;
&lt;p&gt;Not because it’s a bad thing.&lt;br&gt;
But I would have done it at the wrong time,&lt;br&gt;
And for the wrong reasons.&lt;/p&gt;
&lt;p&gt;I would have done it to avoid the open world.&lt;br&gt;
To remain on a predictable track.&lt;br&gt;
Because I didn’t know …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm happy I didn't make a PhD.&lt;/p&gt;
&lt;p&gt;Not because it’s a bad thing.&lt;br&gt;
But I would have done it at the wrong time,&lt;br&gt;
And for the wrong reasons.&lt;/p&gt;
&lt;p&gt;I would have done it to avoid the open world.&lt;br&gt;
To remain on a predictable track.&lt;br&gt;
Because I didn’t know what to do.&lt;br&gt;
I didn’t see options. &lt;/p&gt;
&lt;p&gt;Also, I would have done it at the wrong time.&lt;br&gt;
I went back to school at 23.&lt;br&gt;
I was 28 when I got my master's degree.&lt;br&gt;
It was time to jump in.&lt;/p&gt;
&lt;p&gt;I was apprehensive. &lt;/p&gt;
&lt;p&gt;But anyway. &lt;br&gt;
I jumped into life. &lt;br&gt;
I realised I was lost. &lt;br&gt;
Ang I got into troubles.&lt;/p&gt;
&lt;p&gt;A shock happened. &lt;br&gt;
But I found inspiration on my way.&lt;/p&gt;
&lt;p&gt;I did introspection. &lt;br&gt;
I tried things. &lt;br&gt;
And I know me better. &lt;/p&gt;
&lt;p&gt;Now, &lt;br&gt;
I’m having fun. &lt;br&gt;
I move forward. &lt;br&gt;
And I’m excited about the future.&lt;/p&gt;</content></entry><entry><title>Apprendre à travers des interviews</title><link href="https://me.petitapetit.io/itw" rel="alternate"/><published>2025-04-02T00:00:00+02:00</published><updated>2025-04-02T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-04-02:/itw</id><summary type="html">&lt;p&gt;Apprendre est une de mes activités favorites.&lt;/p&gt;
&lt;p&gt;Plusieurs de mes derniers projets expérimentent autour de ce thème : &lt;a href="chess"&gt;Passer de zéro à 1000 aux échecs&lt;/a&gt;, &lt;a href="second-cerveau"&gt;Explorer le monde avec un second cerveau&lt;/a&gt;, &lt;a href="mp"&gt;Mémoriser python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je n'ai pas trouvé de méthode ultime. Mais chaque test me permet de progresser. Et voici ma …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Apprendre est une de mes activités favorites.&lt;/p&gt;
&lt;p&gt;Plusieurs de mes derniers projets expérimentent autour de ce thème : &lt;a href="chess"&gt;Passer de zéro à 1000 aux échecs&lt;/a&gt;, &lt;a href="second-cerveau"&gt;Explorer le monde avec un second cerveau&lt;/a&gt;, &lt;a href="mp"&gt;Mémoriser python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je n'ai pas trouvé de méthode ultime. Mais chaque test me permet de progresser. Et voici ma méthode actuelle. &lt;/p&gt;
&lt;p&gt;Mon objectif est de maîtriser le refactoring et le code legacy. Mon processus est structuré autour d'interviews. Et ma feuille de route est une cartographie des problèmes liés au legacy (réalisée sous la forme d'une mindmap).&lt;/p&gt;
&lt;p&gt;Chaque interview se déroule en 3 temps.&lt;/p&gt;
&lt;p&gt;Avant l'interview :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;identifier une personne intéressante sur des problèmes&lt;/li&gt;
&lt;li&gt;construire sa biographie (sous forme de liste à puces)&lt;/li&gt;
&lt;li&gt;recenser ses travaux (livres, podcasts, articles, conférences, posts, tout)&lt;/li&gt;
&lt;li&gt;lire un maximum de ce qu'elle a écrit, écouter ses interviews&lt;/li&gt;
&lt;li&gt;prendre des notes&lt;/li&gt;
&lt;li&gt;noter les thèmes sur lesquels elle parle&lt;/li&gt;
&lt;li&gt;coder des preuves de concepts&lt;/li&gt;
&lt;li&gt;écrire des billets de blog sur mes expérimentations&lt;/li&gt;
&lt;li&gt;noter un maximum de questions que j'aimerais lui poser&lt;/li&gt;
&lt;li&gt;la contacter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pendant l'interview : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;appuyer sur enregistrer&lt;/li&gt;
&lt;li&gt;poser mes questions&lt;/li&gt;
&lt;li&gt;écouter&lt;/li&gt;
&lt;li&gt;relancer pour avoir des réponses concrètes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Après l'interview : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;réécouter l'épisode&lt;/li&gt;
&lt;li&gt;prendre des notes &lt;/li&gt;
&lt;li&gt;éditer la transcription&lt;/li&gt;
&lt;li&gt;écrire un article pour chaque question à laquelle je n'ai pas sur répondre&lt;/li&gt;
&lt;li&gt;diffuser l'épisode&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'ai tendance à me couper socialement et à rester dans la théorie.&lt;/p&gt;
&lt;p&gt;Ce que j'aime dans ce processus, c'est qu'il combine trois axes importants pour moi : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de la théorie (consommer ses travaux)&lt;/li&gt;
&lt;li&gt;de la pratique (coder des preuves de concept, écrire)&lt;/li&gt;
&lt;li&gt;du social (aller vers des gens, m'intéresser aux autres)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'essaie de mettre en pratique un maximum pour qu'on puisse en parler lors de l'interview. Je me sens en joie à la fin de chaque interview. Lorsque je réécoute les épisodes, je suis affligé par ma façon de parler. Mais en même temps, je suis excité. Car je prends conscience de ce qui ne va pas, et je sais que je vais m'améliorer.&lt;/p&gt;
&lt;p&gt;Les épisodes sont disponibles à l'adresse &lt;a href="stb"&gt;petitapetit.io/stb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et toi, quel est ton processus d'apprentissage en ce moment ?&lt;/p&gt;</content></entry><entry><title>Pourquoi Derek Sivers me fascine</title><link href="https://me.petitapetit.io/ds" rel="alternate"/><published>2025-04-01T00:00:00+02:00</published><updated>2025-04-01T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-04-01:/ds</id><summary type="html">&lt;p&gt;Derek Sivers est né en 1969 à Berkeley, en Californie.&lt;/p&gt;
&lt;p&gt;Il a été musicien. Il a commencé avec un gig que personne ne voulait, pour un pig show. L'agent a reçu de bons retours sur sa prestation et l'a présenté à un cirque. Il a rejoint la troupe comme meneur …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Derek Sivers est né en 1969 à Berkeley, en Californie.&lt;/p&gt;
&lt;p&gt;Il a été musicien. Il a commencé avec un gig que personne ne voulait, pour un pig show. L'agent a reçu de bons retours sur sa prestation et l'a présenté à un cirque. Il a rejoint la troupe comme meneur/musicien-maître-de-cérémonie pendant 10 ans. Il a réalisé plus de 1000 shows. Il a appris à être ingénieur du son pour enregistrer son album. Et il a ouvert son studio d'enregistrement.&lt;/p&gt;
&lt;p&gt;Il a été entrepreneur. En 1997, il cherchait à distribuer son album sur internet. Il a fait plusieurs mois de démarches administratives pour être en droit d'accepter des paiements sur internet. Un ami musicien lui a demandé s'il pouvait s'occuper de vendre son album à lui aussi. CD Baby était lancé. Presque par accident. Il a donné ses instruments et a arrêté la musique pour concentrer son énergie sur son entreprise. Il a appris à coder pour développer le service. 10 ans plus tard, il a revendu l'entreprise 22 millions.&lt;/p&gt;
&lt;p&gt;Il a été conférencier. Il a donné 3 conférences TED en une année. Il est devenu auteur. Il a publié 5 livres entre 2020 et 2024, dont "How to Live", un livre qui explore 27 façons orthogonales d'approcher la vie.&lt;/p&gt;
&lt;p&gt;Il blog. Il héberge son site sur son propre serveur. Il a développé sa propre plateforme pour vendre ses livres. Il répond à tous les e-mails qu'il reçoit. Il partage &lt;a href="https://sive.rs/book"&gt;ses notes de lecture&lt;/a&gt; depuis 2008.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C'est un enthousiaste&lt;/strong&gt;. Il se concentre sur un projet à la fois. Il sait ce qu'il veut plus que tout et renonce au reste. Il cherche à réduire ses possessions plutôt que courir après plus. Il donne tout à son sujet.&lt;/p&gt;
&lt;p&gt;Sa &lt;a href="https://sive.rs/about"&gt;page about&lt;/a&gt; me montre une façon simple et sincère d'aborder la vie. &lt;/p&gt;
&lt;p&gt;Derek est la personne avec qui j'ai passé le plus de temps cette année. J'ai lu ces 5 livres. Je lis son blog. J'ai écouté 21 podcasts où il a été invité. Et j'ai 364 notes associées à lui dans mon second cerveau. &lt;/p&gt;
&lt;p&gt;Voici quelques directives que m'ont inspirées ses publications : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Formule tes notes sous forme de directive&lt;/strong&gt; — the point is to take action&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reformule de façon concise et dis-le avec tes mots&lt;/strong&gt; — don't quote&lt;/li&gt;
&lt;li&gt;Choisis l'approche qui t'excite le plus — it's about doing, not having it done&lt;/li&gt;
&lt;li&gt;Cherche à internaliser plutôt qu'à mémoriser&lt;/li&gt;
&lt;li&gt;Choisis une option suffisamment bonne plutôt que chercher la meilleure&lt;/li&gt;
&lt;li&gt;Fais avec peu — impressionne par le peu de moyens dont tu as besoin&lt;/li&gt;
&lt;li&gt;Concentre-toi sur aider les gens aujourd'hui et oublie les grands plans&lt;/li&gt;
&lt;li&gt;Concentre-toi sur un projet à la fois — fais de terminer une priorité&lt;/li&gt;
&lt;li&gt;Applique-toi — la façon dont tu fais une chose est la façon dont tu fais tout&lt;/li&gt;
&lt;li&gt;Concentre-toi sur ce qui ne change pas plutôt qu'essayer de prédire&lt;/li&gt;
&lt;li&gt;Make it readable in 2 minutes&lt;/li&gt;
&lt;li&gt;N'essaie pas de paraître important — don't try to sound big&lt;/li&gt;
&lt;li&gt;Décide rapidement, puis engage-toi sur cette décision.&lt;/li&gt;
&lt;li&gt;Oublie-toi et donne aux gens ce qu'ils veulent — it's not about you&lt;/li&gt;
&lt;li&gt;Push-it — à moins que tu commences à te sentir hargneux&lt;/li&gt;
&lt;li&gt;Porte ton attention sur les sujets sous-exploités — underserved&lt;/li&gt;
&lt;li&gt;Les explorateurs sont de mauvais leaders&lt;/li&gt;
&lt;li&gt;Don't try to fix people&lt;/li&gt;
&lt;li&gt;In doubt, throw it out&lt;/li&gt;
&lt;li&gt;Whatever scares you, go do it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il m'a inspiré les projets suivants : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/mp"&gt;Mémoriser la référence de python&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/stb"&gt;Partager la transcription de mes podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/deploy"&gt;Setup mon propre serveur&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/quickstart-pelican"&gt;Créer mon site personnel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ma phase de vie actuelle est chaotique.&lt;/p&gt;
&lt;p&gt;Et son état d'esprit m'aide à avancer.&lt;/p&gt;</content></entry><entry><title>Ce que Benoit d'Artisan Développeur m'a apporté</title><link href="https://me.petitapetit.io/benoit" rel="alternate"/><published>2025-03-30T00:00:00+01:00</published><updated>2025-03-30T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-03-30:/benoit</id><summary type="html">&lt;p&gt;J'ai été diplômé en juin 2020. Je me suis beaucoup investi dans mon stage de fin d'étude. J'y travaillais de 8h à 20h. Et j'étais convaincu que j'intégrerais l'entreprise à la fin. Mais cela ne c'est pas passé comme je l'avais prévu. Les grandes entreprises fonctionnent avec des tickets de …&lt;/p&gt;</summary><content type="html">&lt;p&gt;J'ai été diplômé en juin 2020. Je me suis beaucoup investi dans mon stage de fin d'étude. J'y travaillais de 8h à 20h. Et j'étais convaincu que j'intégrerais l'entreprise à la fin. Mais cela ne c'est pas passé comme je l'avais prévu. Les grandes entreprises fonctionnent avec des tickets de recrutement. Il n'y en avait pas à la fin de mon stage. J'ai été prolongé en intérim. Mais je ne savais pas où j'allais.&lt;/p&gt;
&lt;p&gt;J'ai passé un entretien avec une personne du top management. Elle m'a demandé si je faisais de la veille. Je lui ai répondu que non, que je n'accrochais pas avec les blogs, mais que lorsque je voulais être artiste numérique, je participais à des meetups et ces rencontres m'avaient beaucoup apprises. &lt;/p&gt;
&lt;p&gt;Sa question m'a marqué. &lt;/p&gt;
&lt;p&gt;J'ai pris conscience que je ne faisais plus de veille.&lt;/p&gt;
&lt;p&gt;C'est un problème quand tu travailles dans la tech. Mais mon problème était encore plus profond. Je n'étais plus intéressé par mon métier. Entre 2010 à 2018, j'ai tiré mon énergie de ma passion pour les arts numériques. Mais cette passion c'est éteinte. Réaliser les enjeux liés au changement climatique m'a bouleversé. Je me suis tourné vers EDF car je croyais au nucléaire et au secteur public, mais je peinais à trouver ma place. J'étais égaré.&lt;/p&gt;
&lt;p&gt;J'ai testé plusieurs podcasts tech pour refaire de la veille. &lt;/p&gt;
&lt;p&gt;Artisan Développeur avait quelque chose d'accessible. J'ai senti la passion de Benoit, son amour pour ce qui est bien fait, et j'ai accroché.&lt;/p&gt;
&lt;p&gt;Je suis reparti du premier épisode et j'ai tout écouté. Cela a représenté presque 400 épisodes. Et sa voix m'a accompagné pendant un an.&lt;/p&gt;
&lt;p&gt;J'ai découvert le craftsmanship, le TDD, les code retreats et les coding dojo. &lt;/p&gt;
&lt;p&gt;Je me suis mis à lire des livres sur la tech comme Clean Code et Refactoring.&lt;/p&gt;
&lt;p&gt;J'ai recommencé à participer à des meetups.&lt;/p&gt;
&lt;p&gt;J'ai suivi sa formation.&lt;/p&gt;
&lt;p&gt;Et j'ai travaillé avec lui en coaching lors de ma recherche d'emploi. &lt;/p&gt;
&lt;p&gt;Il m'a aidé à clarifier mon positionnement afin que je présente un projet cohérent aux recruteurs. Il m'a aidé à chercher du travail de façon méthodique. Et il m'a aidé à passer au delà de croyances limitantes, du type "non mais cette boîte-là est inaccessible pour moi".&lt;/p&gt;
&lt;p&gt;Benoit a réinsufflé de la vie dans mon existence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;J'ai retrouvé ma passion pour le développement.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et cela a débloqué ma vie professionnelle.&lt;/p&gt;</content></entry><entry><title>Mon avis sur Jira</title><link href="https://me.petitapetit.io/jira" rel="alternate"/><published>2025-03-20T00:00:00+01:00</published><updated>2025-03-20T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-03-20:/jira</id><summary type="html">&lt;p&gt;Jira est un outil de suivi de tâches développé par Atlassian.&lt;/p&gt;
&lt;p&gt;Dans &lt;a href="stb2"&gt;l'épisode 2 de Spaghetti to Bento&lt;/a&gt;, j'ai demandé à Adrien Joly :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pourquoi est-ce que les devs détestent Jira ? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ma question est inspirée par &lt;a href="_assets/jira-meme.png"&gt;ce meme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il a évoqué deux aspects : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la dimension de contrôle&lt;/li&gt;
&lt;li&gt;la friction que cela …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Jira est un outil de suivi de tâches développé par Atlassian.&lt;/p&gt;
&lt;p&gt;Dans &lt;a href="stb2"&gt;l'épisode 2 de Spaghetti to Bento&lt;/a&gt;, j'ai demandé à Adrien Joly :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pourquoi est-ce que les devs détestent Jira ? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ma question est inspirée par &lt;a href="_assets/jira-meme.png"&gt;ce meme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il a évoqué deux aspects : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la dimension de contrôle&lt;/li&gt;
&lt;li&gt;la friction que cela ajoute&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Puis, il m'a demandé mon avis. 
Voici ma réponse posée.&lt;/p&gt;
&lt;h2&gt;Ce qui me frustre&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;L'outil est lent&lt;/strong&gt;. Je travaille pour une grande entreprise. Le service est hébergé on-premise. Mais les machines semblent sous-dimensionnées. L'outil manque de réactivité. Lorsque je l'ouvre en début de journée, j'ai un motif tout trouvé pour être ronchon. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L'outil utilise un formatage non standard&lt;/strong&gt;. Il n'est pas possible de copier-coller du markdown dedans. Cela me donne l'impression que l'outil est pensé pour les managers avant d'être pensé pour les devs. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L'outil est encombré&lt;/strong&gt;. Il présente un tas d'options qui ne me servent pas. Ces options créent du bruit, de la distraction. Lorsque je lance l'outil, je penser à cette image qui compare l'interface de Yahoo à celle de Google. L'interface de Yahoo déborde d'informations inutiles. L'interface de Google va à l'essentiel. Elle est claire et reposante.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L'outil est paramétré par un organisme distant&lt;/strong&gt;. Et on ne peut pas se l'approprier. Dans mon cas, l'outil est paramétré pour fonctionner en sprint avec un tableau à 3 colonnes : todo / doing / done.  Je préfère l'approche en flux qui permet de communiquer clairement le processus de travail : à faire, use-case clarifié, en cours de développement, en cours de revue, en cours de recette, livré. L'approche en flux facilite l'identification des goulots d'étranglements. En théorie, Jira peut embrasser cette approche. En pratique, je n'ai pas la main dessus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le processus de création d'une tâche est lourd&lt;/strong&gt; : créer un ticket, lui donner un titre, ajouter la bonne étiquette, ajouter une estimation, déplacer le ticket dans le sprint. La friction encourage à créer des gros tickets plutôt que de travailler en petits lots avec de petits tickets.&lt;/p&gt;
&lt;p&gt;Ces douleurs sont notables. Mais le plus important se joue au niveau des interactions humaines. Comme je n'aime pas m'en servir, j'évite d'y passer du temps. Or, c'est un outil important pour le manager de l'équipe. En ne le complétant pas, je ne réponds pas à son besoin de visibilité. Cela crée une tension entre nous. J'associe alors l'outil à cette tension. Cela renforce mon envie d'y passer le moins de temps possible. Et cela crée une spirale infernale.&lt;/p&gt;
&lt;p&gt;Peut-on casser ce cycle ?&lt;/p&gt;
&lt;h2&gt;Pourquoi utiliser un outil de gestion des tâches&lt;/h2&gt;
&lt;p&gt;Revenons aux bases. Pourquoi utilise-t-on des outils de gestion des tâches ? &lt;/p&gt;
&lt;p&gt;Un outil de gestion des tâches répond aux besoins de : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendre visible le travail en cours - et éviter que des tâches non-planifiées perturbent le flux des opérations&lt;/li&gt;
&lt;li&gt;Ne pas oublier de tâche&lt;/li&gt;
&lt;li&gt;Discuter des priorités&lt;/li&gt;
&lt;li&gt;Identifier les blocages&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reconsidérer ma première impression&lt;/h2&gt;
&lt;p&gt;L'outil n'est pas parfait. Mais il répond suffisamment bien aux besoins principaux. Il permet en plus de mesurer le temps passé à résoudre des bugs vs à développer de nouvelles fonctionnalités. Et il fait sens pour l'entreprise car il permet de garder la main sur les données.&lt;/p&gt;
&lt;p&gt;Avec le temps, j'ai trouvé une façon de fonctionner avec Jira qui me convient et qui répond aux besoins de l'équipe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le matin, je crée une todo-liste dans ma note journalière sur Obsidian&lt;/li&gt;
&lt;li&gt;au fil de ma progression sur une tâche, je prends mes notes dans le OneNote de l'équipe&lt;/li&gt;
&lt;li&gt;en fin de journée, je prends 5 minutes pour mettre à jour les tickets dans Jira. J'indique le temps passé, les actions réalisées, les actions qui restent à faire, ce que je prévois de remonter au daily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce point peut paraître un détail. Mais c'est un irritant qui revenait tous les jours. Cela pesait sur l'ambiance dans l'équipe. Clarifier ce point me permet de travailler de façon plus sereine et de concentrer mon énergie sur les problèmes métier.&lt;/p&gt;</content></entry><entry><title>Untestable code to testable code with Invert Dependency</title><link href="https://me.petitapetit.io/inject" rel="alternate"/><published>2025-02-28T00:00:00+01:00</published><updated>2025-02-28T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-02-28:/inject</id><summary type="html">&lt;p&gt;Dependency Injection is a great tool to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;make your code testable&lt;/li&gt;
&lt;li&gt;run your program in an isolated fashion&lt;/li&gt;
&lt;li&gt;toggle features&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is how to achieve it in code that is tightly coupled.&lt;/p&gt;
&lt;p&gt;We can proceed in 4 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup the project &lt;/li&gt;
&lt;li&gt;Group calls to infrastructure &lt;/li&gt;
&lt;li&gt;Move to delegate &lt;/li&gt;
&lt;li&gt;Inject dependency …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;Dependency Injection is a great tool to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;make your code testable&lt;/li&gt;
&lt;li&gt;run your program in an isolated fashion&lt;/li&gt;
&lt;li&gt;toggle features&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is how to achieve it in code that is tightly coupled.&lt;/p&gt;
&lt;p&gt;We can proceed in 4 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup the project &lt;/li&gt;
&lt;li&gt;Group calls to infrastructure &lt;/li&gt;
&lt;li&gt;Move to delegate &lt;/li&gt;
&lt;li&gt;Inject dependency&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="" src="_assets/trivia-inject-4-steps.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Apply instructions in &lt;a href="sao#setup"&gt;Setup&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Regroup calls to infrastructure code&lt;/h2&gt;
&lt;p&gt;Apply instructions in &lt;a href="sao#regroup"&gt;Regroup calls to infrastructure code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Move to delegate&lt;/h2&gt;
&lt;p&gt;Let's do some wishful thinking.&lt;/p&gt;
&lt;p&gt;I would like to have an instance of a &lt;code&gt;ConsoleLogger&lt;/code&gt; and call &lt;code&gt;logger.log(message)&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# what we would like&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# what we actually have&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well, let's create it.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It does nothing for the moment. &lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;p&gt;Now, move the infrastructure code &lt;code&gt;print(message)&lt;/code&gt; into the delegate.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;h2&gt;Invert dependency&lt;/h2&gt;
&lt;p&gt;We just took the infrastructure code out of our &lt;code&gt;Game&lt;/code&gt; class. &lt;/p&gt;
&lt;p&gt;For the sake of our test, we would like to pass an alternative implementation of the logger. For example : an in memory implementation that doesn't make any call to infrastructure code like &lt;code&gt;print&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To do so, we start by inverting the dependency.&lt;/p&gt;
&lt;p&gt;Extract the interface &lt;code&gt;Logger&lt;/code&gt; from &lt;code&gt;ConsoleLogger&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metaclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ABCMeta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;p&gt;Write a test implementation (it's a good candidate for TDD):&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;InMemoryLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the question is, "How do we use this implementation in our test?".&lt;/p&gt;
&lt;p&gt;Part of the answer is that it's not the responsibility of the game to instantiate the logger. And even less a responsibility of one of its methods. This can be fixed in a few steps.&lt;/p&gt;
&lt;p&gt;Introduce Field in the constructor:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Introduce Parameter:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Go back to &lt;code&gt;test_trivia.py&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Instantiate an &lt;code&gt;InMemoryLogger&lt;/code&gt; and inject it into the game. &lt;/p&gt;
&lt;p&gt;In the assert, use &lt;code&gt;logger.getvalue()&lt;/code&gt; instead of the StringIO.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# output = StringIO()                   -- delete this code&lt;/span&gt;
        &lt;span class="c1"&gt;# sys.stdout = output                   -- delete this code&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InMemoryLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;approved_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;data/test_trivia-approved-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate the approved versions&lt;/span&gt;
        &lt;span class="c1"&gt;# with open(approved_path, &amp;quot;w&amp;quot;) as f:&lt;/span&gt;
        &lt;span class="c1"&gt;#     f.write(output.getvalue())&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approved_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;expected&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="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# assert output.getvalue() == expected  -- delete this code&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, we are able to choose which implementation to use in our game.&lt;/p&gt;
&lt;p&gt;Convenient, isn't it?&lt;/p&gt;</content></entry><entry><title>Untestable code to testable code with Subclass and Override</title><link href="https://me.petitapetit.io/sao" rel="alternate"/><published>2025-02-27T00:00:00+01:00</published><updated>2025-02-27T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-02-27:/sao</id><summary type="html">&lt;p&gt;In &lt;a href="pac"&gt;Enabling safe refactoring with pytest and coverage&lt;/a&gt;, we saw how to secure a codebase and how to fill the holes in a test suite. But we cheated in one aspect.&lt;/p&gt;
&lt;p&gt;We used tricks to tame IOs. It's fair, but it has limitations: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it won't work if you run the …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;In &lt;a href="pac"&gt;Enabling safe refactoring with pytest and coverage&lt;/a&gt;, we saw how to secure a codebase and how to fill the holes in a test suite. But we cheated in one aspect.&lt;/p&gt;
&lt;p&gt;We used tricks to tame IOs. It's fair, but it has limitations: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it won't work if you run the tests in parallel&lt;/li&gt;
&lt;li&gt;such strategies can leave the environment in an undesired state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a way to intercept infrastructure code without relying on global objects.&lt;/p&gt;
&lt;p&gt;We will proceed in two steps: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;regroup calls to infrastructure code&lt;/li&gt;
&lt;li&gt;substitute calls to infrastructure code with Subclass and Override&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="_assets/trivia-subclass-and-override.jpg"&gt;&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;

&lt;p&gt;Retrieve the code: &lt;strong&gt;&lt;a href="_assets/trivia.py"&gt;trivia.py&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create an empty &lt;code&gt;play()&lt;/code&gt; method in the &lt;code&gt;Game&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Move the suite of &lt;code&gt;if __name__ == '__main__':&lt;/code&gt; into &lt;code&gt;Game.play&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;not_a_winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Chet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Sue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&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;if&lt;/span&gt; &lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;not_a_winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrong_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;not_a_winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;was_correctly_answered&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;not_a_winner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use the test elaborated in &lt;a href="trivia-safe"&gt;the previous post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;test_trivia.py&lt;/code&gt; : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;trivia&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;approved_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;data/test_trivia-approved-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate the approved versions&lt;/span&gt;
        &lt;span class="c1"&gt;# with open(approved_path, &amp;quot;w&amp;quot;) as f:&lt;/span&gt;
        &lt;span class="c1"&gt;#     f.write(output.getvalue())&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approved_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;expected&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="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt;. It should pass.&lt;/p&gt;
&lt;h2 id="regroup"&gt;Regroup calls to infrastructure code&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to verify we start in a working state.&lt;/p&gt;
&lt;p&gt;Look for a call to &lt;code&gt;print&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;how_many_players&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;purses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;how_many_players&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_penalty_box&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;how_many_players&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; was added&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- here!&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;They are player number &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Extract the argument of the first call to print in a variable &lt;code&gt;message&lt;/code&gt;: &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; was added&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;p&gt;Extract &lt;code&gt;print(message)&lt;/code&gt; in a method. Call it &lt;code&gt;_log&lt;/code&gt; : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; was added&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;They are player number &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Apply the transformation to every call to &lt;code&gt;print&lt;/code&gt; so that &lt;code&gt;_log&lt;/code&gt; is the only place where &lt;code&gt;print&lt;/code&gt; is used.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you're using PyCharm, it will offer you to apply this transformation to the 19 similar fragments. This automated refactoring messes with the string arguments declared on several physical lines. Run &lt;code&gt;black trivia.py -l 150&lt;/code&gt; before applying the transformation, and it will work fine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;p&gt;Inline the &lt;code&gt;message&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;pytest&lt;/code&gt; to check we didn't break anything. &lt;/p&gt;
&lt;p&gt;OK!&lt;/p&gt;
&lt;h2&gt;Substitute calls to infrastructure code with Subclass and Override&lt;/h2&gt;
&lt;p&gt;Now that we grouped calls to &lt;code&gt;print&lt;/code&gt;, it will be easy to substitute them.&lt;/p&gt;
&lt;p&gt;We want to capture the logs without relying on the standard output.&lt;/p&gt;
&lt;p&gt;Create a class &lt;code&gt;TestableGame&lt;/code&gt; that inherits &lt;code&gt;Game&lt;/code&gt; and override the &lt;code&gt;log&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;TestableGame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__test__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- to prevent pytest trying to collect it&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can test our code without using the standard output:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InspectableGame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;approved_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;data/test_trivia-approved-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate the approved versions&lt;/span&gt;
        &lt;span class="c1"&gt;# with open(approved_path, &amp;quot;w&amp;quot;) as f:&lt;/span&gt;
        &lt;span class="c1"&gt;#     f.write(output.getvalue())&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approved_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;expected&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="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content></entry><entry><title>Enabling safe refactoring with pytest and coverage</title><link href="https://me.petitapetit.io/pac" rel="alternate"/><published>2025-02-26T00:00:00+01:00</published><updated>2025-02-26T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-02-26:/pac</id><summary type="html">&lt;blockquote&gt;
&lt;p&gt;This article starts like &lt;a href="bat"&gt;bash approval testing&lt;/a&gt;, but the solution takes another path. Let's rewind the story ! (or jump to &lt;a href="#solution"&gt;putting the code under tests&lt;/a&gt; to skip the plot)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The plot&lt;/h2&gt;
&lt;p&gt;&lt;u&gt;Chris the product owner&lt;/u&gt; : 
Hey Alex. 
The business wants to add a new questions category to Trivia Game. 
Our …&lt;/p&gt;</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;This article starts like &lt;a href="bat"&gt;bash approval testing&lt;/a&gt;, but the solution takes another path. Let's rewind the story ! (or jump to &lt;a href="#solution"&gt;putting the code under tests&lt;/a&gt; to skip the plot)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The plot&lt;/h2&gt;
&lt;p&gt;&lt;u&gt;Chris the product owner&lt;/u&gt; : 
Hey Alex. 
The business wants to add a new questions category to Trivia Game. 
Our developer left the project. Can you do it ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex the dev&lt;/u&gt; : Hey Chris. Sure. You send me the code ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Chris&lt;/u&gt; : Here it is &lt;strong&gt;&lt;a href="_assets/trivia.py"&gt;trivia.py&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex&lt;/u&gt; : I take a look at it.&lt;/p&gt;
&lt;p&gt;Here we are, with some legacy code. It's valuable. The business runs on it. And it has no tests. The author of this code isn't here anymore to explain his choices. We are left alone with the task.&lt;/p&gt;
&lt;p&gt;The plan is to proceed in two steps :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a general idea of what's going on&lt;/li&gt;
&lt;li&gt;Put the code under tests to clean it up&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What's going on&lt;/h2&gt;
&lt;p&gt;We execute the code. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;python trivia.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It prints a series of messages in the console:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Chet was added
They are player number 1
Pat was added
They are player number 2
Sue was added
They are player number 3
Chet is the current player
They have rolled a 4
Chet&amp;#39;s new location is 4
The category is Pop
Pop Question 0
Answer was corrent!!!!
Chet now has 1 Gold Coins.
Pat is the current player
...
Answer was corrent!!!!
Chet now has 6 Gold Coins.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These messages are the &lt;strong&gt;observable behavior&lt;/strong&gt; of the application.&lt;/p&gt;
&lt;p&gt;It appears to be a board game. Players are added to a game. They roll dice, answer questions, and earn coins. The game ends when a player gets 6 coins.&lt;/p&gt;
&lt;p&gt;The code is messy and error-prone. &lt;/p&gt;
&lt;p&gt;Let's refactor it. This will : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Improve the clarity of the code&lt;/li&gt;
&lt;li&gt;Make it easier to add the new feature&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But before making any change, we want to secure it. &lt;/p&gt;
&lt;h2 id="solution"&gt;Putting the code under test&lt;/h2&gt;

&lt;p&gt;Extract the suite of &lt;code&gt;if __name__ == '__main__'&lt;/code&gt; in a &lt;code&gt;play()&lt;/code&gt; method. &lt;/p&gt;
&lt;p&gt;Then, create a test that captures the observable behavior of the game.&lt;/p&gt;
&lt;p&gt;At the project's root:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install pytest
touch test_trivia.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;test_trivia.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;trivia&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;play&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;

    &lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;APPROVED&lt;/span&gt;


&lt;span class="n"&gt;APPROVED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The test will fail.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;pytest -k test_trivia -vv&lt;/code&gt; and assign the value of &lt;code&gt;output&lt;/code&gt; to &lt;code&gt;APPROVED&lt;/code&gt; : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;APPROVED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;Chet was added&lt;/span&gt;
&lt;span class="s2"&gt;They are player number 1&lt;/span&gt;
&lt;span class="s2"&gt;Pat was added&lt;/span&gt;
&lt;span class="s2"&gt;They are player number 2&lt;/span&gt;
&lt;span class="s2"&gt;Sue was added&lt;/span&gt;
&lt;span class="s2"&gt;They are player number 3&lt;/span&gt;
&lt;span class="s2"&gt;Chet is the current player&lt;/span&gt;
&lt;span class="s2"&gt;They have rolled a 5&lt;/span&gt;
&lt;span class="s2"&gt;Chet&amp;#39;s new location is 5&lt;/span&gt;
&lt;span class="s2"&gt;The category is Science&lt;/span&gt;
&lt;span class="s2"&gt;Science Question 0&lt;/span&gt;
&lt;span class="s2"&gt;Answer was corrent!!!!&lt;/span&gt;
&lt;span class="s2"&gt;Chet now has 1 Gold Coins.&lt;/span&gt;
&lt;span class="s2"&gt;Pat is the current player&lt;/span&gt;
&lt;span class="s2"&gt;...&lt;/span&gt;
&lt;span class="s2"&gt;Answer was corrent!!!!&lt;/span&gt;
&lt;span class="s2"&gt;Chet now has 6 Gold Coins.&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;-vv&lt;/code&gt; increase verbosity twice and prevent &lt;code&gt;pytest&lt;/code&gt; from truncating the output. &lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;pytest -k test_trivia -vv&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;The beginning of the strings are equal but they differ on the first roll:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; Chet was added
    They are player number 1
    Pat was added
    They are player number 2
    Sue was added
    They are player number 3
    Chet is the current player
  - They have rolled a 5
  ?                    ^
  + They have rolled a 3
  ?                    ^
  - Chet&amp;#39;s new location is 5
  ?                        ^
  + Chet&amp;#39;s new location is 3
  ?                        ^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It looks like there is randomness. &lt;/p&gt;
&lt;p&gt;To solve this problem, we can : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fix randomness by calling &lt;code&gt;random.seed(10)&lt;/code&gt; in the arrange phase of our test&lt;/li&gt;
&lt;li&gt;run the test again — &lt;code&gt;pytest -k test_trivia -vv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;capture the output for the seed and paste it into &lt;code&gt;APPROVED&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;run the test again — &lt;code&gt;pytest -k test_trivia&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Green !&lt;/p&gt;
&lt;p&gt;Now : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What about the quality of our test ? &lt;/li&gt;
&lt;li&gt;Do we cover all the production code ? &lt;/li&gt;
&lt;li&gt;Do we cover every behavior? &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's take a look at the coverage.&lt;/p&gt;
&lt;h2&gt;Finding holes with test coverage&lt;/h2&gt;
&lt;p&gt;Test coverage reports gives the code executed by our test suite.&lt;/p&gt;
&lt;p&gt;Along with pytest, we can get it by running:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install coverage
coverage run -m pytest
coverage html
open htmlcov/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="" src="_assets/trivia-coverage.png"&gt;&lt;/p&gt;
&lt;p&gt;Only two lines aren't covered :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;is_playable&lt;/code&gt; method (but it's dead code)&lt;/li&gt;
&lt;li&gt;the suite of &lt;code&gt;if __name__ == '__main__':&lt;/code&gt; (but there isn't any logic here, the bloc just calls &lt;code&gt;play&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It looks like everything is covered.&lt;/p&gt;
&lt;p&gt;Keep in mind that coverage is a negative metric. If the code is not covered, you know for sure that the code isn't protected from regression. But coverage doesn't guarantee you're safe. To be sure an instruction is protected, mutate it. The test should now fail. If it's not the case, your test can be improved.&lt;/p&gt;
&lt;p&gt;As we have enough confidence in our test, let's format the code with black.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install black
black .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's way better ✨ 😎 ✨.&lt;/p&gt;
&lt;p&gt;Let's have a look to our coverage again&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;coverage run -m pytest &amp;amp;&amp;amp; coverage html
open htmlcov/trivia_py.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="" src="_assets/trivia-coverage-formatted.png"&gt;&lt;/p&gt;
&lt;p&gt;There are 10 missing lines now. 😱&lt;/p&gt;
&lt;p&gt;We missed holes due to the formatting.&lt;/p&gt;
&lt;p&gt;The clause&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_player&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Pop&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;became&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_player&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Pop&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with the formatting. &lt;/p&gt;
&lt;p&gt;The header is covered, but the suite is never executed. &lt;/p&gt;
&lt;p&gt;This information was missed in the first report.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Format your code to standards. It avoids wrong measures with &lt;code&gt;coverage&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Filling holes with more test cases&lt;/h2&gt;
&lt;p&gt;Our test suite is viable, but isn't 100% secure. &lt;/p&gt;
&lt;p&gt;I would tend to find it acceptable for day-to-day situations.&lt;/p&gt;
&lt;p&gt;It offers a good compromise between level of protection and setup effort.&lt;/p&gt;
&lt;p&gt;But we can improve it. &lt;/p&gt;
&lt;p&gt;The usual solution is to add missing test scenarios. &lt;/p&gt;
&lt;p&gt;Here, the missing scenarios are games with other seeds.&lt;/p&gt;
&lt;p&gt;A way to cover them is to :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;duplicates trivia code&lt;/li&gt;
&lt;li&gt;test it over a wide range of seeds&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
        &lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- the code we will refactor&lt;/span&gt;

        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;
        &lt;span class="n"&gt;play_reference&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- a copy of the original behavior&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If duplicating the code isn't convenient, we can store the approved outputs:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_trivia&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
        &lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;approved_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;data/test_trivia-approved-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate the approved versions&lt;/span&gt;
        &lt;span class="c1"&gt;# with open(approved_path, &amp;quot;w&amp;quot;) as f:&lt;/span&gt;
        &lt;span class="c1"&gt;#     f.write(output.getvalue())&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;approved_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;expected&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="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's check the coverage:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pytest --cov=. --cov-report html
open htmlcov/trivia_py.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="" src="_assets/trivia-covered.png"&gt;&lt;/p&gt;
&lt;p&gt;Dead code appart, all the paths are covered. &lt;/p&gt;
&lt;p&gt;We can play a few mutations to increase confidence in the test. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Now we can refactor safely&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We explored a safe way to put trivia under test. &lt;/p&gt;
&lt;p&gt;But this approach still has a weakness : it plays with side effects.&lt;/p&gt;
&lt;p&gt;It works with randomness and logs. But this approach won't work with external dependencies like HTTP calls and database communications.&lt;/p&gt;
&lt;p&gt;Also : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it can cause problems if you want to run tests in parallel&lt;/li&gt;
&lt;li&gt;it's all or nothing (you cannot decide which log to catch and which not)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A more generic approach can be adopted by injecting dependencies. Let's see in &lt;a href="sao"&gt;the next post&lt;/a&gt; to see how we can achieve this with "Subclass and Override", and "Move to delegate" techniques.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Postscript - I'm not recommending to use logs to perform approval tests. I juste note that it can be used to do so. By the way, in most situations, approval tests are meant to be ephemeral. It's like when you protect the room before painting. Once the job is done, you remove the protections. &lt;/p&gt;
&lt;/blockquote&gt;</content></entry><entry><title>Bash approval testing (don't do it)</title><link href="https://me.petitapetit.io/bat" rel="alternate"/><published>2025-02-20T00:00:00+01:00</published><updated>2025-02-20T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-02-20:/bat</id><summary type="html">&lt;h2&gt;The plot&lt;/h2&gt;
&lt;p&gt;&lt;u&gt;Chris the product owner&lt;/u&gt; : 
Hey Alex. 
The business wants to add a new questions category to Trivia Game. 
Our developer left the project. Can you do it ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex the dev&lt;/u&gt; : Hey Chris. Sure. You send me the code ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Chris&lt;/u&gt; : Yes, here it is &lt;strong&gt;&lt;a href="_assets/trivia.py"&gt;trivia.py&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex&lt;/u&gt; : Ok, I …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The plot&lt;/h2&gt;
&lt;p&gt;&lt;u&gt;Chris the product owner&lt;/u&gt; : 
Hey Alex. 
The business wants to add a new questions category to Trivia Game. 
Our developer left the project. Can you do it ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex the dev&lt;/u&gt; : Hey Chris. Sure. You send me the code ?&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Chris&lt;/u&gt; : Yes, here it is &lt;strong&gt;&lt;a href="_assets/trivia.py"&gt;trivia.py&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Alex&lt;/u&gt; : Ok, I take a look at it.&lt;/p&gt;
&lt;p&gt;Here we are, with some legacy code. It's valuable. The business runs on it. And it has no tests. The author of this code isn't here anymore to explain his choices. We are left alone with the task.&lt;/p&gt;
&lt;p&gt;The plan is to proceed in two steps :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a general idea of what's going on&lt;/li&gt;
&lt;li&gt;Put the code under tests to clean it up&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What's going on&lt;/h2&gt;
&lt;p&gt;We execute the code. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;python trivia.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It prints a series of messages in the console:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Chet was added
They are player number 1
Pat was added
They are player number 2
Sue was added
They are player number 3
Chet is the current player
They have rolled a 4
Chet&amp;#39;s new location is 4
The category is Pop
Pop Question 0
Answer was corrent!!!!
Chet now has 1 Gold Coins.
Pat is the current player
...
Answer was corrent!!!!
Chet now has 6 Gold Coins.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These messages are the &lt;strong&gt;observable behavior&lt;/strong&gt; of the application.&lt;/p&gt;
&lt;p&gt;It appears to be a board game. Players are added to the game. They roll dice, answer questions, and earn coins. The game ends when a player gets 6 coins.&lt;/p&gt;
&lt;p&gt;The code is messy and error-prone. &lt;/p&gt;
&lt;p&gt;Let's refactor it. This will : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Improve the clarity of the code&lt;/li&gt;
&lt;li&gt;Make it easier to add the new feature&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But before making any change, we want to secure it. &lt;/p&gt;
&lt;h2&gt;Putting the code under test&lt;/h2&gt;
&lt;p&gt;Refactorings restructure the software without modifying its observable behavior. That is in theory, when you don't introduce regressions. &lt;/p&gt;
&lt;p&gt;The best way to prevent regressions is to put your code under tests. &lt;/p&gt;
&lt;p&gt;A fast way to put legacy code under tests is to capture the output, and check that the output remains the same after each refactoring. In this case, the output isn't returned by a function, put is expressed through the standard output.&lt;/p&gt;
&lt;p&gt;Let's catch this output in a file that will serve as a reference:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;trivia.py&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;trivia.approved.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let's make another run and compare the two outputs:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;trivia.py&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt&lt;span class="w"&gt; &lt;/span&gt;
diff&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt&lt;span class="w"&gt; &lt;/span&gt;trivia.approved.txt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;less

&lt;span class="c1"&gt;# 8,9c8,9&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt; They have rolled a 5&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt; Chet&amp;#39;s new location is 5&lt;/span&gt;
&lt;span class="c1"&gt;# ---&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; They have rolled a 1&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; Chet&amp;#39;s new location is 1&lt;/span&gt;
&lt;span class="c1"&gt;# 12,32d11&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On the approved game, Chet ran a 5 on the first round.
On the received game, Chet ran a 1 on the first round. &lt;/p&gt;
&lt;p&gt;Randomness is going there. &lt;/p&gt;
&lt;p&gt;For the sake of our test, we can fix it. In &lt;code&gt;trivia.py&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- add this import&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;not_a_winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- add this instruction&lt;/span&gt;

    &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's try again&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python trivia.py &amp;gt; trivia.approved.txt
python trivia.py &amp;gt; trivia.received.txt 
diff trivia.received.txt trivia.approved.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The outputs are now the same! &lt;/p&gt;
&lt;p&gt;To put our safety system under stress, we can introduce errors in the code to check if they get detected. Like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introduce a typo in the logs&lt;/li&gt;
&lt;li&gt;Comment some lines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, we can comment the first line of &lt;code&gt;game.roll&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# print(&amp;quot;%s is the current player&amp;quot; % self.players[self.current_player])&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;They have rolled a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output of the edited code differs from our reference:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;trivia.py&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt&lt;span class="w"&gt; &lt;/span&gt;
diff&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt&lt;span class="w"&gt; &lt;/span&gt;trivia.approved.txt
&lt;span class="c1"&gt;# 47a48,49&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; Answer was correct!!!!&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; Sue now has 1 Gold Coins.&lt;/span&gt;
&lt;span class="c1"&gt;# 67a70,71&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; Answer was correct!!!!&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt; Sue now has 2 Gold Coins.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The error was detected.&lt;/p&gt;
&lt;p&gt;Let's encapsulate this logic in a &lt;code&gt;test.sh&lt;/code&gt; script:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;

python&lt;span class="w"&gt; &lt;/span&gt;trivia.py&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;diff&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;trivia.received.txt&lt;span class="w"&gt; &lt;/span&gt;trivia.approved.txt&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;✅ : no changes detected&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;❌ : something has been broken&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can check that we haven't broken anything with a single command.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We explored a fast way to put our code under tests, but this approach has two main problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We don't know which part of the code is covered (or not)&lt;/li&gt;
&lt;li&gt;To be able to build our test, we made changes in the behavior of production code. Remember &lt;code&gt;seed(0)&lt;/code&gt; ? Now each party is the same. We cannot ship this code. Using this approach can only be a temporary solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the next article, we will see how we can improve these two aspects!&lt;/p&gt;</content></entry><entry><title>A simple way to find hotspots with Python</title><link href="https://me.petitapetit.io/hotspots" rel="alternate"/><published>2025-02-09T00:00:00+01:00</published><updated>2025-02-09T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-02-09:/hotspots</id><summary type="html">&lt;p&gt;I've just finished &lt;a href="https://understandlegacycode.com/first-aid-kit/"&gt;Legacy Code: First Aid Kit&lt;/a&gt; by Nicolas Carlo.&lt;/p&gt;
&lt;p&gt;I loved it: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it's clear and concise (no random blabla to fill the pages)&lt;/li&gt;
&lt;li&gt;it focuses on the underlying principles (and go beyond buzzwords)&lt;/li&gt;
&lt;li&gt;it's very practical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The book expose 14 cohesive techniques. &lt;/p&gt;
&lt;p&gt;The first one is about identifying …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've just finished &lt;a href="https://understandlegacycode.com/first-aid-kit/"&gt;Legacy Code: First Aid Kit&lt;/a&gt; by Nicolas Carlo.&lt;/p&gt;
&lt;p&gt;I loved it: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it's clear and concise (no random blabla to fill the pages)&lt;/li&gt;
&lt;li&gt;it focuses on the underlying principles (and go beyond buzzwords)&lt;/li&gt;
&lt;li&gt;it's very practical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The book expose 14 cohesive techniques. &lt;/p&gt;
&lt;p&gt;The first one is about identifying &lt;strong&gt;hotspots&lt;/strong&gt; when you approach a codebase.&lt;/p&gt;
&lt;p&gt;Hotspots are parts of your program that are complex, and where the team spend a lot of time. &lt;/p&gt;
&lt;p&gt;Hotspots are great candidates for refactoring.&lt;/p&gt;
&lt;p&gt;Here is how to visualize them on a python codebase.&lt;/p&gt;
&lt;h2&gt;Compute the churn&lt;/h2&gt;
&lt;p&gt;The churn is a measure of "where things move".&lt;/p&gt;
&lt;p&gt;You can get it by counting how many times a file has been edited the last year.&lt;/p&gt;
&lt;p&gt;To get this information, &lt;code&gt;git&lt;/code&gt; is your friend : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;--format&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="w"&gt; &lt;/span&gt;--name-only&lt;span class="w"&gt; &lt;/span&gt;--since&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;.month&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;egrep&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^$&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uniq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;-nr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;head&lt;span class="w"&gt; &lt;/span&gt;-10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On &lt;a href="https://github.com/django/django"&gt;django&lt;/a&gt;'s codebase, it gives: &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;28&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;admin_views&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt;&lt;span class="n"&gt;ressions&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;test_operations&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="mf"&gt;13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let's tweak it to send the result in a csv:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;--format&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="w"&gt; &lt;/span&gt;--name-only&lt;span class="w"&gt; &lt;/span&gt;--since&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;.month&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;egrep&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^$&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uniq&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;egrep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\.py&lt;/span&gt;$&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;-nr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;BEGIN {print &amp;quot;filename,churn&amp;quot;} {gsub(/^[ \t]+/, &amp;quot;&amp;quot;, $0); print $2&amp;quot;,&amp;quot;$1}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;churn.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Hotspots according to lines of code (LOC)&lt;/h2&gt;
&lt;p&gt;The simplest complexity metrics for a file is the number of lines of code it has. &lt;/p&gt;
&lt;p&gt;The LOCs.&lt;/p&gt;
&lt;p&gt;To get this information for each file, you can use &lt;a href="https://github.com/AlDanial/cloc"&gt;cloc&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;cloc
cloc&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;--match-f&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\.py$&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--by-file&lt;span class="w"&gt; &lt;/span&gt;--csv&lt;span class="w"&gt; &lt;/span&gt;--out&lt;span class="o"&gt;=&lt;/span&gt;loc.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, lets plot the LOCs against the churn:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pathlib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;plotly.express&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;px&lt;/span&gt;


&lt;span class="n"&gt;churns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;churn.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;churn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;locs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;loc.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;code&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;loc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropna&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;locs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;locs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_posix&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;hotspots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;churns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hotspots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;churn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;loc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hover_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hotspots_with_loc.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and, voilà :&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/hotspots-loc.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;Hotspots according to LOCs on django&lt;/div&gt;

&lt;h2&gt;Hotspots according to cyclomatic complexity&lt;/h2&gt;
&lt;p&gt;The cyclomatic complexity is given by the number of path a program can take.&lt;/p&gt;
&lt;p&gt;A program with a single path is linear and is easy to understand.&lt;/p&gt;
&lt;p&gt;A programs with a lot of forks can take many paths. It makes it mentally demanding to follow, easy to mis-understand, and risky to change. &lt;/p&gt;
&lt;p&gt;To measure the cyclomatic complexity of your files, you can use &lt;a href="https://github.com/rubik/radon"&gt;radon&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install radon
radon cc . -j -O complexity.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; is the current directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-j&lt;/code&gt; format the output in &lt;code&gt;json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-O&lt;/code&gt; indicates the output file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, here is a script to plot the hotspots:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;plotly.express&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;px&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;read_complexity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&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;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_stats&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;nb_elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;file_complexity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;file_complexity&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;complexity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;nb_elements&lt;/span&gt;
            &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_complexity&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;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cyclomatic_complexity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="n"&gt;churns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;churn.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;churn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;ccs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_complexity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;complexity.json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;hotspots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;churns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ccs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hotspots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;churn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cyclomatic_complexity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hover_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;filename&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hotspots_with_cyclomatic_complexity.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here it is !
&lt;img alt="" src="_assets/hotspots-cc.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;Hotspots according to cyclomatic complexity on django&lt;/div&gt;

&lt;p&gt;At first glance, the codebase looks healthy.&lt;/p&gt;</content></entry><entry><title>Mes intentions pour 2025</title><link href="https://me.petitapetit.io/intentions-2025" rel="alternate"/><published>2025-01-29T00:00:00+01:00</published><updated>2025-01-29T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-01-29:/intentions-2025</id><summary type="html">&lt;p&gt;L'année dernière, j'ai publié un article de 10 minutes sur mes intentions pour 2024. Cette note m'a guidée. Mais je n'ai pas réalisé un dixième de ce que j'avais projeté. Cette année, je veux me concentrer sur moins de choses : moins de projets, moins de principes directeurs, mais plus d'intensité …&lt;/p&gt;</summary><content type="html">&lt;p&gt;L'année dernière, j'ai publié un article de 10 minutes sur mes intentions pour 2024. Cette note m'a guidée. Mais je n'ai pas réalisé un dixième de ce que j'avais projeté. Cette année, je veux me concentrer sur moins de choses : moins de projets, moins de principes directeurs, mais plus d'intensité.&lt;/p&gt;
&lt;h2&gt;Vision à 3 ans&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;J'ai un podcast niché&lt;/li&gt;
&lt;li&gt;J'ai vécu dans plusieurs pays&lt;/li&gt;
&lt;li&gt;Je suis à l'aise pour sociabiliser&lt;/li&gt;
&lt;li&gt;Je suis à l'aise pour m'exprimer en anglais&lt;/li&gt;
&lt;li&gt;Je suis à l'aise avec Python, Javascript et PostgreSQL. Je travaille avec une stack rodée que je maîtrise parfaitement. Et j'ai développé mon réseau professionnel en me faisant plein d'amis dans la tech.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mes projets de l'année&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vendre ma maison.&lt;/li&gt;
&lt;li&gt;Lancer un podcast autour du refactoring.&lt;/li&gt;
&lt;li&gt;M'installer au Vietnam.&lt;/li&gt;
&lt;li&gt;M'aimer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mes valeurs cardinales&lt;/h3&gt;
&lt;p&gt;Enthousiasme : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Et si ? expérimenter, considérer les options contre-intuitives.&lt;/li&gt;
&lt;li&gt;Considérer la direction option opposée à ma première intuition.&lt;/li&gt;
&lt;li&gt;Choisir l'option qui m'excite le plus, qui laisse entrevoir fun et aventure.&lt;/li&gt;
&lt;li&gt;Avoir confiance dans l'avenir.&lt;/li&gt;
&lt;li&gt;Exécuter sans traîner.&lt;/li&gt;
&lt;li&gt;Aller de l'avant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Minimalisme et simplicité : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Me séparer de ce dont je n'ai pas vraiment besoin (in doubt, throw it out).&lt;/li&gt;
&lt;li&gt;Introduire la complexité juste-à-temps plutôt qu'au cas où.&lt;/li&gt;
&lt;li&gt;Partir du problème et du besoin (plutôt que des solutions tendance).&lt;/li&gt;
&lt;li&gt;Choisir la simplicité, même si c'est plus difficile.&lt;/li&gt;
&lt;li&gt;Chérir ce que j'ai et être diligent.&lt;/li&gt;
&lt;li&gt;Ne pas m'attacher à mes objectifs.&lt;/li&gt;
&lt;li&gt;Garder un niveau de vie bas.&lt;/li&gt;
&lt;li&gt;Faire la paix avec le pire.&lt;/li&gt;
&lt;li&gt;Réduire mes attentes.&lt;/li&gt;
&lt;li&gt;Réduire mes besoins.&lt;/li&gt;
&lt;li&gt;Manger moins vite.&lt;/li&gt;
&lt;li&gt;Faire moins.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sincérité :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dire non plus souvent.&lt;/li&gt;
&lt;li&gt;Partager mes émotions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Générosité : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chercher à apporter un maximum de valeur.&lt;/li&gt;
&lt;li&gt;Maximiser le fun et la satisfaction client plutôt que le profit.&lt;/li&gt;
&lt;li&gt;Considérer les inconnus comme des amis potentiels.&lt;/li&gt;
&lt;li&gt;M'aimer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ce que je souhaite vivre, expérimenter et ressentir en 2025&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;de l'enthousiasme&lt;/li&gt;
&lt;li&gt;de la concentration&lt;/li&gt;
&lt;li&gt;de l'amour pour moi-même&lt;/li&gt;
&lt;li&gt;un sentiment de contribution aux autres&lt;/li&gt;
&lt;li&gt;du contentement : apprécier la chance que j'ai plutôt que de me lamenter sur mon sors, me concentrer sur ce qui ne va pas, et me battre avec mon environnement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ce qui me fera dire que 2025 fut une bonne année&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;je n'ai plus de dettes&lt;/li&gt;
&lt;li&gt;j'ai passé +4 mois au Vietnam&lt;/li&gt;
&lt;li&gt;je suis à l'aise pour tenir une conversation en anglais&lt;/li&gt;
&lt;li&gt;j'ai passé 2 mois 100% focus sur des expériences sociales&lt;/li&gt;
&lt;li&gt;j'ai enregistré 24 épisodes de podcast&lt;/li&gt;
&lt;li&gt;on s'est bien marré avec Florent (NERDS)&lt;/li&gt;
&lt;li&gt;j'ai une source de revenus pour 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Comment j'aimerais me souvenir de cette année dans 20 ans&lt;/h3&gt;
&lt;p&gt;L'année où j'ai pris mon envol. &lt;br&gt;L'année où j'ai arrêté de procrastiner. &lt;br&gt;L'année où j'ai commencé à m'aimer, sincèrement et profondément.&lt;/p&gt;</content></entry><entry><title>Mémoriser Python</title><link href="https://me.petitapetit.io/mp" rel="alternate"/><published>2025-01-28T00:00:00+01:00</published><updated>2025-01-28T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-01-28:/mp</id><summary type="html">&lt;h1&gt;Mémoriser Python&lt;/h1&gt;
&lt;p&gt;Je me suis lancé comme défi de mémoriser Python in a Nutshell, un livre de 700 pages qui est une référence synthétique du langage de programmation Python.&lt;/p&gt;
&lt;p&gt;Mon objectif était :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de challenger ma mémoire &lt;/li&gt;
&lt;li&gt;d'aller en profondeur sur le langage&lt;/li&gt;
&lt;li&gt;d'améliorer ma compréhension de l'écosystème&lt;/li&gt;
&lt;li&gt;d'envoyer le signal …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;h1&gt;Mémoriser Python&lt;/h1&gt;
&lt;p&gt;Je me suis lancé comme défi de mémoriser Python in a Nutshell, un livre de 700 pages qui est une référence synthétique du langage de programmation Python.&lt;/p&gt;
&lt;p&gt;Mon objectif était :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de challenger ma mémoire &lt;/li&gt;
&lt;li&gt;d'aller en profondeur sur le langage&lt;/li&gt;
&lt;li&gt;d'améliorer ma compréhension de l'écosystème&lt;/li&gt;
&lt;li&gt;d'envoyer le signal que je m'engage avec Python (autant à moi qu'au monde)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mes hypothèses étaient que : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;j'allais prendre plus de plaisir à travailler une fois la référence en tête&lt;/li&gt;
&lt;li&gt;les bénéfices allaient être supérieurs aux efforts requis&lt;/li&gt;
&lt;li&gt;cela allait améliorer ma confiance en moi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mon processus a consisté à lire sur ordinateur, prendre des notes en split-screen, et créer des cartes de révision pour Anki.&lt;/p&gt;
&lt;p&gt;Anki est un outil de mémorisation basée sur le principe de répétition espacée. &lt;/p&gt;
&lt;p&gt;L'idée derrière la technique de la révision espacée est que pour mémoriser une information pour toujours, il suffit de la réviser le lendemain, puis trois jours plus tars, une semaine plus tard, un mois plus tard, ... avec des intervalles de plus en plus espacés. La courbe de l'oubli s'atténue à chaque fois que l'on parvient à rappeler l'information.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-spaced-repetition.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;Illustration prise sur le site &lt;a href="https://www.lifehack.org/851026/spaced-repetition" target="_blank"&gt;LifeHack&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;J'ai mis 3 mois à terminer ce projet en y consacrant en moyenne 2h/jour.&lt;/p&gt;
&lt;p&gt;Voici les principales erreurs que j'ai commises.&lt;/p&gt;
&lt;h2&gt;Erreur #1 : créer des cartes trop théoriques&lt;/h2&gt;
&lt;p&gt;Les cartes de définitions sont des rabbit holes. &lt;/p&gt;
&lt;p&gt;Il est difficile d'arrêter une définition équilibrée : précise et en même temps concise ;  naturelle et en même temps exacte sur les termes ; non pédante ; claire ; intuitive ; orale. &lt;/p&gt;
&lt;p&gt;Il est possible de simplifier une définition en itérant dessus. Mais même simplifiées, les définitions restent des cartes difficiles à mémoriser par rapport à des cartes "pratiques".&lt;/p&gt;
&lt;p&gt;Cela tombe bien : l'aspect pratique est ce qui apporte le plus de valeur.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-simplified-flashcard.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;à gauche : une première version précise mais verbeuse&lt;br&gt;à droite : version simplifiée après plusieurs itérations&lt;/div&gt;

&lt;p&gt;Un développeur lit du code et écrit du code.&lt;/p&gt;
&lt;p&gt;Les deux questions les plus importantes sont donc :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Comment lire &lt;em&gt;truc&lt;/em&gt; ? (ex : que renvoi &lt;code&gt;"abc" + "hello"&lt;/code&gt; ?)&lt;/li&gt;
&lt;li&gt;Comment exprimer &lt;em&gt;machin&lt;/em&gt; ? (ex : comment concaténer &lt;code&gt;s1&lt;/code&gt; et &lt;code&gt;s2&lt;/code&gt; ?)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Toujours avec le cas du starred pattern, voici un exemple de bonne carte : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-concrete-flashcard.png"&gt;
Cette carte est bonne car :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la question est concise et tient sur une ligne&lt;/li&gt;
&lt;li&gt;la réponse est directe et tient en quelques mots&lt;/li&gt;
&lt;li&gt;une démonstration est jointe et permet démontrer le résultat rapidement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Takeaways : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fait des cartes avec des exemples concrets.&lt;/li&gt;
&lt;li&gt;Sois concis dans les questions et les réponses, élimine chaque mot inutile.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Erreur #2 : créer trop de cartes d'un coup&lt;/h2&gt;
&lt;p&gt;Au début, je me suis concentré sur la partie facile :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lire&lt;/li&gt;
&lt;li&gt;prendre des notes&lt;/li&gt;
&lt;li&gt;structurer mes notes en cartes de révision &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-structured-note.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;Une note structurée en carte de révision&lt;/div&gt;

&lt;p&gt;Puis, après 3 semaines de lecture, j'ai exporté mes cartes dans Anki. Aïe.&lt;/p&gt;
&lt;p&gt;Je me suis retrouvé avec 800 éléments à mémoriser. D'un coup.  &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-flow-with-stock.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;Oups.&lt;/div&gt;

&lt;p&gt;On peut visualiser le flux de travail de ce projet ainsi : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lire génère des notes&lt;/li&gt;
&lt;li&gt;les notes génère un travail de formatage &lt;/li&gt;
&lt;li&gt;le formatage génère des cartes ANKI à mémoriser&lt;/li&gt;
&lt;li&gt;ne pas mémoriser crée un embouteillage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En concentrant mon énergie sur lire et en délaissant le travail de mémorisation, j'ai créé un grand stock de cartes à mémoriser. Cela a bloqué mon flux de travail, et je n'ai reçu tardivement le feedback que mes cartes étaient étaient trop compliquées, pas assez éprouvées, et trop nombreuses. &lt;/p&gt;
&lt;p&gt;Il s'en est ensuivi un cycle d'un mois dédié à mémoriser, éditer pour simplifier, mettre en pratique pour vérifier, mémoriser, simplifier, mémoriser, simplifier, mémoriser ...  Jusqu'à overdose.&lt;/p&gt;
&lt;p&gt;Takeaways : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mémorise au fil de l'eau.&lt;/li&gt;
&lt;li&gt;La mémorisation n'est pas gratuite. Les cartes de révision encadrent et facilitent le travail de mémorisation, mais cela demande quand même fournir des efforts et du temps. Ne mémorise que l'essentiel. S'il manque une information, tu pourras ajouter une carte plus tard.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Erreur #3 : créer des cartes sur parole&lt;/h2&gt;
&lt;p&gt;Créer une carte pour un truc que tu n'as pas mis en pratique ne sert à rien.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;D'abord parce que l'information ne se fixe pas.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Manipuler un objet permet de créer une connexion avec, de développer une intuition sur son fonctionnement, d'explorer ses fonctionnalités, de se poser des questions. C'est une étape importante du processus d'apprentissage. C'est un lieu commun de le dire, mais je suis tombé dans le piège de dévorer la lecture sans m'arrêter pour pratiquer. Cela donne l'impression d'aller plus vite et de savoir. Mais en fait l'information ne se fixe pas. Et &lt;em&gt;infine&lt;/em&gt;, je ne sais pas faire + de choses.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Ensuite, parce que l'information n'est pas fiable.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;À plusieurs reprises, j'ai trouvé des informations inexactes dans le livre. Je me suis mis à douter de mes cartes. Et j'ai fini par retravailler toutes celles que j'avais créées sans les avoir vérifiées.&lt;/p&gt;
&lt;p&gt;Note que les définitions ne sont pas vérifiables. C'est un problème. Et c'est un argument de plus pour aller vers des cartes pratiques, orientées action. Idem, les affirmations sur le comportement interne sont difficilement vérifiables - même si tu peux regarder le code source de &lt;a href="https://github.com/python/cpython"&gt;CPython&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Takeaways : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lire sans créer ou pratiquer est du gâchis.&lt;/li&gt;
&lt;li&gt;Fait de manipuler et jouer avec la notion une priorité.&lt;/li&gt;
&lt;li&gt;Ne mémorise pas une information que tu n'as pas vérifiée.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="_assets/mp-flow-with-practice.png"&gt;&lt;/p&gt;
&lt;div class="legend"&gt;La version actualisée du workflow&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Les cartes servent à mémoriser ce que tu as appris, pas à apprendre en premier lieu.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Erreur #4 : créer des cartes exotiques&lt;/h2&gt;
&lt;p&gt;Certains modules et certaines méthodes sont exotiques. &lt;/p&gt;
&lt;p&gt;Il est intéressant de savoir qu'ils existent, noter dans quel contexte ils ont été introduits et quels problèmes ils résolvent. Il peut être intéressant les manipuler et noter comment les utiliser sur des cas courants (on parle parfois de &lt;em&gt;recipes&lt;/em&gt;), mais c'est tout. &lt;/p&gt;
&lt;p&gt;Il n'est pas pertinent de mémoriser toutes les utilisations.&lt;/p&gt;
&lt;p&gt;Une solution est de mémoriser &lt;u&gt;just in time&lt;/u&gt; plutôt que &lt;u&gt;just in case&lt;/u&gt; : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prendre des notes&lt;/li&gt;
&lt;li&gt;les tester, les vérifier&lt;/li&gt;
&lt;li&gt;créer des cheat sheets autour de chaque concept&lt;/li&gt;
&lt;li&gt;ne créer des cartes que pour les connaissances que tu viens chercher&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Takeaways : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You ain't gonna need it&lt;/li&gt;
&lt;li&gt;Ne crée pas plus de 3 cartes sur un module exotique&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;En lisant Python in a Nutshell, j'ai beaucoup appris sur le langage, sa syntaxe, et le contenu de la bibliothèque standard. Après avoir passé +200 heures à l'étudier, je me sens plus proche du langage, plus connecté à lui. Je connais mieux ces contours et ce qu'il ne contient pas, ses limites. &lt;/p&gt;
&lt;p&gt;En parallèle de cette lecture, j'ai écouté plusieurs podcasts de Guido Van Rossum, le créateur du langage. C'est aussi une écoute que je recommande pour comprendre la philosophie du langage est ses grandes tendances d'évolution.&lt;/p&gt;
&lt;p&gt;Anki est un outil formidable. Cela fait partie des outils que j'aurais aimé connaître durant ma scolarité. C'est le buddy parfait pour mémoriser de la poésie, des formules de mathématiques, du vocabulaire d'Anglais. &lt;/p&gt;</content></entry><entry><title>Minimalist flashcards with Anki and Obsidian</title><link href="https://me.petitapetit.io/o2anki" rel="alternate"/><published>2025-01-26T00:00:00+01:00</published><updated>2025-01-26T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2025-01-26:/o2anki</id><summary type="html">&lt;p&gt;Over the last 3 months, I've spent 2h/day playing with Anki and Flashcards. &lt;/p&gt;
&lt;p&gt;I'm used to store my notes in Obsidian. With the plugin Obsidian_to_anki, you can write flashcards directly in Obsidian by using some delimiters. The plugin is then able to parse your notes and synchronize the flashcard …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Over the last 3 months, I've spent 2h/day playing with Anki and Flashcards. &lt;/p&gt;
&lt;p&gt;I'm used to store my notes in Obsidian. With the plugin Obsidian_to_anki, you can write flashcards directly in Obsidian by using some delimiters. The plugin is then able to parse your notes and synchronize the flashcard with Anki. so you can study them anywhere (in the bus, waiting at the train station, ...).&lt;/p&gt;
&lt;p&gt;I've ended up with a smooth workflow. But I couldn't get used to the noise that the delimiters create around the cards.&lt;/p&gt;
&lt;p&gt;I didn't find how to customize obsidian2anki the way I wanted. &lt;/p&gt;
&lt;p&gt;So I've coded my solution 👇.&lt;/p&gt;
&lt;h2&gt;Before / after&lt;/h2&gt;
&lt;p&gt;Here is a before/after of the note &lt;code&gt;Booléens (bool).md&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;The note contains metadata (MOC, source, date ...) and four flashcards.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/o2anki-before-after.png"&gt;&lt;/p&gt;
&lt;p&gt;On the left is before. Delimiters &lt;code&gt;STARTI [Basic]&lt;/code&gt;, &lt;code&gt;Back:&lt;/code&gt; and &lt;code&gt;ENDI&lt;/code&gt; create noise and makes the reading experience painful.&lt;/p&gt;
&lt;p&gt;On the right is after :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;we get a lightweight formatting, with juste &lt;code&gt;Q :&lt;/code&gt; and &lt;code&gt;A :&lt;/code&gt; delimiters&lt;/li&gt;
&lt;li&gt;question an answer get one line each. The goal is to keep them short&lt;/li&gt;
&lt;li&gt;I've added empty lines between questions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn't get rid of the &lt;code&gt;&amp;lt;!--ID: XXXX--&amp;gt;&lt;/code&gt; tag. It tells if the note exist in Anki. When I update the deck, if the note exist, I update it rather than creating a new one.&lt;/p&gt;
&lt;p&gt;There is a way to get rid of this tag : you recreate the deck from scratch each time you synchronize it. It's radical because this way you lose information about your progress. But I can do the job.&lt;/p&gt;
&lt;p&gt;Speaking about the logic, here is how it works.&lt;/p&gt;
&lt;h2&gt;Architecture of the program&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="_assets/o2anki-architecture.png"&gt;&lt;/p&gt;
&lt;p&gt;Roughly : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;o2anki&lt;/code&gt; is the orchestrator / the main program &lt;/li&gt;
&lt;li&gt;&lt;code&gt;o2anki&lt;/code&gt; takes in a path to a folder and analyse the &lt;code&gt;*.md&lt;/code&gt; files it contains&lt;/li&gt;
&lt;li&gt;&lt;code&gt;o2anki&lt;/code&gt; maps the files into structured objects (&lt;code&gt;Vault&lt;/code&gt;, &lt;code&gt;File&lt;/code&gt;, and &lt;code&gt;ParsedNote&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;o2anki&lt;/code&gt; updates and creates the notes in Anki through &lt;code&gt;AnkiConnectClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;AnkiConnect&lt;/code&gt; add-on receive the http requests and deals with Anki&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;NotePresenter&lt;/code&gt; class transform the markdown-formatted text into html&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_ids&lt;/code&gt; function writes the &lt;code&gt;&amp;lt;!--ID: XXXX--&amp;gt;&lt;/code&gt; tags in the &lt;code&gt;*.md&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far, I've favored design clarity over efficiency. I consider I lose my time if I try to optimize before I have a perfectly clear understanding of the problem and it's edge-cases. Also, runs I've made until now have been really fast. &lt;/p&gt;
&lt;p&gt;Performance is not a bottleneck at this stage. But understanding the problem and choosing where to start has been.&lt;/p&gt;
&lt;h2&gt;Some tips if you want to run into similar problems&lt;/h2&gt;
&lt;p&gt;If I had to do it again, here are the steps I would follow :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take a few minutes to identify the usecases :&lt;ul&gt;
&lt;li&gt;creating a new basic note&lt;/li&gt;
&lt;li&gt;updating an existing note&lt;/li&gt;
&lt;li&gt;deleting a note&lt;/li&gt;
&lt;li&gt;creating a note that contains an image&lt;/li&gt;
&lt;li&gt;creating a note that contains code&lt;/li&gt;
&lt;li&gt;ignoring files in a given folder&lt;/li&gt;
&lt;li&gt;parsing note with question on several lines&lt;/li&gt;
&lt;li&gt;parsing note with answer on several lines&lt;/li&gt;
&lt;li&gt;parsing note followed by some unrelated text&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Consider the &lt;a href="https://addon-docs.ankiweb.net/the-anki-module.html"&gt;anki module&lt;/a&gt;. AnkiConnect looks like an unnecessary level of abstraction for this use-case. Using it reminds me a bit of &lt;a href="https://www.postgresql.org/message-id/36cffb61-3912-915c-4933-3bcd9cac063a%40dndg.it"&gt;the over-engineering story behind psycopg&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If AnkiConnect is the solution, then start by playing with the api manually (see some &lt;a href="https://github.com/petitapetitio/o2anki/blob/92715d486b4bd91fc7901474d6078a9200d48de9/tests_exploration/test_anki_connect_client_api.py"&gt;example of exploratory commands with raw json body&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Export some notes created with &lt;a href="https://github.com/ObsidianToAnki/Obsidian_to_Anki"&gt;Obsidian_to_Anki&lt;/a&gt; in order to grasp their formatting : &lt;code&gt;Anki &amp;gt; Browse &amp;gt; Select a card &amp;gt; Right Click &amp;gt; Notes &amp;gt; Export notes ... &amp;gt; Notes in plain text &amp;gt; Export ...&lt;/code&gt; (see : &lt;a href="https://github.com/petitapetitio/o2anki/blob/92715d486b4bd91fc7901474d6078a9200d48de9/assets/with-code.txt"&gt;export a card with code&lt;/a&gt;, &lt;a href="https://github.com/petitapetitio/o2anki/blob/92715d486b4bd91fc7901474d6078a9200d48de9/assets/with-image.txt"&gt;export of a card with an image&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Implement the parsing in TDD using tailor-made files (see : &lt;a href="https://github.com/petitapetitio/o2anki/blob/92715d486b4bd91fc7901474d6078a9200d48de9/tests/test_parsing_files.py"&gt;testing the parsing of a single file&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Code the orchestration (&lt;a href="https://github.com/petitapetitio/o2anki/blob/92715d486b4bd91fc7901474d6078a9200d48de9/o2anki/o2anki.py"&gt;o2anki&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Manage a deck with this solution for a few days to test it!&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;I've implemented a limited set of features. Just what I need. And I like the simplicity it makes. &lt;/p&gt;
&lt;p&gt;I've planned to add some features, like adding DELETE: ID tag handling, adding css and code_hilite in code blocs. But my intent is to keep it focus. Complexity bumps when you try to do everything and please everybody.&lt;/p&gt;
&lt;p&gt;If you want to have a look at the code : https://github.com/petitapetitio/o2anki&lt;/p&gt;</content></entry><entry><title>Bilan de ma phase de vie à Rouen</title><link href="https://me.petitapetit.io/rouen" rel="alternate"/><published>2024-12-31T00:00:00+01:00</published><updated>2024-12-31T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-31:/rouen</id><summary type="html">&lt;p&gt;Je viens de passer 4 mois à Rouen (02/09/24 → 03/01/25).&lt;/p&gt;
&lt;p&gt;C'est la première fois que j'habite seul, c'est-à-dire pas en famille, pas en colocation, et pas en couple non plus. &lt;/p&gt;
&lt;p&gt;Je prévois de voyager. Prendre un appartement dans la ville où j'ai grandi était une façon …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Je viens de passer 4 mois à Rouen (02/09/24 → 03/01/25).&lt;/p&gt;
&lt;p&gt;C'est la première fois que j'habite seul, c'est-à-dire pas en famille, pas en colocation, et pas en couple non plus. &lt;/p&gt;
&lt;p&gt;Je prévois de voyager. Prendre un appartement dans la ville où j'ai grandi était une façon de vérifier que vivre seul me va ; avant de le faire à l'autre bout du monde.&lt;/p&gt;
&lt;p&gt;Voici un bilan de cette phase de vie : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comment j'ai vécu (mon organisation générale, mon alimentation)&lt;/li&gt;
&lt;li&gt;ce que j'ai aimé&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Emménagement&lt;/h1&gt;
&lt;p&gt;J'ai passé l'été à me balader à droite à gauche avec mes affaires. Pour la rentrée, ma priorité était de retrouver un environnement stable où je pourrais travailler à mon rythme. Aussi, je voulais minimiser le temps passé à emménager et à équiper le lieu. &lt;/p&gt;
&lt;p&gt;J'ai pris un meublé. &lt;/p&gt;
&lt;p&gt;Mon emménagement a pris 2h. &lt;/p&gt;
&lt;p&gt;Wow. &lt;/p&gt;
&lt;p&gt;C'est tellement agréable quand les choses sont simples.&lt;/p&gt;
&lt;h1&gt;Monk mode&lt;/h1&gt;
&lt;p&gt;J'ai profité d'être seul pour expérimenter un rythme strict. &lt;/p&gt;
&lt;p&gt;Voici une semaine typique : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/rouen-week.jpg"&gt;&lt;/p&gt;
&lt;p&gt;En général : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mon réveil sonne à 6h&lt;/li&gt;
&lt;li&gt;je me lève vers 6h15&lt;/li&gt;
&lt;li&gt;je fais ma routine pompes/abdos&lt;/li&gt;
&lt;li&gt;je prends un café&lt;/li&gt;
&lt;li&gt;je commence à travailler sur mon projet de mémoriser la Référence de Python&lt;/li&gt;
&lt;li&gt;vers 9h je fais 15 minutes d'étirements&lt;/li&gt;
&lt;li&gt;je prends un 2ème un café&lt;/li&gt;
&lt;li&gt;ma mission commence à 9h30 avec un daily meeting&lt;/li&gt;
&lt;li&gt;en fin de matinée, je passe 15 minutes sur LinkedIn. Je like et je commente.&lt;/li&gt;
&lt;li&gt;je déjeune en 30 minutes puis je vais marcher 15 minutes.&lt;/li&gt;
&lt;li&gt;après déjeuner, je consacre du temps à écrire (mais cette heure est souvent peu productive)&lt;/li&gt;
&lt;li&gt;je vais à la salle vers 18h30&lt;/li&gt;
&lt;li&gt;quand je rentre, je prends ma douche, je dîne, je médite 20 minutes, et je me couche avec un roman&lt;/li&gt;
&lt;li&gt;je ne sors pas le soir en semaine&lt;/li&gt;
&lt;li&gt;le samedi je travail à 100% sur mes projets perso&lt;/li&gt;
&lt;li&gt;le dimanche matin, je travaille 2h et je consacre 2h à du journaling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Après 3 mois, j'ai l'impression de ne pas avoir tant avancé sur mes projets. &lt;/p&gt;
&lt;p&gt;En regardant mon agenda de façon rétrospective, je me dis : c'est dense, mais ça manque de focus. &lt;/p&gt;
&lt;h3&gt;Tracker ses journées&lt;/h3&gt;
&lt;p&gt;J'ai pris l'habitude de tracker mes journées. Et wow. Il y a un écart immense entre : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ce que je prévoie de faire&lt;/li&gt;
&lt;li&gt;ce que j'ai l'impression d'avoir fait&lt;/li&gt;
&lt;li&gt;ce que j'ai vraiment fait&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tracker m'aide à en prendre conscience et à accepter la réalité / à faire des ajustements.&lt;/p&gt;
&lt;h3&gt;Faire des pauses régulières&lt;/h3&gt;
&lt;p&gt;J'ai testé de travailler en pomodoro avec Brain.fm : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je coupe mes notifications&lt;/li&gt;
&lt;li&gt;je me fixe un objectif de travail (une intention)&lt;/li&gt;
&lt;li&gt;je lance une session de 25 minutes sur brain.fm&lt;/li&gt;
&lt;li&gt;lorsque la musique s'arrête, je ferme les yeux et médite 4'&lt;/li&gt;
&lt;li&gt;puis je renouvelle mon intention et repars sur un cycle&lt;/li&gt;
&lt;li&gt;je prends une pause longue toutes les 2h&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C'est top pour éviter de se cramer et pour éviter de dériver. Toutes les 30 minutes, je lève le nez. Est-ce que je suis toujours &lt;em&gt;on-track&lt;/em&gt; ou est-ce que je me suis égaré ? Quel est mon objectif pour les 25 prochaines minutes ? &lt;/p&gt;
&lt;p&gt;Le problème par contre est que je perds facilement le momentum. Lorsque je prends une pause de 5 minutes, si je me lève pour aller au toilettes, prendre un verre d'eau, ouvrir les rideaux, ... c'est foutu, je perds 10 minutes, et je n'avance pas très vite. &lt;/p&gt;
&lt;p&gt;Sur certaines activités comme écrire, j'aime me perdre dans mes pensées, naviguer dans mes notes, et ne pas mettre de timer.&lt;/p&gt;
&lt;p&gt;Lorsque j'ai mis en place la section commentaires sur mon site web, j'ai travaillé sans interruption pendant presque 3h tellement j'étais absorbé par ce que je faisais. &lt;/p&gt;
&lt;p&gt;Pour la suite, je vais : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mieux préparer mes sessions (pour faire 4 pomo sans me lever)&lt;/li&gt;
&lt;li&gt;tester des alternatives (ex : 45' de travail, 15' de pause avec des exercices)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Alimentation&lt;/h1&gt;
&lt;p&gt;L'alimentation a un impact majeur sur ton niveau d'énergie et de concentration.&lt;/p&gt;
&lt;p&gt;Je n'y connais pas grand chose en alimentation, et je n'ai pas envie d'y passer trop de temps. Aussi, j'ai essayé de m'en tenir à quelques principes de base. J'ai composé une alimentation autour de produits &lt;strong&gt;non transformés&lt;/strong&gt;, &lt;strong&gt;rapide à préparer&lt;/strong&gt;, et en éliminant les sources de sucre.&lt;/p&gt;
&lt;p&gt;Le midi : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Une salade composée d'ingrédients parmi mâche, avocats, falafels, tomates, champignons, olives, oignons, feta, mozzarella, noix, huile d'olives, vinaigre balsamique&lt;/li&gt;
&lt;li&gt;Un fruit parmi pomme, poire, ananas, mangue, raisin, banane&lt;/li&gt;
&lt;li&gt;Une barre de chocolat noir à 98%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le soir : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Une tranche de pain de mie avec du beurre et du gruyère en apéro&lt;/li&gt;
&lt;li&gt;Une demi tarte&lt;/li&gt;
&lt;li&gt;De la salade&lt;/li&gt;
&lt;li&gt;Un fruit / du fromage blanc avec une pomme ou une banane&lt;/li&gt;
&lt;li&gt;Un bol de protéines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Composition des tartes : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;concentré de tomates, coppa, coeurs d'artichaut, mozza, olives, noix&lt;/li&gt;
&lt;li&gt;oeufs, bleu, noix&lt;/li&gt;
&lt;li&gt;oeufs, lardons, oignons, noix&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Je mange trop le soir)&lt;/p&gt;
&lt;p&gt;J'ai tendance à me jeter sur les trucs sucrés comme un fou. J'adore les patisseries, les glaces, la brioche avec du nutella, les granola. L'enfer. &lt;/p&gt;
&lt;p&gt;Ma stratégie repose à 100% sur ne pas m'exposer. Je fais mes courses en ligne et une fois par semaine. Je n'achète pas de chocolat, pas de yaourts, pas de nutella, pas de granola - et la vie est facile.&lt;/p&gt;
&lt;p&gt;Il est plus simple de designer son environnement pour orienter le courant dans la direction qui te convient plutôt que de nager à contre-courant en permanence.&lt;/p&gt;
&lt;h1&gt;Ce que j'ai aimé&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;avoir du temps seul&lt;/li&gt;
&lt;li&gt;passer du temps avec mon frère&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/sf"&gt;aller trop loin dans mes routines de sport&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/vernis"&gt;porter du vernis pendant 30 jours&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/leizup"&gt;tester les apps de rencontres amicales&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/ar"&gt;tester les apps de rencontres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://petitapetit.io/dbz"&gt;relire tout Dragon Ball&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mon appartement - grand, lumineux, et plutôt calme&lt;/li&gt;
&lt;li&gt;chanter avec mes écouteurs en marchant à la salle &lt;/li&gt;
&lt;li&gt;chanter en mode karaoke sur Spotify. OMG. C'est un exercice de respiration relaxant.&lt;/li&gt;
&lt;li&gt;prendre du temps pour moi le hammam et les massages &lt;/li&gt;
&lt;li&gt;prendre du temps pour moi en m'occupant de mes rendez-vous médicaux (lunettes, médecin, prises de sang, ...)&lt;/li&gt;
&lt;li&gt;me promener sur les quais, dans la brume, les lumières de la foire en fond&lt;/li&gt;
&lt;li&gt;explorer la ville : monter en haut du Gros-Horloge, faire un bowling au Snooker, danser au Coco Bongo, assister à des Standups au PUBLIC et au Fridge, voir du burlesque aux Mots Éphémères.&lt;/li&gt;
&lt;li&gt;tester des restos : Yulin, le XXI, Chiba, Chez Huri, Racine 14, Resto Viet au SQUARE HENRI IV, Brasserie Gil, Bistrot d'Arthur, Alaje, Lolo et Zaza, Nhà, Hai Ba, Le Bouillon Popote, Papa Tom, H8, Pintxos, L'Entrepôt Food Hall, Pepe, le Jeanette, le café de l'Hôtel Littéraire Gustave Flaubert.&lt;/li&gt;
&lt;li&gt;tout faire à pied (à pieds &amp;gt; vélo &amp;gt;&amp;gt; métro &amp;gt;&amp;gt;&amp;gt; voiture)&lt;ul&gt;
&lt;li&gt;j'ai pris un cours de Bachata au Coco Bongo : 5 min à pieds&lt;/li&gt;
&lt;li&gt;je suis allé plusieurs fois au hammam : 6 minutes à pieds&lt;/li&gt;
&lt;li&gt;j'ai pris un abonnement dans une salle de sport lumineuse et avec une jolie vue : 10 min à pied&lt;/li&gt;
&lt;li&gt;je suis allé 8 fois au standup : 5 min à pieds&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Ce que j'ai appris sur moi&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;J'ai aimé cette phase de vie et vivre seul et me va bien.&lt;/li&gt;
&lt;li&gt;Sortir avec des amis, faire de nouvelles rencontres, prendre des risques me donne de l'énergie&lt;/li&gt;
&lt;li&gt;Construire des choses et m'exprimer me donne de l'énergie &lt;/li&gt;
&lt;li&gt;Même quand personne ne me dérange, je passe beaucoup de temps à faire autre chose que travailler, comme faire des étirements, regarder par la fenêtre, faire une machine, faire un café. Je ne suis pas si productif que ça.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Dernière note : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vivre dans un quartier bruyant a un immense impact sur le sommeil. Dormir avec boules Quies est désagréable (en particulier avec des acouphènes). Le fait de s'attendre à être réveillé flingue ma qualité de sommeil.&lt;/li&gt;
&lt;li&gt;je me suis rendu compte 2 semaine avant de déménager que mes voisins sont hyper sympas. La prochaine fois, aller les voir + tôt.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Photos&lt;/h1&gt;
&lt;div class="pictures-grid"&gt;
    &lt;a href="_assets/rouen-1.jpg"&gt;&lt;img src="_assets/rouen-1-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-2.jpg"&gt;&lt;img src="_assets/rouen-2-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-3.jpg"&gt;&lt;img src="_assets/rouen-3-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-4.jpg"&gt;&lt;img src="_assets/rouen-4-mini.jpg"/&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="pictures-grid"&gt;
    &lt;a href="_assets/rouen-5.jpg"&gt;&lt;img src="_assets/rouen-5-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-6.jpg"&gt;&lt;img src="_assets/rouen-6-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-7.jpg"&gt;&lt;img src="_assets/rouen-7-mini.jpg"/&gt;&lt;/a&gt;
    &lt;a href="_assets/rouen-8.jpg"&gt;&lt;img src="_assets/rouen-8-mini.jpg"/&gt;&lt;/a&gt;
&lt;/div&gt;</content></entry><entry><title>Les apps de rencontre (épisode 1)</title><link href="https://me.petitapetit.io/ar" rel="alternate"/><published>2024-12-29T00:00:00+01:00</published><updated>2024-12-29T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-29:/ar</id><summary type="html">&lt;p&gt;J'ai testé 3 apps de rencontre.&lt;/p&gt;
&lt;p&gt;Voici 4 anecdotes amusantes et quelques idées pour la suite.&lt;/p&gt;
&lt;p&gt;Comme le dit si bien Thomas Edison :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je n'ai pas échoué. J'ai juste trouvé 10,000 façons qui ne fonctionnent pas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Anecdote #1 - Cercle social&lt;/h2&gt;
&lt;p&gt;Un pote que je n'ai pas vu depuis 10 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;J'ai testé 3 apps de rencontre.&lt;/p&gt;
&lt;p&gt;Voici 4 anecdotes amusantes et quelques idées pour la suite.&lt;/p&gt;
&lt;p&gt;Comme le dit si bien Thomas Edison :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je n'ai pas échoué. J'ai juste trouvé 10,000 façons qui ne fonctionnent pas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Anecdote #1 - Cercle social&lt;/h2&gt;
&lt;p&gt;Un pote que je n'ai pas vu depuis 10 ans fête son anniversaire dans un food hall. &lt;/p&gt;
&lt;p&gt;J'y vais. Et là, wow. Je recroise 20 personnes que je n'avais pas revues depuis le lycée. &lt;/p&gt;
&lt;p&gt;Je click sur une amie. &lt;/p&gt;
&lt;p&gt;Le lendemain, je lui envoie un message pour lui proposer de prendre un verre. Chaque message que j'écris me jette dans un ascenseur émotionnel. J'écris mes deux lignes. Je me fais tout un tas de films. Je mets 30 minutes à appuyer sur envoyer. La pression monte. Enfin, je me lance. Un pic de chaleur m'envahit. Puis tout redescend. Ensuite, je suis détendu. C'est comme la Tour de la Terreur de Disney Studio. J'ai si peur du rejet. Mais la seule façon d'échouer, c'est de ne pas essayer.&lt;/p&gt;
&lt;p&gt;Elle propose que l'on se voie avec une autre amie du Lycée. Ce ne sera pas un rencard donc. Mais ça me branche, je suis curieux d'avoir de leurs nouvelles.&lt;/p&gt;
&lt;p&gt;On se retrouve sur les quais. Lorsqu'elle arrive ma première pensée est "mais pas du tout". La perspective amicale me convient. On passe une bonne soirée. Je ressens une affection simple et sincère pour elles. Au final, je sens plus d'affinité avec mon autre amie. Mais elle est en couple et vient d'avoir un enfant.&lt;/p&gt;
&lt;h2&gt;Anecdote #2 - OK Cupid&lt;/h2&gt;
&lt;p&gt;Un ami me recommande OkCupid. Il l'utilise régulièrement et a fait plein de rencontres dessus.&lt;/p&gt;
&lt;p&gt;J'installe l'application. &lt;/p&gt;
&lt;p&gt;L'app propose de faire des rencontres basées sur les affinités. On peut répondre à plein de questions. Je passe 2h à répondre à des questions. C'est clairement une façon de prendre un chemin facile et d'esquiver l'espace de rencontre. &lt;/p&gt;
&lt;p&gt;Après 2h d'"introspection", je m'aventure plus loin dans l'app. Wow, j'ai déjà plein de likes ! Il faut souscrire à un abonnement pour les voir. C'est ma première expérience sur une app. Je sens l'entourloupe, mais je suis naïf, il est tard, et je suis prêt à payer 15€ la leçon. &lt;/p&gt;
&lt;p&gt;Je souscris une formule premium. Je regarde les likes : que des femmes à +200 km de chez moi. Duuuuuuuuuuuuuuuuude ! Tu plaisantes ??&lt;/p&gt;
&lt;p&gt;Le lendemain, je swipe sur l'app. J'ai fait le tour au bout de 5 profils. &lt;/p&gt;
&lt;p&gt;Mon pote vit sur Tour. &lt;/p&gt;
&lt;p&gt;Il n'y a personne sur Rouen. &lt;/p&gt;
&lt;h2&gt;Anecdote #3 - Tinder&lt;/h2&gt;
&lt;p&gt;Cette fois, je décide de jouer une valeur sûre. Tinder compte parmi les apps de rencontre les plus populaires. Sur cette plateforme, je suis certain qu'il y aura du monde.&lt;/p&gt;
&lt;p&gt;Les profils sont plus succincts que sur OkCupid. Je complète le mien et je commence à swiper. &lt;/p&gt;
&lt;p&gt;Les gens qui n'ont pas beaucoup d'images et qui mettent des photos d'eux avec leur ex flouté me tuent.&lt;/p&gt;
&lt;p&gt;Ma première stratégie est de m'investir 30 minutes par jour, d'être régulier sans être pressé. Le midi, je regarde les profils, les photos, la bio. Je me demande : est-ce que j'ai envie de rencontrer cette personne ? Je décline ceux qui ne collent pas. Je sélectionne uniquement lorsque je pense que ça pourrait le faire. Après 10 jours : zéro match. Mdr. OK, essayons autre stratégie.&lt;/p&gt;
&lt;p&gt;Ma deuxième stratégie est de liker tout le monde. Pendant 5 minutes, j'envoie tous les profils à droite. Après 10 jours, j'ai quelques matches, mais ce n'est pas la folie. La lassitude me gagne.&lt;/p&gt;
&lt;h2&gt;Anecdote #4 - F*ck Friends&lt;/h2&gt;
&lt;p&gt;Sur Tinder, 95% des profils cherchent une relation sérieuse. Je cherche l'opposé : des relations pas sérieuses. 80% des profils que je vois ont -25 ans. Je cherche des gens de mon âge, pas des enfants.&lt;/p&gt;
&lt;p&gt;Un soir, en désespoir de cause, je pars en quête d'une autre app.&lt;/p&gt;
&lt;p&gt;Qu'est-ce qui pourrait être amusant ? &lt;/p&gt;
&lt;p&gt;Et si, je m'inscrivais sur une plateforme de cougars ?&lt;/p&gt;
&lt;p&gt;Explorons.&lt;/p&gt;
&lt;p&gt;Je fais quelques recherches en ligne. Je crée un compte sur JM Cougars. Je me retrouve en même temps inscrit à F*ck Friends et CaféCoquin. Il y a une offre d'essai premium à 2€ pour deux semaines. Je souscrit. L'interface utilisateur est claquée au sol. Le site ne m'inspire aucune confiance. Je fais l'hypothèse que 75% sont des faux profils. &lt;/p&gt;
&lt;p&gt;Le lendemain matin, j'ai les idées plus claires. Je n'ai pas envie d'investir du temps et de l'énergie sur ce type de plateforme. Ça ressemble vraiment à un endroit où les mecs vont lorsqu'ils sont désespérés. Je cherche à me désinscrire. &lt;/p&gt;
&lt;p&gt;Le mécanisme de désinscription tordu. Il n'est possible de résilier que pendant les 3 jours avant le renouvellement. Vous voulez jouer à ça ? OK. Je mets un rappel dans mon agenda. Le jour J, la plage de désinscription s'est décalée. J'envoie un message au service client. "On ne peut rien faire blablabla". Dudes, vous me faites perdre mon temps avec vos conneries.&lt;/p&gt;
&lt;p&gt;Je bloque les paiements internet de ma carte.&lt;/p&gt;
&lt;p&gt;Cela m'aura permis de trier mes paiements récurrents.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Si je devais réécrire les slogans de ces apps pour refléter l'expérience que j'en ai eu, ça donnerait : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OK Cupid : matcher des femmes à l'autre bout du monde&lt;/li&gt;
&lt;li&gt;Tinder : voir des gens sans pouvoir leur parler&lt;/li&gt;
&lt;li&gt;F*ck Friends : on baise surtout ta CB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et je n'essaie pas de rejeter la responsabilité. C'est juste du feedback. Je n'ai pas encore cracké le truc, mais il n'y a pas de raison que je n'y parvienne pas.&lt;/p&gt;
&lt;p&gt;Quelques pistes pour la suite : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;dire oui à tout&lt;/strong&gt; - quitte à en rester à une rencontre amicale. Quand tu est submergé, il est pertinent d'élever la barre : si ce n'est pas un oui franc, dis non par défaut (&lt;a href="https://sive.rs/n"&gt;Hell Yeah, or No&lt;/a&gt;). Mais lorsque tu débutes, lorsque tu es en manque d'opportunités, dis oui à tout. Chaque opportunité est un ticket de loterie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;faire de belles photos&lt;/strong&gt;. Sur les apps de rencontre, tout passe par les photos et beaucoup se joue au niveau de l'inconscient. Un de mes principes d'écriture favoris est "Show, don't tell". Il permet de rendre un texte plus vivant, plus convaincant, plus émotionnel. Appliqué à un profil, cela donne "J'aime le sport" → "Je vais à la salle 4x/semaine" → "Une photo de toi à la salle". Ou encore "J'aime l'aventure" → "J'ai descendu l'orne en canoë en 8 jours" → "Une photo de toi en treck Canoë". Tell → Show (avec des mots) → Show (avec une photo).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;passer par des entremetteurs&lt;/strong&gt;. Les potes de tes potes sont des personnes pré-validées. Si tu as des affinités avec eux, il est probable que tu aies aussi des affinités avec leurs amis. Et ça marche dans les deux sens. C'est l'approche la moins aventureuse, mais c'est une option.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;collaborer avec des personnes que tu serais susceptible de dater&lt;/strong&gt;. Je ne suis pas sûr de cette idée, mais je la pose quand même ici. Les relations professionnelles sont avant tout des relations sociales. Des personnes qu'on rencontre par le travail deviennent des amis. Et inversement, les amis sont vecteurs d'opportunités pro. On aime filer le boulot aux personnes qu'on aime bien. Quand tu cherches un service comme un shooting, un tatouage, un cours d'anglais, choisis une personne que tu serais susceptible de rencarder. Et si la relation reste professionnelle, c'est OK. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;développer son narcissisme&lt;/strong&gt;. Nous sommes attirés comme des aimants par les personnes qui ont confiance en eux. La confiance en soi est un effet de bord de s'aimer soi-même, sincèrement et profondément. Problème : mon estime de moi est basse. Je ne m'aime pas. Je ne me déteste pas non plus. Mais je ne m'aime pas &lt;u&gt;sincèrement et profondément&lt;/u&gt;. Et ça, c'est un problème qui mérite de s'y attarder. Ce point sera sans doute un de mes focus pour 2025.&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>6 expériences de rencontres amicales</title><link href="https://me.petitapetit.io/leizup" rel="alternate"/><published>2024-12-27T00:00:00+01:00</published><updated>2024-12-27T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-27:/leizup</id><summary type="html">&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;Septembre 2024. &lt;/p&gt;
&lt;p&gt;Je sors d'une relation de 4 ans dans laquelle je me suis isolé. Je suis de retour sur Rouen, le coin où j'ai grandi. J'ai envie de rencontrer du monde, de développer mes compétences sociales. &lt;/p&gt;
&lt;p&gt;J'ai deux options : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reprendre contact avec mes connaissances du lycée, de la …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;Septembre 2024. &lt;/p&gt;
&lt;p&gt;Je sors d'une relation de 4 ans dans laquelle je me suis isolé. Je suis de retour sur Rouen, le coin où j'ai grandi. J'ai envie de rencontrer du monde, de développer mes compétences sociales. &lt;/p&gt;
&lt;p&gt;J'ai deux options : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reprendre contact avec mes connaissances du lycée, de la fac&lt;/li&gt;
&lt;li&gt;Aller de l'avant et rencontrer de nouvelles personnes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;J'aime l'idée d'aller de l'avant.&lt;/p&gt;
&lt;p&gt;Mon psy me parle de Leizup : une application de rencontres amicales. Le concept m'intrigue. Aller.&lt;/p&gt;
&lt;p&gt;Je me fixe un objectif simple :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Participer à 4 activités&lt;/li&gt;
&lt;li&gt;Organiser 4 activités&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Participer à des activités&lt;/h1&gt;
&lt;p&gt;En avant l'aventure !&lt;/p&gt;
&lt;p&gt;&lt;u&gt;2024-09-14. Activité #1 : Atelier de méditation&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Pour ma première sortie, j'ai participé à un atelier de méditation avec Alain et Dhagpo Rouen. J'ai reçu un super accueil, et on m'a présenté deux images intéressantes. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;La mèche&lt;/strong&gt;. L'entrainement à la méditation permet d'allonger la mèche du bâton de dynamite. Tu n'exploses pas à la première étincelle. Tu as même une chance d'éteindre la mèche - si tu le souhaites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le verre d'eau et les sédiments&lt;/strong&gt;. Imagine un verre d'eau rempli de sédiments et qu'on aurait agité comme une boule de noël. Un esprit agité ressemble à cela. Méditer est une façon de faire le calme, de laisser retomber les sédiments, et de gagner en clarté mentale.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;u&gt;2024-09-21. Activité #2 : Atelier d'écriture&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Pour ma deuxième sortie, je me suis inscrit à un atelier d'écriture. C'était au labo Victor Hugo, un ancien bâtiment des beaux-arts. C'était organisé par Marie, une Clown Chamane d'une trentaine d'année. Et il y avait Henry (jardinier), Christine (du sud), Christelle, Jersey (des Cornouailles), Victor (bloqué dans l'écriture d'un roman), Gislaine, Sophie, Jean-lou, Helena, Mélanie, Faustine, Helina, Katie.&lt;/p&gt;
&lt;p&gt;Le programme était inattendu. Plus qu'on atelier d'écriture, c'était un atelier d'expression. Et surtout d'expression vocale. Il y a eu de la gêne, des regards sceptiques, mais tout le monde ajouté le jeu. &lt;/p&gt;
&lt;p&gt;J'en retiens que &lt;strong&gt;le corps est un lieu de passage, pas de stockage&lt;/strong&gt;. Les émotions sont faites pour circuler. Pour cela, tu peux les &lt;strong&gt;donner à ta voix&lt;/strong&gt; : souffler, laisser des sons sortir de ta voix, des rires, des notes, ce qui vient.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;2024-09-29. Activité #3 : Poulet-Frite du dimanche&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Pour ma troisième, je me suis inscrit à une sortie au restaurant. &lt;/p&gt;
&lt;p&gt;C'était avec Valérie, Véronique et Karthik. Et c'était l'occasion de découvrir le Quartier Libre. &lt;/p&gt;
&lt;p&gt;Je suis fasciné par ces lieux constitués de bric et de broc. Ils semblent émerger de rien. Il y a différents espaces. C'est convivial. J'aime beaucoup l'atmosphère.&lt;/p&gt;
&lt;p&gt;Ce type de sortie est axé sur les conversations. &lt;/p&gt;
&lt;p&gt;Socialement, ce n'était pas un match. Mais je suis ressorti content de l'expérience. C'était sans enjeu. Lorsque l'on m'a posé des questions, j'ai essayé de m'ouvrir. Personne n'a vraiment creusé, mais j'ai répondu aux questions et ai développé afin de poser des éléments de relance.&lt;/p&gt;
&lt;p&gt;Ce type de rencontre est une bonne occasion de travailler ses accroches. Quand ton accroche n'est pas bonne, tu perds l'attention du groupe, et tu ne peux pas terminer ton histoire.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;2024-10-01. Activité #4 : Friends dating&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Le concept est de passer 5-6 minutes avec chaque personne. Des questions d'ouverture sont posées sur les tables afin de lancer des sujets de conversation.&lt;/p&gt;
&lt;p&gt;C'était à l'Entrepôt Food Hall, un lieu de vie du nouveau Rouen. C'était organisé par Yoan, le co-fondateur de Leizup. Et il y avait Véro, Steph, Silvie, Sandie, Valérie. &lt;/p&gt;
&lt;p&gt;Que des femmes ! &lt;/p&gt;
&lt;p&gt;De plus de 50 ans.&lt;/p&gt;
&lt;p&gt;Ce soir, je me suis rendu compte que : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l'âge compte&lt;/li&gt;
&lt;li&gt;la culture compte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mes conversations avec Yoan étaient simples, fluides. Il a mon âge, il aime l'entrepreneuriat. On avait des affinités. &lt;/p&gt;
&lt;p&gt;Je n'ai eu aucun mal à converser 4-5 minutes avec chaque personne. Mais je n'ai pas réussi à connecter. Je n'ai pas trouvé d'intersection dans nos besoins. &lt;/p&gt;
&lt;p&gt;&lt;u&gt;2024-10-03 - Point d'étape&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;La dernière soirée m'a plombée le moral. J'ai ressenti de la lassitude à rencontrer des gens avec qui je ne ressens pas l'envie de construire des choses. Rencontrer des gens random m'a amusé un moment. Il est temps d'ajuster ma stratégie. &lt;/p&gt;
&lt;p&gt;Alors que je broie du noir, il me revient cette phrase de Marshall Rosenberg : &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La dépression est la récompense que nous obtenons pour notre conformité. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Fuck it. Je vais tenter des trucs + osés.&lt;/p&gt;
&lt;p&gt;Je regarde la liste des évènements proposés.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Soirée Bachata. Cela me fait envi autant que ça me flipper. Let's go. &lt;/li&gt;
&lt;li&gt;Soirée Célibataires. Je trouve ça gênant et en même temps ça me fait envie. C'est exactement l'intersection que je recherche. Let's go.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;u&gt;2024-10-05. Activité #5 : Soirée Bachata&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Wooow.&lt;/p&gt;
&lt;p&gt;J'ai adoré.&lt;/p&gt;
&lt;p&gt;Le contact, le mouvement, l'ambiance générale. Les sourires, les déconvenues. Je me suis senti vivant, joyeux. J'étais souriant toute la soirée.&lt;/p&gt;
&lt;p&gt;Mon programme d'activité est déjà bien chargé, mais je note que la danse est définitivement un truc que j'ai envie d'explorer davantage.&lt;/p&gt;
&lt;p&gt;~~&lt;u&gt;2024-10-12. Activité #6 : Soirée célibataire&lt;/u&gt;~~&lt;/p&gt;
&lt;p&gt;L'organisateur a annulé au dernier moment. Je n'ai pas insisté.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;2024-11-03. Activité #6 : Atelier d'écriture&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Avec Henry, Aurélien, Paulyne, Anne-Marie, Sun. Au Jehanne, le bar du Radisson Blu, un dimanche après-midi.&lt;/p&gt;
&lt;p&gt;Le Jehanne est un des bars de Rouen que j'affectionne le plus. C'est cosy, lounge. L'ambiance est feutrée. Il y a de la moquette au sol, le lieux est calme et le service est discret. J'y vais rarement, mais je m'y sens bien.&lt;/p&gt;
&lt;p&gt;On a fait trois exercices d'écriture créative autour d'un café. Chaque fois avec la même structure :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;proposition de sujet &lt;/li&gt;
&lt;li&gt;10-20 minutes d'écriture &lt;/li&gt;
&lt;li&gt;tour de table de restitution&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Exercice 1 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;chacun tire un mot au hasard (ici pris dans un jeu Codenames)&lt;/li&gt;
&lt;li&gt;le but est de placer tous les mots tirés dans une histoire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Exercice 2 : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tu es dans une galerie. Un évènement survient&lt;/li&gt;
&lt;li&gt;Racontes l'histoire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'ai passé un super moment. Chacun a son style : baroque / minimaliste, premier degré / 10-ème degré, descriptif / profond. J'ai eu du mal à lire mes textes. Et en même temps, c'était libérateur de partager des choses écrites de façon spontanées, sans trop de contrôle. J'ai aussi trouvé cela nourrissant d'écouter les textes des autres. On a reparlé de principes d'écriture comme "Show, don't tell". J'ai utilisé la trame narrative la plus simple au monde : planter le décor, dérouler une histoire, terminer. Et j'ai bien rigolé.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;J'ai été investi dans cette démarche un mois. Je n'ai pas obtenu les résultats que je cherchais (amis, rencards), mais j'ai vécu des expériences amusantes. &lt;/p&gt;
&lt;p&gt;Mi-octobre, j'ai fait évoluer ma stratégie : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;D'abord, j'ai priorisé de sortir avec mon frère. On s'est fait des standups, on est allé au cabaret, on a fait de la Kizomba, on a testé des restos. On a parlé de nos vies, de nos aspirations, de nos projets. Ce n'était pas un vecteur de rencontre, mais c'était hyper cool.&lt;/li&gt;
&lt;li&gt;J'ai testé de reprendre contact avec des connaissances. Cela m'a fait sincèrement plaisir de revoir certaines personnes. Je me suis souvenu pourquoi je n'avais pas envie d'en revoir d'autres. Globalement, il n'y a pas eu de clic.&lt;/li&gt;
&lt;li&gt;J'ai testé les apps de rencontre - mais ça, c'est une autre histoire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cette deuxième stratégie a eu pour effet de m'isoler à nouveau. J'envisage donc de revenir à la première - avec ce que j'ai appris en cours de route.&lt;/p&gt;
&lt;h2&gt;Tips&lt;/h2&gt;
&lt;p&gt;&lt;u&gt;Cibler les personnes&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Me lancer à l'aveugle m'amusait. Et puis cela réduisait l'enjeu. Mais, avec le recul, je me rends compte que l'âge des gens est important pour moi. La culture aussi. Pour les prochaines fois, dans la mesure du possible, je viserais des sorties avec des personnes de mon âge (+/- 10, mais pas +/- 20).&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Cibler les activités&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Pour qu'une activité soit tenable dans le temps, il faut qu'elle soit rentable.&lt;/p&gt;
&lt;p&gt;C'est incontournable pour les activités économiques. C'est aussi un peu vrai pour les activités sociales. &lt;/p&gt;
&lt;p&gt;Le trick est donc d'aligner les activités sur ses centres d'intérêts : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Participer à un atelier de méditation est en phase avec mon projet de réaliser Samatha&lt;/li&gt;
&lt;li&gt;Participer au Poulet-Frite était en phase avec mon objectif de repérer 4-5 lieux que j'apprécie pour d'éventuels rencards&lt;/li&gt;
&lt;li&gt;Participer à un atelier d'écriture est en phase avec mon objectif de créer du contenu en ligne&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ainsi, même si tu n'accroches pas avec le groupe, tu as tout de même progressé sur un de tes objectifs. Plus une activité nourrit tes objectifs, le mieux. C'est une forme de "productive fun" - à l'opposé des activités de type "passive fun" comme regarder la télé.&lt;/p&gt;
&lt;p&gt;Enfin, la meilleure façon de participer à des activités qui sont en phase avec tes projets, c'est de proposer les activités auxquelles tu aurais envie de participer.&lt;/p&gt;
&lt;h2&gt;Pour aller plus loin&lt;/h2&gt;
&lt;p&gt;La deuxième partie de l'expérience devait consister à organiser des évènements. &lt;/p&gt;
&lt;p&gt;Je ne me suis pas aventuré dans cette partie. Et la phase de vie que je vais ouvrir ne va pas s'y prêter. Mais j'aimerais y revenir. &lt;/p&gt;
&lt;p&gt;Voici des idées d'activité à proposer : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Atelier d'écriture créative&lt;/li&gt;
&lt;li&gt;Atelier d'écriture d'introspection &lt;/li&gt;
&lt;li&gt;Atelier d'écriture orienté "création de contenu"&lt;/li&gt;
&lt;li&gt;Atelier d'écriture sur le thème des émotions&lt;/li&gt;
&lt;li&gt;Footing (5 km)&lt;/li&gt;
&lt;li&gt;Marche, promenade&lt;/li&gt;
&lt;li&gt;Méditation&lt;/li&gt;
&lt;li&gt;Souplesse (ex : Yoga, pilatte)&lt;/li&gt;
&lt;li&gt;Cours de danse (ex : Bachata, Kizomba)&lt;/li&gt;
&lt;li&gt;Visite d'une exposition&lt;/li&gt;
&lt;li&gt;Soirée Mario Kart&lt;/li&gt;
&lt;li&gt;Soirée jeux de société&lt;/li&gt;
&lt;li&gt;Soirée film (ex : Vice-versa 2, DJ Mehdi : Made in France)&lt;/li&gt;
&lt;li&gt;Restaurant&lt;/li&gt;
&lt;li&gt;Escape game&lt;/li&gt;
&lt;li&gt;Quizz room&lt;/li&gt;
&lt;li&gt;Crêpes party à l'appart&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et voici deux messages que je veux laisser au moi du futur, quand il s'apprêtera à franchir le cap : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S'il n'y a pas d'offre pour une activité, c'est l'opportunité de la proposer. L'absence d'offre indique peut-être que le format ne fonctionne pas. Mais la seule façon de le savoir est d'essayer. &lt;/li&gt;
&lt;li&gt;Même s'il y a une offre pour une activité, il est intéressant de la proposer en son nom. Cela créer de la diversité dans les groupes. Comme il n'y a pas un seul restaurant par ville, il n'y a pas de raison qu'il y ait une seule sortie restaurant cinéma par ville.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;À suivre ... ! &lt;/p&gt;</content></entry><entry><title>90 jours à la salle de sport</title><link href="https://me.petitapetit.io/sf" rel="alternate"/><published>2024-12-26T00:00:00+01:00</published><updated>2024-12-26T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-26:/sf</id><summary type="html">&lt;p&gt;Je suis allé 3-4x/semaine à la salle de sport pendant 3 mois.&lt;/p&gt;
&lt;p&gt;C'est l'endroit où j'ai passé le plus de temps après chez moi.&lt;/p&gt;
&lt;p&gt;Voici un compte rendu de mon expérience et de ce que j'ai appris.&lt;/p&gt;
&lt;h1&gt;Mise en place&lt;/h1&gt;
&lt;p&gt;Début septembre, j'ai voulu essayer le street workout avec …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Je suis allé 3-4x/semaine à la salle de sport pendant 3 mois.&lt;/p&gt;
&lt;p&gt;C'est l'endroit où j'ai passé le plus de temps après chez moi.&lt;/p&gt;
&lt;p&gt;Voici un compte rendu de mon expérience et de ce que j'ai appris.&lt;/p&gt;
&lt;h1&gt;Mise en place&lt;/h1&gt;
&lt;p&gt;Début septembre, j'ai voulu essayer le street workout avec mon frère. Nom de code du projet : &lt;code&gt;Street Fighter 💪🏋️‍♂️&lt;/code&gt;. Comme il pleuvait, on est allé à la salle. Elle se situe sur les quais, donne vue sur la Seine, et j'ai tout de suite accroché. On a pivoté sur de la musculation classique. Il n'y a pas donc ni "Street", ni "Fight" dans ce projet, mais le nom "Street Fighter" me faisait bien rire, alors je l'ai gardé.&lt;/p&gt;
&lt;p&gt;Mes objectifs / motivations étaient : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avoir un meilleur niveau d'énergie général&lt;/li&gt;
&lt;li&gt;couper avec le mental en fin de journée&lt;/li&gt;
&lt;li&gt;développer un corps plus attrayant&lt;/li&gt;
&lt;li&gt;passer du temps avec mon frère&lt;/li&gt;
&lt;li&gt;développer une posture droite&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="" src="_assets/street-fighter.png"&gt;&lt;/p&gt;
&lt;p&gt;Ready ? Fight ! &lt;/p&gt;
&lt;h1&gt;Comment ça c'est passé&lt;/h1&gt;
&lt;p&gt;&lt;u&gt;Phase 1 : découverte&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;16 septembre 2025.&lt;/p&gt;
&lt;p&gt;J'entre dans une salle de sport pour la première fois de ma vie. Chaque machine est un mystère pour moi. Je ne sais pas comment faire du rameur. Je fais des exercices avec des haltères de 4kg et c'est déjà difficile. Je ne parviens pas à terminer mes séries de demi-pompes. &lt;/p&gt;
&lt;p&gt;Je suis avec mon frère. Il a fait un mois de musculation l'année dernière. Il connait un peu mieux cet environnement. On installe l'application Fitness Online pour avoir des programmes complets. Je teste tout plein d'exercices.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Phase 2 : sociabilisation&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Mon frère se concentre sur d'autres projets et je vais désormais à la salle en solo. Je sympathise avec Thibaut sur les rameurs. Par son intermédiaire, je fais la rencontre de Simon. Le weekend, on est presque que tous les deux à venir. J'aime le vélo. Lui aussi. Je viens de découvrir la bachata. Il en fait depuis plusieurs années. On a des affinités !&lt;/p&gt;
&lt;p&gt;Quelques jours plus tard, un mec vient me voir et se présente. Il est en L3 STAPS, il démarre un stage et est disponible pour donner des conseils. Je réfléchis 3 secondes. OK dude, j'ai plein de questions ! On sympathise. &lt;/p&gt;
&lt;p&gt;Les semaines suivantes, on est inséparables. Je fais mes séances avec Simon et Adan. Ils me donnent des conseils sur les exercices : comment ne pas se blesser, comment bien se placer. Ils corrigent ma posture. Les séances sont fun et je progresse vite. &lt;/p&gt;
&lt;p&gt;On dit que &lt;a href=""&gt;seul, on va plus vite ; ensemble, on va plus loin&lt;/a&gt;. Maintenant je comprends.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Phase 3 : OAHS&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;Un jour, Adan débarque avec une question simple : c'est quoi ton objectif ? &lt;/p&gt;
&lt;p&gt;À ce moment-là, je viens régulièrement. Je fais pleins d'exercices, mais je n'ai pas de direction. Je réfléchis quelques secondes. Je sais ! J'aimerais tenir en équilibre sur une main. &lt;/p&gt;
&lt;p&gt;C'est un exercice qui nécessite un travail du corps complet. Il demande de la force, mais aussi de la souplesse et de l'équilibre. C'est subtil. Et c'est &lt;a href="_assets/oahs-mw.png"&gt;trop stylé&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il se trouve que c'est ce qu'il travaille aussi. C'est fou. Il propose de m'accompagner sur cet objectif. On commence à travailler le handstand ensemble. Je regarde le programme de Matthew Wismith en parallèle. Je suis sur-motivé. &lt;/p&gt;
&lt;p&gt;La salle est devenue ma deuxième maison. J'y vais dès que peux en fin de journée. &lt;/p&gt;
&lt;p&gt;Ma semaine la plus intensive donnera :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une routine de 10 minutes au réveil (4x15 pompes et 4x20 abdos de type mountain climber)&lt;/li&gt;
&lt;li&gt;15 minutes de souplesse vers 9h&lt;/li&gt;
&lt;li&gt;15 minutes de marche après déjeuner&lt;/li&gt;
&lt;li&gt;25 minutes d'entraînement à l'équilibre à 16h&lt;/li&gt;
&lt;li&gt;6 séances à la salle (dont une de RPM)&lt;/li&gt;
&lt;li&gt;Hammam avec massage le dimanche&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J'adore ça.&lt;/p&gt;
&lt;p&gt;Je progresse bien sur les exercices de handstand.&lt;/p&gt;
&lt;p&gt;Tenir 5-10 secondes sur deux mains d'ici à la fin de l'année me paraît jouable.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Phase 4 : stop&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;L'objectif de maîtriser le One Arm Handstand a renouvelé ma motivation.&lt;/p&gt;
&lt;p&gt;Il est recommandé de faire 3 à 4 séances d'équilibre par semaine. J'imagine que c'est un rythme minimal. Je veux y aller à fond et m'entraîner tous les jours. &lt;/p&gt;
&lt;p&gt;Les séances d'équilibres demandent de plus en plus de temps. Mais je n'ai pas plus de temps. Pour que les séances continuent de tenir en 25 minutes, je commence à timer mes pauses et à réduire leur durée. &lt;/p&gt;
&lt;p&gt;Une douleur s'installe dans mon poignet gauche. &lt;/p&gt;
&lt;p&gt;Je ne la vois pas venir. &lt;/p&gt;
&lt;p&gt;Un matin, je sens que même faire des pompes est douloureux. &lt;/p&gt;
&lt;p&gt;Je lève le pied, mais il est trop tard. &lt;/p&gt;
&lt;p&gt;La douleur est installée.&lt;/p&gt;
&lt;p&gt;J'attends une semaine. &lt;/p&gt;
&lt;p&gt;La douleur est toujours là. &lt;/p&gt;
&lt;p&gt;F**k.&lt;/p&gt;
&lt;p&gt;Pas de poignet, pas d'équilibre. &lt;/p&gt;
&lt;p&gt;Les exercices avec des haltères sont douloureux eux aussi. &lt;/p&gt;
&lt;p&gt;Fin de la partie.&lt;/p&gt;
&lt;h1&gt;Ce que j'ai appris&lt;/h1&gt;
&lt;h2&gt;Poids, Séries, répétitions et rythme&lt;/h2&gt;
&lt;p&gt;Tout est une question de poids, de séries, de répétitions, de tempo et de repos. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le &lt;strong&gt;poids&lt;/strong&gt; est la charge que tu places&lt;/li&gt;
&lt;li&gt;une &lt;strong&gt;série&lt;/strong&gt; est un groupe de répétitions. Ex : 10 pompes.&lt;/li&gt;
&lt;li&gt;une &lt;strong&gt;répétition&lt;/strong&gt; est un cycle d'un mouvement. Ex : une pompe. &lt;/li&gt;
&lt;li&gt;le &lt;strong&gt;tempo&lt;/strong&gt; décrit la vitesse d'exécution d'une répétition. Ex : 2-1-1-1 correspond à une descente contrôlée sur 2 secondes, un maintien en position basse pendant 1 seconde, une remontée sur 1 seconde (+ explosif), un maintien en position haute d'1 seconde.&lt;/li&gt;
&lt;li&gt;enfin, chaque série est séparée par un temps de &lt;strong&gt;repos&lt;/strong&gt;. Ex : 1 min 30. Le secret pour faire 50 pompes et de ne pas les faire d'affilées. Il est plus facile de faire 5x10 pompes avec 1 minute 30 de repos que 50 pompes d'affilées. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela donne lieu à une notation &lt;code&gt;Pompes: 0kg 4×12 / 60s @ 2-1-1-1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Il ne sert à rien de se précipiter. Au contraire, ralentir la phase excentrique (la descente) permet de travailler davantage. Pour cadencer l'exercice, tu peux utiliser un métronome ou une app comme soundbrenner qui permet de régler la longueur d'une mesure (ex : mesure en 5 temps pour 2-1-1-1).&lt;/p&gt;
&lt;p&gt;En général, pour un exercice, tu fais :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;entre 3 et 5 séries&lt;/li&gt;
&lt;li&gt;entre 5 et 12 répétitions&lt;/li&gt;
&lt;li&gt;entre 15 secondes et 2 min 30 de repos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il existe aussi des formats plus ésotériques : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;séries de 6 + 8 répétitions (avec un poids, puis un poids + léger)&lt;/li&gt;
&lt;li&gt;des pyramides (monter en charge jusqu'au max puis re-descendre)&lt;/li&gt;
&lt;li&gt;des descentes (pour chaque série, faire le max de répétition. Décharger entre chaque série. À la fin, même la série à vide te paraît difficile)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ordre des exercices&lt;/h2&gt;
&lt;p&gt;Une fois bien échauffé, l'idée est d'aller du plus libre au plus cadré. Les exercices libres sont par exemple les squats et les tractions. Des exercices comme le tirage à la poulie haute en prise serrée ou le leg curl sont cadrés. Les exercices libres sont plus complets. Les exercices cadrés permettent d'isoler un muscle et sont plus facile à réaliser proprement quand tu commences à fatiguer.&lt;/p&gt;
&lt;p&gt;Tu peux combiner deux exercices complémentaires et faire ta pause après (superset). Mon superset préféré est d'enchaîner des tirages poulie haute en prise serrée avec des pompes diamants. &lt;/p&gt;
&lt;div style="display: flex; justify-content: center; gap: 10px"&gt;
  &lt;img src="_assets/streetfighter-tirage.png" width="45%"&gt;
  &lt;img src="_assets/streetfighter-diamant.png" width="45%"&gt;
&lt;/div&gt;

&lt;h2&gt;Comment ne pas se blesser&lt;/h2&gt;
&lt;p&gt;Se blesser = le plus grand risque. (à recopier 100 fois)&lt;/p&gt;
&lt;p&gt;Voici quelques trucs que j'ai appris pour ne pas se blesser :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;u&gt;&lt;strong&gt;Bien s'échauffer&lt;/strong&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Garder le dos droit&lt;/li&gt;
&lt;li&gt;Ne pas bloquer les articulations lorsque tu portes des charges lourdes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;L'échauffement consiste à progressivement activer la zone que tu t'apprêtes à travailler. Cela peut avoir l'air bête, mais il est important d'échauffer la bonne partie du corps.  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pour une session jambes : courir ou faire du vélo.&lt;/li&gt;
&lt;li&gt;pour une session haut du corps : faire du rameur et des tours de chauffe avec un baton. &lt;/li&gt;
&lt;li&gt;pour une session handstand : échauffer les poignets, échauffer les poignets, échauffer les poignets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voici un programme d'échauffement en 7' à faire en rameur ou en vélo :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cycle 1&lt;/li&gt;
&lt;li&gt;2' en vitesse normale&lt;/li&gt;
&lt;li&gt;30" en vitesse max&lt;/li&gt;
&lt;li&gt;30" en repos actif&lt;/li&gt;
&lt;li&gt;Cycle 2 (x2)&lt;/li&gt;
&lt;li&gt;1' normale&lt;/li&gt;
&lt;li&gt;30" en vitesse max&lt;/li&gt;
&lt;li&gt;30" en repos actif&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il est tentant de passer l'échauffement, pour aller plus vite. Mais, une fois que tu es blessé, c'est game over pour plusieurs semaines (minimum).&lt;/p&gt;
&lt;p&gt;Aussi, un bon échauffement permet de meilleures performances pendant la séance.&lt;/p&gt;
&lt;h2&gt;Demander de l'aide est une excellente façon de sociabiliser.&lt;/h2&gt;
&lt;p&gt;Je me lançais sur un rameur pour m'échauffer quand un mec s'installe à côté de moi. &lt;/p&gt;
&lt;p&gt;C'est clair qu'il est débutant. Il me demande des conseils. Je lui partage ce que je sais sur le rameur. On parle un peu. &lt;/p&gt;
&lt;p&gt;Il n'a pas de programme. Je lui propose qu'on fasse ma séance ensemble.&lt;/p&gt;
&lt;p&gt;Il vient une fois par semaine.&lt;/p&gt;
&lt;p&gt;En trois semaines, il avait rencontré et sympathisé avec plus de personnes que moi.&lt;/p&gt;
&lt;p&gt;La leçon de cette anecdote est qu'être débutant, motivé, et plein de questions est un superpouvoir pour nouer des liens. Le plus souvent, nous apportons volontiers notre aide. Cela nous fait nous sentir important.&lt;/p&gt;
&lt;h2&gt;Écouteurs : avec ou sans ?&lt;/h2&gt;
&lt;p&gt;J'ai testé les deux.&lt;/p&gt;
&lt;p&gt;Écouter de la musique pendant une séance est stimulant. Cela donne de l'élan sur les exercices de force. Cela casse la monotonie sur les exercices de cardio. &lt;/p&gt;
&lt;p&gt;Mais, si je vais à la salle, c'est aussi pour sociabiliser. Je pourrais faire des entraînements complets chez moi. Ce qui me plaît, c'est aussi d'avoir un échange avec Adan, avec Simon, d'échanger un sourire avec une autre personne, de dire bonjour sans rien attendre en retour, sans appréhender un rejet (et je suis souvent bien reçu). &lt;/p&gt;
&lt;p&gt;Donc, pas d'écouteurs.&lt;/p&gt;
&lt;h2&gt;Miroir, mon beau miroir ...&lt;/h2&gt;
&lt;p&gt;Les salles de sports sont souvent recouvertes de miroir. &lt;/p&gt;
&lt;p&gt;Pourquoi ? &lt;/p&gt;
&lt;p&gt;Au départ, je pensais qu'ils étaient là pour se mater. Je trouvais cela embarrassant. &lt;/p&gt;
&lt;p&gt;Avec la pratique, j'ai compris qu'ils aident à vérifier et corriger la posture. Cette explication me convenait mieux.&lt;/p&gt;
&lt;p&gt;J'ai posé la question à un habitué. Il m'a répondu : en vrai, les deux.&lt;/p&gt;
&lt;p&gt;Et finalement, pourquoi pas. Développer sa confiance en soi et moins se blesser sont deux motivations honorables.&lt;/p&gt;
&lt;p&gt;Après, il semble que les effets sont débattus. D'après &lt;a href="https://www.slate.fr/story/241006/salle-de-sport-muscu-fitness-ameliorer-seances-snober-miroir-oublier-reflet"&gt;cet article&lt;/a&gt; : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les miroirs peuvent être plaisants chez les personnes qui ont un bon niveau, mais chez les débutants, ils soulignent l'écart entre le "moi actuel" et le "moi idéalisé". Cela rend la séance plus stressante.&lt;/li&gt;
&lt;li&gt;les miroirs peuvent au contraire favoriser les blessures en encouragement des mouvements inappropriés (pour vérifier sa posture). Une alternative est de se concentrer sur les sensations de son corps et de se filmer pour corriger sa posture dans un second temps.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3 types de sessions&lt;/h2&gt;
&lt;p&gt;Session en solo&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top pour écouter son corps&lt;/li&gt;
&lt;li&gt;top pour dérouler sans "perdre" de temps&lt;/li&gt;
&lt;li&gt;idéal quand ta séance est calibrée et que tu connais les exercices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Session avec coach&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top pour se challenger, aller chercher + loin&lt;/li&gt;
&lt;li&gt;top pour corriger ta posture et découvrir de nouveaux exercices&lt;/li&gt;
&lt;li&gt;idéal pour les premières séances&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Session avec amis&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top pour passer un bon moment et rigoler&lt;/li&gt;
&lt;li&gt;absolument inefficace en terme de rythme&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;En vrac&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ne pas sauter les séances jambes&lt;/strong&gt;. Si tu viens 3-4x/semaine, faire les jambes 1x/semaine. C'est une séance assez courte. Elle permet de développer son corps de façon équilibrée. Tu n'as pas envie de ressembler à un magnum.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les poignées de pompe peuvent sauver tes poignets&lt;/strong&gt;. Les poignets sont particulièrement fragiles. Les exercices d'équilibre du OAHS et les pompes les sollicitent beaucoup et il est facile de se blesser. De façon générale, les articulations peu sollicitées et les petits muscles sont les plus à vulnérables. Les poignées permettent de faire des pompes sans forcer sur les poignets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le rythme fait tout&lt;/strong&gt;. Enchaîner les séries d'exercices avec 15 secondes de pause plutôt qu'avec 1 minute 30 change la donne. À haute intensité, attention cependant à choisir des exercices ou le risque de blessure est faible. Le gainage à haute intensité est ok. L'équilibre à haute intensité n'est pas ok.&lt;/li&gt;
&lt;li&gt;Tu peux combiner une séance &lt;u&gt;avant&lt;/u&gt; le RPM, mais pas après. Après, c'est fini. Le risque à est de faire n'importe quoi techniquement, car tu as tout donné pendant le RPM.&lt;/li&gt;
&lt;li&gt;Commencer par une série à vide afin se concentrer sur le ressenti et la position.&lt;/li&gt;
&lt;li&gt;Tes accessoires indispensables : un timer, une gourde, une serviette&lt;/li&gt;
&lt;li&gt;Décomposer puis assembler est la façon la plus rapide et la plus efficace de procéder&lt;/li&gt;
&lt;li&gt;Inspirer par les nez, expirer par la bouche&lt;/li&gt;
&lt;li&gt;Inspirer en phase de descente, expirer pendant l'effort&lt;/li&gt;
&lt;li&gt;La routine matinale peut avoir un effet de bord indésirable. Elle ajoute de la friction au réveil. Il y a des matins où je n'avais pas envie de me lever, car je n'étais pas chaud pour une séance d'abdos. &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;J'ai commencé avec des exercices aléatoires et sans direction. Sociabiliser et me fixer un objectif m'ont fait passer à la vitesse supérieure.&lt;/p&gt;
&lt;p&gt;Une fois lancé, j'ai perdu de vue mon objectif initial. Les entraînements ont pris de plus en plus de place dans mon quotidien. Et puis, à trop forcer, je me suis blessé. &lt;/p&gt;
&lt;p&gt;Mais j'ai découvert une pratique que j'aime beaucoup. &lt;/p&gt;
&lt;p&gt;L'enjeu pour 2025 va être de :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;soigner mon poignet &lt;/li&gt;
&lt;li&gt;y passer moins de temps&lt;/li&gt;
&lt;li&gt;mettre le sport au service de mes projets&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="" src="_assets/streetfighter-salle.png"&gt;&lt;/p&gt;</content></entry><entry><title>J'ai porté du vernis pendant 30 jours</title><link href="https://me.petitapetit.io/vernis" rel="alternate"/><published>2024-12-21T00:00:00+01:00</published><updated>2024-12-21T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-21:/vernis</id><summary type="html">&lt;p&gt;J'ai porté du vernis pendant 30 jours.&lt;/p&gt;
&lt;p&gt;Pourquoi ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Travailler ma confiance en moi &lt;/li&gt;
&lt;li&gt;Susciter des échanges avec des filles&lt;/li&gt;
&lt;li&gt;Suivre ma curiosité&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Génèse (je manque de confiance en moi)&lt;/h1&gt;
&lt;p&gt;Mardi 12 novembre, 12h.&lt;/p&gt;
&lt;p&gt;Mon projet de rencontres n'avance pas. Je vais à la salle, je sors au standup, j'ai fait …&lt;/p&gt;</summary><content type="html">&lt;p&gt;J'ai porté du vernis pendant 30 jours.&lt;/p&gt;
&lt;p&gt;Pourquoi ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Travailler ma confiance en moi &lt;/li&gt;
&lt;li&gt;Susciter des échanges avec des filles&lt;/li&gt;
&lt;li&gt;Suivre ma curiosité&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Génèse (je manque de confiance en moi)&lt;/h1&gt;
&lt;p&gt;Mardi 12 novembre, 12h.&lt;/p&gt;
&lt;p&gt;Mon projet de rencontres n'avance pas. Je vais à la salle, je sors au standup, j'ai fait plusieurs sorties sur via Leizup, mais aucun rencard. Je n'ai matché personne sur Tinder en 10 jours. Et je n'ose pas aborder les femmes dans la rue. Mon hypothèse est la suivante : je dégage un tel manque de confiance en moi que je suis peu attrayant.&lt;/p&gt;
&lt;p&gt;Développer ma confiance en moi remonte dans mes priorités.&lt;/p&gt;
&lt;p&gt;Lors de mes 15 minutes quotidiennes de LinkedIn, je tombe sur un post d'Oussama. Il a porté du vernis pendant trois semaines. Il raconte la palette de réactions que cela a généré, que cela a suscité du rejet chez certaines personnes, que jamais autant de jolies filles ne sont venues lui parler.&lt;/p&gt;
&lt;p&gt;Je suis intrigué.&lt;/p&gt;
&lt;p&gt;Cela me rappelle l'expérience de l'artiste conceptuel Jacques. Pour choisir sa coupe de cheveux, il s'est demandé : que fond les jeunes hommes de mon âge pour se sentir confiants ? Réponse : court sur les côtés et long sur le dessus. Alors, il a fait l'inverse. Pour voir. Plus tard, il racontera qu'assumer cette coupe a été difficile au début, mais cette expérience a boosté sa confiance en lui. &lt;/p&gt;
&lt;p&gt;Porter du vernis pourrait m'aider à développer ma confiance en moi. &lt;/p&gt;
&lt;p&gt;L'idée de porter du vernis m'excite et m'effraie à la fois. &lt;/p&gt;
&lt;p&gt;C'est le signe qu'il y a un truc à creuser.&lt;/p&gt;
&lt;h1&gt;Exécution (ne pas trop réfléchir)&lt;/h1&gt;
&lt;p&gt;Ma décision d'expérimenter le vernis est prise. &lt;/p&gt;
&lt;p&gt;Maintenant, l'enjeu est de ne pas traîner. Chaque moment que je passe à réfléchir à ce projet sans me mettre en mouvement m'éloigne de sa réalisation. Plus j'attends, plus il y a de chances que je ne le fasse pas : l'excitation va retomber et je vais me raconter toutes sortes d'histoires pour justifier de ne pas le faire, ne pas m'aventurer en dehors de ce que je connais.&lt;/p&gt;
&lt;p&gt;Pour aller vite, je me suis rendu dans le premier nailbar que j'ai croisé, à la première occasion. &lt;/p&gt;
&lt;p&gt;Entre l'idée et la pose, il s'est écoulé 6 jours. J'y pensais tous les matins en me levant, avec une excitation mêlée d'appréhension. Le jour J, ma petite voix se faisait de plus en plus forte : "Pourquoi tu fais ça ? ", "Tout ça pour attirer l'attention", "C'est même pas drôle".&lt;/p&gt;
&lt;p&gt;Mais trop tard, c'était lancé.&lt;/p&gt;
&lt;h1&gt;Expérience&lt;/h1&gt;
&lt;h2&gt;Exprimer sa sensibilité&lt;/h2&gt;
&lt;p&gt;Lundi 18 novembre, 13h (jour 1).&lt;/p&gt;
&lt;p&gt;En sortant du bar à ongle, je me sens joyeux. &lt;/p&gt;
&lt;p&gt;Je suis excité par le territoire inconnu que je m'apprête à explorer. Excité par ce nouveau niveau que je viens de débloquer. Excité par l'idée d'avoir de la couleur au bout des doigts. &lt;/p&gt;
&lt;p&gt;C'est comme être une princesse. Comme être une licorne. Comme avoir le pinceau magique de Super Mario Sunshine au bout des doigts. Comme avoir de la magie au bout des doigts. &lt;/p&gt;
&lt;p&gt;Cela faisait longtemps que je n'avais pas ouvert un canal pour exprimer ma sensibilité. C'est un effet inattendu. Cela me fait tripper d'avoir les doigts colorés. Cela apporte de la couleur. Cela apporte de l'amusement. Cela apporte de la joie.&lt;/p&gt;
&lt;h2&gt;D'où vient ta gêne&lt;/h2&gt;
&lt;p&gt;Lundi 18 novembre, 19h (jour 1)&lt;/p&gt;
&lt;p&gt;J'arrive à la salle de sport. C'est ma première sortie. Je me répète "ok, souviens-toi que le but est de s'amuser, ne te replis pas sur toi. Tu vas ressentir de la gêne. Quand ce sera le cas, fais une régulation émotionnelle et traverse tes peurs." &lt;/p&gt;
&lt;p&gt;Je rentre dans le vestiaire. En général, il est vide. Aujourd'hui, il est plein. Les mecs se changent, sortent de la douche, tout le monde est à moitié à poil. Il y a de la testostérone dans l'air. Presque tous les bancs sont pris. Je sens l'embarras monter.&lt;/p&gt;
&lt;p&gt;D'où vient cette gêne ? &lt;/p&gt;
&lt;p&gt;Clairement, de moi.&lt;/p&gt;
&lt;p&gt;Ma gêne vient des histoires que je projette dans ma tête. Cela se traduit dans mes déplacements, et alimente le circuit de la gêne. Mais tout part de moi. Personne ne me fait de remarque. Personne n'a de geste hostile.&lt;/p&gt;
&lt;p&gt;Alors, pourquoi cette gêne ? &lt;/p&gt;
&lt;p&gt;Je suis gêné parce que je me dis que les gens vont penser que je suis homosexuel.&lt;/p&gt;
&lt;p&gt;OK. &lt;/p&gt;
&lt;p&gt;En quoi est-ce un problème ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Je ne pense pas l'être - mais j'ai toujours été mal à l'aise avec l'idée que je pourrais l'être&lt;/li&gt;
&lt;li&gt;Même si c'était le cas, et alors ?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;La question reste en suspend.&lt;/p&gt;
&lt;p&gt;Je trouve un espace. Je me change. Dans la salle, je croise mon pote Simon. Il est excité et trouve ça top. Venant d'un mec qui fait de la Bachata, j'aurais pu m'attendre à cette ouverture d'esprit, mais son enthousiasme me touche. Je me détends.&lt;/p&gt;
&lt;p&gt;Je réalise qu'on a tendance à confondre sensibilité avec homosexualité.&lt;/p&gt;
&lt;h2&gt;C'est en étant gêné que tu crées la gêne&lt;/h2&gt;
&lt;p&gt;Mardi 19 novembre, 7h20 (jour 2)&lt;/p&gt;
&lt;p&gt;Je monte dans le train. Je suis placé dans un carré. Un couple de retraité est installé face à face, côté fenêtre. Je leur dis bonjour. Le mec me fixe plusieurs secondes - je sens le phénomène. Je m'installe à côté de lui. Il remarque mes ongles. Il s'agite. Il tape un message sur son téléphone et le montre à sa femme. Je me doute du sujet. Elle lui répond : "c'est pas ton problème". Cela confirme mon doute.&lt;/p&gt;
&lt;p&gt;Je l'ignore, mais je suis un peu raide. Je ne suis pas bien réveillé et tout le monde dors. Il n'y a pas un bruit dans la rame. Je ne me sens pas d'engager la conversation. Mais il y avait matière. Et je pense qu'on aurait bien rigolé. &lt;/p&gt;
&lt;h2&gt;La Twingo du Père Noël&lt;/h2&gt;
&lt;p&gt;24 novembre, 18h30.&lt;/p&gt;
&lt;p&gt;Je me rends à la salle. Je suis dans mes pensées. Au feu, une Twingo s'arrête devant moi. Elle est recouverte de guirlandes. Cela me sort de ma torpeur. C'est tellement kitch. Je souris. Il y en a qui vont vraiment trop loin. Mon visage s'illumine, et je poursuis ma route avec cette énergie. &lt;/p&gt;
&lt;p&gt;Si je peux apporter ça simplement avec du vernis, je prends. &lt;/p&gt;
&lt;h2&gt;Assumer ses intentions&lt;/h2&gt;
&lt;p&gt;1 décembre, 12h.&lt;/p&gt;
&lt;p&gt;Nous voulons parfois faire des choses sans les assumer ouvertement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Comment vendre des formations sans avoir l'air d'un vendeur de formation" ? &lt;/li&gt;
&lt;li&gt;"Comment draguer sans avoir l'air d'être intéressé" ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela traduit une peur de s'exposer. Mais c'est stupide. S'exposer fait partie du jeu.&lt;/p&gt;
&lt;p&gt;Un des freins de ma petite voix est "tu fais ça pour attirer l'attention".&lt;/p&gt;
&lt;p&gt;Une partie de moi juge qu'il n'est pas &lt;em&gt;bien&lt;/em&gt; de chercher à attirer l'attention. &lt;/p&gt;
&lt;p&gt;Ce conflit intérieur m'amène à me demander "Comment attirer l'attention sans avoir l'air de vouloir attirer l'attention" ?&lt;/p&gt;
&lt;p&gt;Cette démarche n'a pas de sens.&lt;/p&gt;
&lt;p&gt;Il n'y a pas de problème en soi à chercher à attirer l'attention, à se faire remarquer. Cela fait partie du jeu de la sociabilisation. &lt;/p&gt;
&lt;p&gt;Maintenant, est-ce que je fais ça pour le regard des autres avant de le faire pour moi ? Non. L'impulsion de départ est que cela a attiré ma curiosité et m'a excité. Ensuite, le jeu du regard des autres vient ajouter du piment. Et bien sûr qu'il y a une forme de provocation.&lt;/p&gt;
&lt;h2&gt;Attirer les filles&lt;/h2&gt;
&lt;p&gt;27 novembre, 20h40.&lt;/p&gt;
&lt;p&gt;Un de mes espoirs était que des filles allaient venir me parler. &lt;/p&gt;
&lt;p&gt;Cela ne s'est pas produit. &lt;/p&gt;
&lt;p&gt;Pourquoi ? &lt;/p&gt;
&lt;p&gt;J'ai plusieurs pistes :  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Une démarcation de style est un point de départ pour un échange, mais ce n'est pas suffisant. Si tu es replié, embarrassé, et que tu évites les contacts visuels, rien de magique ne va se produire. &lt;/li&gt;
&lt;li&gt;Si tu ne sors pas de chez toi et passe ton temps à lire gros, personne ne va venir te parler.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Par ailleurs, je réalise que : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La meilleure façon de parler avec des personnes est d'aller leur parler, pas d'attendre qu'elles viennent te parler. Si les deux font pareil, on voit bien que cela ne peut pas marcher.&lt;/li&gt;
&lt;li&gt;"Venir te parler" est différent de "être attirées". Après, une fois que le contact est établi, il n'appartient qu'à toi de développer la relation : montrer clairement que tu es attiré par elle (si c'est le cas), polariser, partager des sorties, établir une connexion émotionnelle, escalader ...&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Mes motivations étaient de : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Travailler ma confiance en moi &lt;/li&gt;
&lt;li&gt;Susciter des échanges avec des filles&lt;/li&gt;
&lt;li&gt;Suivre ma curiosité&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Résultat ? &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rien de significatif &lt;/li&gt;
&lt;li&gt;Échec complet&lt;/li&gt;
&lt;li&gt;Réussite&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Je m'attendais à susciter plus de réactions. Globalement, tout le monde s'en fou.&lt;/p&gt;
&lt;p&gt;Après, c'était une expérience amusante, et j'ai découvert qu'on pouvait faire des choses incroyables en nail-art.&lt;/p&gt;
&lt;p&gt;J'ai retiré le vernis le 21/12/2024, à 11h.&lt;/p&gt;
&lt;p&gt;J'ai trouvé mes mains tristes après cela. &lt;/p&gt;
&lt;p&gt;En même temps, j'ai trouvé cela reposant. &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/vernis-1.jpg"&gt;
&lt;img alt="" src="_assets/vernis-2.jpg"&gt;
&lt;img alt="" src="_assets/vernis-3.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;PS - en vrac&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Plusieurs personnes m'ont demandé si c'était pour le style ou si c'était médical. Je n'ai pas compris 🤷‍♂️.&lt;/li&gt;
&lt;li&gt;Je me suis fait des films de ouf par anticipation. Le plus extrême ? Qu'au boulot, ils allaient trouver cela abusé et qu'ils n'allaient pas renouveler ma mission pour ce motif.&lt;/li&gt;
&lt;li&gt;Certaines personnes sont excellentes à te regarder dans les yeux et ne pas faire de remarque. Certaines pas du tout.&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>Relire Dragon Ball à 32 ans - 14 insights</title><link href="https://me.petitapetit.io/dbz" rel="alternate"/><published>2024-12-02T00:00:00+01:00</published><updated>2024-12-02T00:00:00+01:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-12-02:/dbz</id><summary type="html">&lt;p&gt;Je viens de relire les 42 tomes de Dragon Ball.&lt;/p&gt;
&lt;p&gt;Voici 14 éléments qui m'ont marquées.&lt;/p&gt;
&lt;h2&gt;1 - Goku ne vit pas toujours avec Chichi&lt;/h2&gt;
&lt;p&gt;Goku s'embarque plusieurs fois dans de longues épopées :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Après avoir été tué par Raditz, il passe un an au paradis où il s'entraîne avec maître Kaïo …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Je viens de relire les 42 tomes de Dragon Ball.&lt;/p&gt;
&lt;p&gt;Voici 14 éléments qui m'ont marquées.&lt;/p&gt;
&lt;h2&gt;1 - Goku ne vit pas toujours avec Chichi&lt;/h2&gt;
&lt;p&gt;Goku s'embarque plusieurs fois dans de longues épopées :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Après avoir été tué par Raditz, il passe un an au paradis où il s'entraîne avec maître Kaïo pour revenir plus fort et affronter Vegeta. &lt;/li&gt;
&lt;li&gt;Après avoir échappé de justesse à l'explosion de Namek, il reste un an sur la planète Yardrat où il apprend la technique du déplacement instantané.&lt;/li&gt;
&lt;li&gt;De façon générale, même lorsqu'il est sur terre, il passe ses journées à s'entraîner. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dragon Ball n'est pas vraiment un modèle pour les relations homme-femme. Mais, je relève ce point. Il a pu m'influencer.&lt;/p&gt;
&lt;h2&gt;2 - Parfois, perdre est la meilleure chose qu'il peut t'arriver.&lt;/h2&gt;
&lt;p&gt;Lors du premier championnat du monde d'arts martiaux, Tortue Génial s'inscrit sous une fausse identité. Il veut s'assurer que son élève ne remporte pas la compétition. De fait, il bat Goku de justesse en finale. Grâce à cette défaite, Goku repart encore plus motivé pour s'entraîner de gagner l'année suivante.&lt;/p&gt;
&lt;p&gt;Tu viens pour gagner. Mais par fois, perdre est la meilleure chose qui peut t'arriver.&lt;/p&gt;
&lt;h2&gt;3 - Dans une équipe de choc, il y a toujours un techos&lt;/h2&gt;
&lt;p&gt;Dans Dragon Ball, c'est Bulma et son père. Bulma fabrique le détecteur de Dragon Ball. Son père construit à Goku un vaisseau pour qu'il aille sur Namek. Dans le futur, Bulma construit une machine à voyager dans le temps. &lt;/p&gt;
&lt;p&gt;D'ailleurs, quand on y pense, c'est Bulma qui lance l'aventure. C'est elle qui fait la rencontre de Goku lorsqu'il est encore un enfant sauvage qui vit dans la foret. &lt;/p&gt;
&lt;h2&gt;4 - L'amour est contagieux&lt;/h2&gt;
&lt;p&gt;Le démon Piccolo est constitué de la part maléfique dont c'est séparé Kami pour devenir dieu. Il est pur mal. Pourtant, il s'adoucit au contact de l'amour que dégage Son Goku. Enfin, il se prends d'affection pour San Gohan l'année où il l'entraîne pour combattre Vegeta. &lt;/p&gt;
&lt;p&gt;Naïf ? Peut-être. &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-piccolo.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;5 - Élimine les mouvements inutiles plutôt que de toujours chercher à ajouter&lt;/h2&gt;
&lt;p&gt;Après sa défaite face à Tao Paï Paï, Goku escalade la Tour Karine pour boire l'eau sacrée et devenir plus fort. Maître Karine l'autorise à boire. À condition qu'il parvienne à la saisir. &lt;/p&gt;
&lt;p&gt;La cruche est suspendue à son baton. Il s'ensuit un jeu du chat à 8000 mètres d'altitude. Goku est vite essoufflé : il s'agite trop. Il fait trop de mouvements inutiles.&lt;/p&gt;
&lt;p&gt;Cette réalisation me rappelle une question de Tim Ferriss : quelles sont les activités que tu penses être productive dans ton quotidien mais qui ne le sont pas ? &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-karin.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;6 - Le secret, c’est l’entraînement&lt;/h2&gt;
&lt;p&gt;Après plusieurs jours de course, Goku parvient à saisir l'eau sacrée. Il la boit. Rien ne se passe. Maître Karine lui explique alors que c’est une eau ordinaire, mais il est aujourd’hui plus fort : son corps s’est renforcé en luttant pour boire l’eau.&lt;/p&gt;
&lt;p&gt;On cherche des raccourcis pour obtenir à coup sûr des résultats en quelques jours, mais il n'y en a pas. Les choses simples fonctionnent et se révèlent dans la durée. Le secret est de s'entraîner chaque jour. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le calme mental demande de l'entraînement&lt;/li&gt;
&lt;li&gt;réaliser un one arm handstand demande de l'entraînement&lt;/li&gt;
&lt;li&gt;développer des logiciels demande de l'entraînement&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7 - Entraines aussi ton esprit&lt;/h2&gt;
&lt;p&gt;Lors de son combat contre Jiren, Maitre Génial rappelle à Goku la grande leçon de ses aventures : entraîner son esprit à faire le vide pour être pleinement dans l'instant est aussi important qu'entraîner son physique.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-esprit.jpeg"&gt;&lt;/p&gt;
&lt;h2&gt;8 - Déploies ton énergie à des moments bien choisis&lt;/h2&gt;
&lt;p&gt;La technique du Kaioken consiste à déployer toute sa force sur de brefs intervalles et uniquement lorsque c'est utile. Lorsqu'il rejoint ses amis sur Namek, Goku utilise cette technique contre les soldats de Freezer. En plus d'augmenter sa force, cette technique lui permet de duper les appareils mesure la force. &lt;/p&gt;
&lt;p&gt;Cette technique me fait penser à la méditation du calme mental (Samatha). Lorsque tu as un travail intellectuel en particulier, tu as intérêt à ne pas gaspiller d'énergie dans des boucles mentales comme les ruminations. Au contraire, faire le vite dans ton esprit, rester calme, et concentrer ta force dans de courtes sessions permet de mobiliser tes ressources mentales de façon efficace. &lt;/p&gt;
&lt;p&gt;En deux mots : Samatha + pomodoro = 🌟&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-kaioken.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;9 - Les événements à priori négatifs apportent aussi leur lot de positif&lt;/h2&gt;
&lt;p&gt;Le Vieux Kaïo Shin était autrefois un bel homme. Il a fusionné accidentellement avec une sorcière. Il est devenu vieux et ridé. Mais il a acquis des pouvoirs magiques par la même occasion. &lt;/p&gt;
&lt;p&gt;Tout comme la défaite de Goku à son premier championnat d'arts martiaux l'a encouragé à poursuivre l'entrainement, on voit ici que les événements à priori négatifs apportent aussi leur lot de positif.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-kaio.jpeg"&gt;&lt;/p&gt;
&lt;h2&gt;10 - Le mindset d'abondance&lt;/h2&gt;
&lt;p&gt;Lors du combat contre Vegeta et Napa, Goku n'hésite pas à donner le dernier Senzu à Krilin et Gohan. Il n'a pas peur de manquer pour lui. On pourrait penser qu'il vaut mieux qu'il le garde pour lui car il est plus fort et a plus de chances de l'emporter. Mais pour Goku, il s'agit de gagner ensemble.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-abondance.jpeg"&gt;&lt;/p&gt;
&lt;h2&gt;11 - Tu ne peux pas passer ta vie coupé du monde&lt;/h2&gt;
&lt;p&gt;La salle de l’esprit et du temps est située dans une dimension parallèle. 24h dans la salle équivaut à 1 an sur terre. C'est incroyable pour s'entraîner, se concentrer, et faire de grands progrès en un temps record. Mais on ne peut pas y vivre indéfiniment. Personne ne peut y passer plus de deux ans dans sa vie. &lt;/p&gt;
&lt;p&gt;Guillaume Moubeche a fait un parallèle entre le monk mode et la salle de l'esprit et du temps. Le &lt;em&gt;monk mode&lt;/em&gt; consiste à s'isoler plusieurs semaines pour se concentrer un projet important et le faire avancer à toute vitesse.&lt;/p&gt;
&lt;p&gt;Cette planche me rappelle que c'est un mode génial, mais qu'il faut aussi s'accorder des moments de relâche pour ne pas devenir fou.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-salle-esprit.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;12 - Genkidama et compassion&lt;/h2&gt;
&lt;p&gt;La technique du Genkidama consiste à demander aux êtres vivants de partager leur force, de collecter cette énergie, puis de l'envoyer sur un opposant. C'est la technique plus puissante de la série. J'y vois une métaphore de la force de la compassion et du sentiment de connexion aux autres.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-genkidama.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;13 - Tu peux avoir la meilleure technique et les meilleures intentions du monde, mais si tu n'est pas capable de délivrer ton message, tu ne parviendras pas à tes objectifs.&lt;/h2&gt;
&lt;p&gt;Après que Gotenks, San Gohan et Vegetto aient échoué face à Boo, Goku est acculé. Il démarre le Genkidama pour sauver l'univers. &lt;/p&gt;
&lt;p&gt;Avec l'aide de maitre Kaïo, Vegeta entre en contact avec les habitants de la terre afin qu'ils envoient leur énergie à Goku. Ceux qui reconnaissent Végéta participent, mais la majorité des gens sont sceptiques. &lt;/p&gt;
&lt;p&gt;Goku passe un message à son tour. Les personnes qu'il a aidé au cours de ses aventures le reconnaissent et contribuent. Mais le compte n'y est toujours pas.&lt;/p&gt;
&lt;p&gt;Ce n'est que lorsque Satan prend la parole qu'ils gagnent une large participation. Satan apparaît jusqu'ici comme un pitre et un imposteur. Mais il est populaire. Les terriens ont confiance en lui. &lt;/p&gt;
&lt;p&gt;Sans Satan et son audience, Goku n'aurait pas pu finaliser son attaque.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-satan.png"&gt;&lt;/p&gt;
&lt;h2&gt;14 - Il est difficile d'avancer seul. La force que t'apporte un groupe d'amis est inestimable.&lt;/h2&gt;
&lt;p&gt;Boo est le dernier adversaire de la saga. Lors l'assaut final, Goku lui adresse un au-revoir. Son message est vide de colère. Au contraire, il est empreint d'amour et de compassion. Goku est salut la force et la combativité de Boo malgré qu'il combatte seul. &lt;/p&gt;
&lt;p&gt;C'est le dernier combat de la série. Et pour moi, c'est aussi le dernier message adressé au lecteur.&lt;/p&gt;
&lt;p&gt;Cette page me rappelle que même si je suis hyper motivé dans mes projets, il est possible que je finisse par m'essouffler si je reste seul dans mon aventure et que je ne trouve personne avec qui partager mes joies, mes peines, mes réussites et mes échecs. Elle me rappelle de ne pas m'enfermer, de m'ouvrir et de partager le chemin. Elle me rappelle que le groupe porte en lui une grande force. &lt;/p&gt;
&lt;p&gt;Les bouddhistes appellent cela la sangha. &lt;/p&gt;
&lt;p&gt;Brian Eno appelle cela un scenius. &lt;/p&gt;
&lt;p&gt;Les entrepreneurs parlent de groupe de responsbilité. &lt;/p&gt;
&lt;p&gt;Benjamin Franklin avait le Junto Club.&lt;/p&gt;
&lt;p&gt;David Perell appelle cela le club des 10.&lt;/p&gt;
&lt;p&gt;Sociabilise dans tes passions.&lt;/p&gt;
&lt;p&gt;Seul on va plus vite, ensemble on va plus loin.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/dragonball-boo.jpeg"&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;J'ai découvert Dragon Ball alors que je devais avoir 11 ans.&lt;/p&gt;
&lt;p&gt;J'ai adoré relire cette saga +12 ans plus tard. &lt;/p&gt;
&lt;p&gt;En essence, Dragon Ball défend deux valeurs : 
- celle de l'entraînement
- celle de l'amitié&lt;/p&gt;
&lt;p&gt;Enfin, un message m'avait complètement échappé en première lecture mais me frappe aujourd'hui. Il revient régulièrement : celui d'entraîner son esprit au calme mental. &lt;/p&gt;
&lt;!--&lt;img src="_assets/dragonball-salut.png"/&gt;--&gt;</content></entry><entry><title>Trouver le handle parfait (Guide ultime)</title><link href="https://me.petitapetit.io/handle" rel="alternate"/><published>2024-10-13T00:00:00+02:00</published><updated>2024-10-13T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-10-13:/handle</id><summary type="html">&lt;p&gt;Au cours de cette année, j'ai tâtonné plusieurs fois sur le choix de mon handle. Est-ce que j'aligne tous mes handle sur &lt;code&gt;petitalx&lt;/code&gt;, l'e-mail que j'ai créé en arrivant à l'université ? Est-ce que je pars sur &lt;code&gt;lxnd&lt;/code&gt;, le handle de ma phase "anonyme" ? Sur quels critères baser cette décision ?&lt;/p&gt;
&lt;p&gt;J'ai …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Au cours de cette année, j'ai tâtonné plusieurs fois sur le choix de mon handle. Est-ce que j'aligne tous mes handle sur &lt;code&gt;petitalx&lt;/code&gt;, l'e-mail que j'ai créé en arrivant à l'université ? Est-ce que je pars sur &lt;code&gt;lxnd&lt;/code&gt;, le handle de ma phase "anonyme" ? Sur quels critères baser cette décision ?&lt;/p&gt;
&lt;p&gt;J'ai cassé plein de fois tous mes liens de bio. &lt;/p&gt;
&lt;p&gt;Pour te faire gagner du temps et t'aider à en fixer un une fois pour toutes, j'ai regroupé ici tout ce que j’ai compris sur comment choisir son handle.&lt;/p&gt;
&lt;h2&gt;C'est quoi un handle ?&lt;/h2&gt;
&lt;p&gt;Un handle est un mot de quelques lettres qui identifie ton compte sur les plateformes comme Twitter, Instagram et LinkedIn. Il se présente souvent précédé d'un arobase. Par exemple : &lt;a href="https://www.instagram.com/ulysselubin/"&gt;@ulysselubin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ton handle est ce qui t'identifie en quelques lettres sur internet. Choisir un bon handle contribue à ta visibilité. Il permet que l'on te reconnaisse d'une plateforme à une autre. Si tu es présent en ligne de façon professionnelle, c'est un élément qui vaut la peine d'être considéré - d'autant plus que ce guide devrait te donner les éléments pour en trouver un rapidement.&lt;/p&gt;
&lt;h2&gt;Le handle parfait&lt;/h2&gt;
&lt;p&gt;Le handle parfait est : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;facile à prononcer&lt;/strong&gt; — afin qu'il soit simple à partager. Lorsque j'avais la culture de l'anonymat, mon adresse e-mail était "lxnd arobase protonmail point com". C'est imprononçable et l'hébergeur est inconnu du grand publique. Lorsque je donne mon adresse e-mail à l'oral, 100% des gens ne comprennent pas. Je me retrouve à l'épeler lettre par lettre, et c'est laborieux - pour moi autant que pour la personne en face.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;court&lt;/strong&gt; — d'une longueur inférieure à 15 caractères, afin qu'il soit facile à mémoriser. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;consistant&lt;/strong&gt; entre les plateformes — pour qu'il soit facile de faire le lien entre tes différents comptes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;facile à saisir&lt;/strong&gt; — éviter les chiffres et les caractères spéciaux (tirets inclus)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Le candidat parfait&lt;/h2&gt;
&lt;p&gt;L'idéal est de composer ton handle à partir de ton &lt;strong&gt;prénom&lt;/strong&gt; et de ton &lt;strong&gt;nom&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Par exemple : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Naval Ravikant utilise simplement son prénom : https://nav.al/ → @naval&lt;/li&gt;
&lt;li&gt;Le nom de domaine de Derek Sivers consiste en son nom : https://sive.rs/&lt;/li&gt;
&lt;li&gt;Pieter Levels utilise également son nom : https://levels.io/ → @levelsio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lorsqu'il ni ton nom ni ton prénom ne sont disponibles, voici quelques tricks à tester pour t'en sortir : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;trick du domaine&lt;/strong&gt; : intègre ton top-level domain à ton handle. C'est par exemple ce que fait la société MADE (https://www.made.com/ → @madedotcom) ou encore Pieter Levels (https://levels.io → @levelsio)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;trick de l'accolade&lt;/strong&gt; : accole la première lettre de ton prénom à ton nom. C'est l'approche qu'a choisi Nassim Nicholas Taleb (@nntaleb). C'est aussi une approche que j'ai considéré avec @apetit. Le jeu de mot avec &lt;em&gt;appétit&lt;/em&gt; m'amusait autant qu'il me mettait mal à l'aise. Mais le nom de domaine https://apetit.io est déjà pris. Cela a tranché mon débat intérieur.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;trick de la simplification&lt;/strong&gt; : utilise une forme simplifiée de ton nom lorsque celui-ci est difficile à prononcer et à mémoriser. C'est la technique qu'a employé Dan Koe (Daniel Koenigslieb → Dan Koe). La simplicité est la sophistication suprême.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;trick du préfixe&lt;/strong&gt; : la technique du préfixe consiste à ajouter un préfixe à ton nom. Tu t'appelles Thomas ? @iamthomas, @thethomas et @heythomas sont des candidats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What do you do ?&lt;/strong&gt; : indique ce que tu fais directement dans ton handle pour nicher ton prénom. C'est ce qu'a fait le codeur Michaël de Marliave (@micode). &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Consistance&lt;/h2&gt;
&lt;p&gt;Pour que ton handle soit consistant, il faut qu'il soit disponible. &lt;/p&gt;
&lt;p&gt;En tant que développeur, idéalement, je veux que :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;le nom de domaine soit disponible. &lt;/li&gt;
&lt;li&gt;le handle soit disponible sur LinkedIn, Youtube, Github, Twitch et X&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tu peux &lt;a href="https://docs.google.com/spreadsheets/d/1-oIRckVfmfAKH3-h9Q9Wc8TDGZuPbqLQdUthFrFTkjw/edit?usp=sharing"&gt;créer une copie de ce google sheet&lt;/a&gt; pour générer et tester rapidement la disponibilité de ton handle sur différentes plateformes. &lt;strong&gt;Attention toutefois&lt;/strong&gt; : pour les noms de domaines, la seule façon de s'assurer qu'il est disponible est de passer par un registraire de nom de domaine comme &lt;a href="https://porkbun.com/"&gt;porkbun&lt;/a&gt; ou &lt;a href="https://www.gandi.net/fr"&gt;gandi&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Derniers mots pour la route&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ton handle reste un détail&lt;/strong&gt;. Certains entrepreneurs n'ont respecté aucune de ces règles. Cela ne les a pas empéché de construire une large audience et de lancer des projets fous. Par exemple, Oussama Amar utilise @daedelium sur Instagram et Twitter, @bettercallouss sur Youtube : ils ne sont pas consistants et le premier repose sur un pseudo. Un bon handle contribue à ta visibilité - mais cela s'arrête là.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un nom reste ce que tu en fais&lt;/strong&gt;. Il y a 10 ans, j'ai lancé une association d'évènementiel avec des amis. Le nom qui a été retenu est celui de Sport National. Ce nom ne m'inspirait pas au début. Il ne me parlait pas. Et puis, une fois l’identité et les premiers événements déployés, une fois qu'on a commencé à le faire vivre, j'ai commencé à l'adorer. La morale de cette histoire est qu'un nom, ça reste ce que tu en fais. &lt;/p&gt;
&lt;p&gt;Qu'est-ce que tu vas faire de ton handle ?&lt;/p&gt;
&lt;p&gt;❤️&lt;/p&gt;</content></entry><entry><title>Deploy Python Web Apps on your own server</title><link href="https://me.petitapetit.io/deploy" rel="alternate"/><published>2024-09-25T00:00:00+02:00</published><updated>2024-09-25T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-09-25:/deploy</id><summary type="html">&lt;h1&gt;Why ?&lt;/h1&gt;
&lt;p&gt;Why would you do that ? &lt;/p&gt;
&lt;p&gt;Here are 5 reasons that motivated me :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it's economical (only 5$/month + the price of your domain name)&lt;/li&gt;
&lt;li&gt;it's easy (just follow this tutorial)&lt;/li&gt;
&lt;li&gt;it's empowering&lt;/li&gt;
&lt;li&gt;it's simple&lt;/li&gt;
&lt;li&gt;it's fun&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another reason that pushed me is mimesis. Pieter Levels deploy his index.php files …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Why ?&lt;/h1&gt;
&lt;p&gt;Why would you do that ? &lt;/p&gt;
&lt;p&gt;Here are 5 reasons that motivated me :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it's economical (only 5$/month + the price of your domain name)&lt;/li&gt;
&lt;li&gt;it's easy (just follow this tutorial)&lt;/li&gt;
&lt;li&gt;it's empowering&lt;/li&gt;
&lt;li&gt;it's simple&lt;/li&gt;
&lt;li&gt;it's fun&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another reason that pushed me is mimesis. Pieter Levels deploy his index.php files on his own server. Derek Sivers run all his business on it's own sever. Hackers do it. It looks exciting.&lt;/p&gt;
&lt;p&gt;So, here is the plan :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#create-server"&gt;Setup your server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#create-app"&gt;Make a simple python web app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#add-certificate"&gt;Add your subdomain to your certificate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#route-traffic"&gt;Route trafic to your web app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create amazing projects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="create-server"&gt; Setup the instance &lt;/h3&gt;

&lt;p&gt;This article sit on the shoulders of &lt;a href="https://sive.rs/ti"&gt;https://sive.rs/ti&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Follow it to setup your own server. Derek's article will lead you to a simple, secure, and powerful setup.&lt;/p&gt;
&lt;p&gt;His guide is frequently updated. But still, things can go wrong. If it does, start over. You'll be amazed how fast you can go. If it still doesn't work, yo are lucky. It's an opportunity to learn and help. Read the &lt;code&gt;ti.sh&lt;/code&gt; source instructions. Dig the man pages. It's most certainly not a big thing and you will learn a lot.&lt;/p&gt;
&lt;p&gt;By the end of the article, you are equipped with your own server. Setup of e-mail, calendar and contacts is out of scope in our case. If you seize the opportunity, great :).&lt;/p&gt;
&lt;h3 id="create-app"&gt;Create a simple app&lt;/h3&gt;

&lt;h4&gt;1. Connect to your server with ssh&lt;/h4&gt;
&lt;h4&gt;2. Run &lt;code&gt;whoami&lt;/code&gt; and note your username (mine is just &lt;code&gt;a&lt;/code&gt;)&lt;/h4&gt;
&lt;h4&gt;3. Install python :&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;doas&lt;span class="w"&gt; &lt;/span&gt;pkg_add&lt;span class="w"&gt; &lt;/span&gt;python
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;4. Create a folder for your app&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/var/www/
doas&lt;span class="w"&gt; &lt;/span&gt;su
mkdir&lt;span class="w"&gt; &lt;/span&gt;myapp
chown&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;username&amp;gt;:&amp;lt;username&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;myapp&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# for me it&amp;#39;s `chown -R a:a myapp`&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;5. Download code of your app&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd myapp
curl -O https://petitapetit.io/pub/app.py
# or git clone &amp;lt;https://github.com/....git&amp;gt; .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;6. Create a virtual environment and install the dependencies&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install flask
pip install gunicorn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;7. Test that it works locally&lt;/h4&gt;
&lt;p&gt;First, with flask : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Serve the web app&lt;/span&gt;
python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;flask&lt;span class="w"&gt; &lt;/span&gt;--app&lt;span class="w"&gt; &lt;/span&gt;app&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;.0.0.1&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8000&lt;/span&gt;
&lt;span class="c1"&gt;# In another window, browse it. You should the html content of the page.&lt;/span&gt;
curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;.0.0.1:8000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, with gunicorn : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Serve the web app&lt;/span&gt;
gunicorn&lt;span class="w"&gt; &lt;/span&gt;app:app&lt;span class="w"&gt; &lt;/span&gt;--workers&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--bind&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;.0.0.1:8000&lt;span class="w"&gt; &lt;/span&gt;--daemon
&lt;span class="c1"&gt;# Again, browse it&lt;/span&gt;
curl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;.0.0.1:8000

&lt;span class="c1"&gt;# Stop the gunicorn process &lt;/span&gt;
ps&lt;span class="w"&gt; &lt;/span&gt;aux&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;gunicorn
&lt;span class="nb"&gt;kill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-9&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;GUNICORN_PID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;8. Configure a daemon&lt;/h4&gt;
&lt;p&gt;Create a file with &lt;code&gt;doas vim /etc/rc.d/myappd&lt;/code&gt; and paste this content inside : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/bin/ksh&lt;/span&gt;

&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/var/www/myapp/.venv&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${venv}/bin/gunicorn&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="n"&gt;daemon_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;app:app --daemon --workers 2 --bind 127.0.0.1:8000 --chdir /var/www/myapp&amp;quot;&lt;/span&gt;

&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subr&lt;/span&gt;

&lt;span class="n"&gt;rc_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;www&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;rc_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PATH=${venv}/bin:/usr/bin:/bin&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;rc_start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Start the myapp gunicorn daemon&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rcexec&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;daemon_flags&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;rc_check&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${venv}/bin/python3 ${venv}/bin/gunicorn app:app&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb nb-Type"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;rc_stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Kill the myapp gunicorn workers&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pkill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${venv}/bin/python3 ${venv}/bin/gunicorn app:app&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;rc_cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;9. Enable the daemon so that it will start automatically if you reboot your server.&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rcctl enable myappd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;10. Start your daemon&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rcctl start myappd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In case of troubles, read &lt;code&gt;man rcctl&lt;/code&gt; or try &lt;code&gt;/etc/rc.d/myappd -d start&lt;/code&gt; to start the deamon manually and see the logs.&lt;/p&gt;
&lt;h3 id="add-certificate"&gt; Add your subdomain to your certificate&lt;/h3&gt;

&lt;h4&gt;1. Add your subdomain to the acme-client configuration.&lt;/h4&gt;
&lt;p&gt;To do so, edit &lt;code&gt;/etc/acme-client.conf&lt;/code&gt; and add your subdomain to the list of &lt;code&gt;alternative names&lt;/code&gt; of your domain.&lt;/p&gt;
&lt;p&gt;Here is how the modification look on my side : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/updated-acme-client-conf.png"&gt;&lt;/p&gt;
&lt;h4&gt;2. Renew your certificate&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;doas&lt;span class="w"&gt; &lt;/span&gt;acme-client&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;your-domain&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# for me it&amp;#39;s `doas acme-client -v petitapetit.io`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="route-traffic"&gt; Route the trafic to your web app&lt;/h3&gt;

&lt;h4&gt;1. Update your relayed configuration to route requests made to your subdomain toward your web app.&lt;/h4&gt;
&lt;p&gt;Open the configuration with &lt;code&gt;doas vim /etc/relayd.conf&lt;/code&gt;. Then add the following lines : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/updated-relayd-conf.png"&gt;&lt;/p&gt;
&lt;h4&gt;2. Apply the changes and load the new certificate&lt;/h4&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;doas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rcctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;relayd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;3. Open a browser and try to browse your subdomain&lt;/h4&gt;
&lt;p&gt;For the purpose of this tutorial, it would be &lt;code&gt;https://myapp.petitapetit.io&lt;/code&gt; on my server. You can visite https://leequotes.petitapetit.io to see a web application that I'm actively serving.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;You can now deploy an infinite number of python web apps, on your own server. &lt;/p&gt;
&lt;p&gt;How does it feel ? &lt;/p&gt;
&lt;p&gt;If something goes wrong, please give me a description of what goes wrong at what step. If you make it work, please tell me. I would be happy to know it helped you. Finally, if you see something that can be improved, please tell me too. &lt;/p&gt;</content></entry><entry><title>Une retraite de méditation, sept leçons</title><link href="https://me.petitapetit.io/retraite" rel="alternate"/><published>2024-09-05T00:00:00+02:00</published><updated>2024-09-05T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-09-05:/retraite</id><summary type="html">&lt;p&gt;Cette année, j'ai lancé plusieurs expérimentations pour gagner en clarté mentale:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;digérer les informations que je consomme dans mon second cerveau&lt;/li&gt;
&lt;li&gt;échanger régulièrement avec un psychologue&lt;/li&gt;
&lt;li&gt;écrire des articles (comme celui-ci)&lt;/li&gt;
&lt;li&gt;écrire un journal&lt;/li&gt;
&lt;li&gt;faire des trecks &lt;/li&gt;
&lt;li&gt;et méditer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Début Juillet : cela fait 8 semaines que je médite 5 à …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Cette année, j'ai lancé plusieurs expérimentations pour gagner en clarté mentale:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;digérer les informations que je consomme dans mon second cerveau&lt;/li&gt;
&lt;li&gt;échanger régulièrement avec un psychologue&lt;/li&gt;
&lt;li&gt;écrire des articles (comme celui-ci)&lt;/li&gt;
&lt;li&gt;écrire un journal&lt;/li&gt;
&lt;li&gt;faire des trecks &lt;/li&gt;
&lt;li&gt;et méditer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Début Juillet : cela fait 8 semaines que je médite 5 à 10 minutes par jour. Je lance un chrono. Je ferme les yeux. Je concentre mon attention sur ma respiration, les sensations de mon corps. Mais je ne sais pas vraiment où je vais. &lt;/p&gt;
&lt;p&gt;C'est alors que je reçois un e-mail qui fait la promotion d'Atomic Meditation. Atomic Meditation est un programme mis en avant par Eliott (Atomic) et proposé par Maxime Gréau. Le programme propose un volet formation et un volet coaching. Il tombe à pic. Je fais déjà confiance à Eliott. Je souhaite m'investir davantage dans la communauté Atomic. Je décide donc de rejoindre le Club Samatha.&lt;/p&gt;
&lt;p&gt;Maxime propose une rencontre physique lors d'une retraite de méditation. C'est à deux pas de chez moi. C'est pile pendant mes vacances. Et je n'ai rien de prévu à ces dates. &lt;/p&gt;
&lt;p&gt;Let's go !&lt;/p&gt;
&lt;h2&gt;Le cadre&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="_assets/retraite-vajrad.png"&gt;&lt;/p&gt;
&lt;p&gt;Vajradhara-Ling est un centre bouddhiste installé au coeur du Pays d'Auge, en Normandie. Il a été inauguré en 1987, est géré par Lama Gyourmé. Il est composé d'un temple, un stupa, d'un moulin à prière, un bassin, un grand parc, et peut hébergé jusqu'à 120 personnes en pension complète.&lt;/p&gt;
&lt;p&gt;L'organisateur de la retraite est Pierre Bourges. Il a une formation de psychologue, est spécialisé dans la pédagogie, et est passionné par les neurosciences et le bouddhisme Thibétain. C'est une personne remarquable est très intéressante. &lt;/p&gt;
&lt;p&gt;Cette édition a été marquée par la présence de Phakyab Rinpoche. Phakyab est né en 1966 et est issu d'une famille de nomades Thibétaine. Son enfance est marquée par l'immensité de l'Hymalaya et une vie minimaliste dans une nature abondante. En 2003, il rejoint New York après avoir été torturé dans son Pays. Il y est hospitalisé pour une gangrene à la cheville. Impressionné par l'organisation et la technologie américaine, il est convaincu que la médecine occidentale pourra le guérir. Cependant, les tentatives de soin échouent et les médecins se replient sur la solution de dernier recours : l'amputation. Il consulte alors le Dalaï lama qui lui donne comme instruction de se soigner en puisant dans ses ressources intérieures. Il va alors appliquer les pratiques de méditation auxquelles il a été entraîné plus jeune 12 heures par jour pendant 3 ans. Lentement, sa circulation sanguine va revenir dans son pied. Ses cartilages vont se reconstituer. Et il va guérir. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention : la conclusion de cette histoire est que la médecine occidentale et la méditation sont deux formes de soin qui se complètent. Il ne s'agit pas de choisir entre l'une ou l'autre. Phakyab décourage les personnes qui souhaitent arrêter leurs traitements pour se soigner uniquement par la méditation. Il rappelle qu'il a plus de 50 000 heures de pratique à son actif. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Maxime Gréau est coach et accompagne les gens à être heureux dans leur vie. C'est le fondateur du Club Samatha que j'ai rejoins début juillet. Il organise des méditations guidées formidables, et c'est grâce à lui que je suis ici cette semaine.&lt;/p&gt;
&lt;p&gt;Le temple est tenu par Lama Gyourmé et Lama Pema qui organisent régulièrement des rituels. Deux résidents vivent ici toute l'année où ils sont hébergés et nourris en échange de services. Et ils sont soutenus par une équipe de bénévoles chaleureuse, dynamique et souriante. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Remarque : Le titre de lama est attribué aux personnes qui enseignent le bouddhisme Thibétain / qui sont spirituellement avancées. Le titre est en général décerné par d'autres lamas.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Enfin, côté participants, nous étions ~60 personnes issues de toute la France, de Belgique, dans une moyenne d'age de 50 ans (😅). &lt;/p&gt;
&lt;p&gt;Lorsque je suis arrivé sur le site, j'ai senti une vague d'appréhension monter :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;est-ce que je vais aimer ?&lt;/li&gt;
&lt;li&gt;est-ce que je vais connecter avec les personnes qui sont sur place ?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;La retraite&lt;/h2&gt;
&lt;h3&gt;Une semaine bien remplie&lt;/h3&gt;
&lt;p&gt;Je suis arrivé le samedi 10 août et reparti le dimanche 18 août. &lt;/p&gt;
&lt;p&gt;Le programme de la retraite était structuré autour la pratique de Samatha et de Samatha Healing. &lt;/p&gt;
&lt;p&gt;Samatha est la méditation de la concentration, du calme mental et de la tranquillité. La concentration est une compétence clé dans la vie de tous les jours. Aussi, dans le cadre de la méditation, développer sa capacité de concentration est la base. C'est un pré-requis pour les pratiques plus avancées. &lt;/p&gt;
&lt;p&gt;Samatha Healing est une méditation de soin qui repose sur de la visualisation. C'est l'une des pratique qui a aidé Phakyab à se soigner, et c'est une technique plus avancée. &lt;/p&gt;
&lt;p&gt;Pendants une semaine, ma journée type était la suivante : 
- &lt;code&gt;06h15&lt;/code&gt; : levé, douche, étirements
- &lt;code&gt;07h00&lt;/code&gt; : session avec Pierre
- &lt;code&gt;08h00&lt;/code&gt; : petit déjeuner (café, muesli)
- &lt;code&gt;09h00&lt;/code&gt; : enseignements de Phakyab
- &lt;code&gt;10h30&lt;/code&gt; : pause 
- &lt;code&gt;11h00&lt;/code&gt; : session avec Pierre ou Phakyab
- &lt;code&gt;12h30&lt;/code&gt; : déjeuner
- &lt;code&gt;14h00&lt;/code&gt; : session avec Pierre ou Phakyab
- &lt;code&gt;15h30&lt;/code&gt; : pause
- &lt;code&gt;16h00&lt;/code&gt; : session avec Pierre ou Phakyab
- &lt;code&gt;17h30&lt;/code&gt; : pause
- &lt;code&gt;18h00&lt;/code&gt; : mise au propre de mes notes
- &lt;code&gt;19h30&lt;/code&gt; : dîner
- &lt;code&gt;20h30&lt;/code&gt; : marche, clarification de mes notes, méditation, lecture
- &lt;code&gt;22h30&lt;/code&gt; : dodo&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/retraite-session-temple.png"&gt;
&lt;em&gt;Les sessions avaient lieu dans le temple. Elles alternaient théorie et pratique.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lors des pauses, j'allais soit faire une sieste, soit écrire, soit m'étirer, soit méditer.&lt;/p&gt;
&lt;p&gt;Nous avons été exposés à beaucoup d'informations. Prendre des notes sur mon téléphone puis les distiller/compléter le soir a été un élément clé pour m'aider à assimiler le contenu de la semaine. &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/retraite-notes.png"&gt;
&lt;em&gt;Un aperçu de la liste des fiches que j'ai créées (à gauche) et de l'index que j'ai assemblé (à droite).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;En plus d'être un shoot de connaissances sur la méditation, le bouddhisme et la spiritualité, cette semaine a aussi été une expérience sociale. &lt;/p&gt;
&lt;h3&gt;Déjeuner avec des inconnus&lt;/h3&gt;
&lt;p&gt;Les repas étaient gérés par les bénévoles du lieu et étaient organisés à heure fixe. Ils étaient végétariens, très bons, et se tenaient dans le réfectoire où étaient allongées de grandes tablées.&lt;/p&gt;
&lt;p&gt;Les premiers jours, je m'asseyais à une table où je ne connaissais personne. Les échanges naissaient naturellement. Et lorsque je me levais, 5 personnes étaient passées du statut de "inconnu que je ne sais pas trop où ranger" à "personne sympa dont je connais le prénom et avec qui j'ai connecté". &lt;/p&gt;
&lt;p&gt;J'ai parlé avec Elisabeth, Kassie, Warren, Bettie, Magda, Amandine, Elise (psy), Elise (reiki), Maxime, Daniel, Jessica, Denis, Audrey, Sandrine (grenoble), Sandrine (physicienne), Sarah, Marie-Laure (samatha), Marie-Laure (orga), Joëlle, Ludovic, Phakyab, Micka, Laure, Magda. &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/retraite-personnes.jpeg"&gt;
&lt;em&gt;Photo de groupe prise le jeudi avec les participants de la retraite&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Au début de la semaine, j'étais mal à l'aise dans les situations sociales. Je labellisais beaucoup - et me sentais jugé en retour. Je sentais ma tension monter au moment des breaks. &lt;/p&gt;
&lt;p&gt;À la fin de la semaine, j'étais à l'aise avec tout le groupe. Je me sentais calme.&lt;/p&gt;
&lt;p&gt;Et ce calme a été renforcé par l'installation du silence.&lt;/p&gt;
&lt;h4&gt;Une expérience du silence&lt;/h4&gt;
&lt;p&gt;Mardi matin, Pierre a instauré le silence.&lt;/p&gt;
&lt;p&gt;Avec les exceptions suivantes : 
- Si vous avez quelque chose d'important à dire à quelqu'un, vous pouvez, mais uniquement sur le parking. L'idée est de lui faire signe et de vous écarter.
- Vous pouvez parler pendants les sessions de formation. Pour poser des questions par exemple.&lt;/p&gt;
&lt;p&gt;Au départ, j'étais décidé à l'appliquer sans concession. &lt;/p&gt;
&lt;p&gt;Mais rapidement, j'ai lâché prise. &lt;/p&gt;
&lt;p&gt;En fait, beaucoup de personnes ne jouaient pas le jeu. 
Parfois parce qu'ils n'avaient pas envie et n'étaient pas venu pour cela. 
Parfois parce que le silence les mettait dans l'inconfort.
Parfois un peu des deux.&lt;/p&gt;
&lt;p&gt;Aussi j'ai abandonné l'idée de faire l'expérience de 4 jours de silence en continu.
Ce n'était pas du pur silence donc, mais cela a quand même été une expérience particulière.
Le silence était globalement respecté au moment des repas. &lt;/p&gt;
&lt;p&gt;L'instauration du silence a eu des effets amusants : 
- le fait que la parole soit '"autorisée" seulement pendant les cours a entraîné une hausse de la participation
- le parking c'est rapidement transformé en salon de discussion
- il arrivait qu'une question à poser à Maxime ou à Elise me vienne en tête pendant les repas. Au lieu de la poser, je me demandais alors "pourquoi" cette question ? Quelle est mon intention derrière cette question ? Très souvent, la question n'avait pas grand intérêt. 
- ne pas parler est un obstacle à faire connaissance (en général). Par effet de simple exposition, le groupe devient familier. Mais je n'ai plus fait de rencontres après l'instauration du silence.
- le silence donne lieu à des situations amusantes où l'on communique avec des signes - sans forcément parvenir à délivrer le message&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Remarque : je dis "en général" car après avoir écouté une histoire d'Elise, j'ai compris que certaines pratiques de danse comme le Mind Body Centering permettent de connecter avec l'intimité d'une personne uniquement avec langage du corps, sans parole.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Au début, le fait de ne pas parler en présence d'autres amène de drôles de questions : 
- que faire de son regard pendant les repas ? 
- s'assoir face à face ou en quinconce ? &lt;/p&gt;
&lt;p&gt;Quoi qu'il en soit, une fois habitué à moins parler et à être en présence d'autres personnes dans le silence, une forme de calme d'installe. &lt;/p&gt;
&lt;p&gt;Un calme propice à la méditation.&lt;/p&gt;
&lt;h3&gt;Les chants méditatifs&lt;/h3&gt;
&lt;p&gt;Les chants méditatifs ont été ma plus grande surprise. &lt;/p&gt;
&lt;p&gt;Le premier atelier de la retraite était une pratique de Tchenrezi. Et j'ai détesté : 
- j'étais au fond de la salle
- je n'avais pas le texte
- c'était trop religieux pour moi&lt;/p&gt;
&lt;p&gt;Je me suis demandé ce que je faisais ici. &lt;/p&gt;
&lt;p&gt;Puis il y a eu cette session sur Chine-Lakthong. Cette fois-ci : 
- j'étais dans les premiers rangs, entouré de participants
- une personne a partagé son texte avec moi
- je suis passé outre la forme religieuse&lt;/p&gt;
&lt;p&gt;Et j'ai pris une claque. &lt;/p&gt;
&lt;p&gt;La pratique de Chine-Lakthong alterne chant en Thibétain et méditation libre par blocs de 10 minutes et pendant une heure. Pratiquer avec un lama et les résidants créent une expérience unique. Les moines Thibétains ont une technique de chant particulière. Leur voix résonne. Et les résidents donnent de la profondeur à la pratique en ajoutant des tambours et des cloches.&lt;/p&gt;
&lt;p&gt;Chanter plonge dans un état d'apaisement propice à la méditation. Je ressens du bien être à méditer lorsque mon mental ne cherche pas à s'échapper. Et l'alternance entre chant et méditation est absorbante&lt;/p&gt;
&lt;p&gt;Aussi, chanter à côté de quelqu'un crée une bulle où tu connectes avec la personne. &lt;/p&gt;
&lt;p&gt;La session était en fin de journée. C'est une pratique éprouvante en attention. Pourtant, en sortant de la session, j'étais super excité. Comme si la pratique avait réactivé la circulation de mes émotions. Elles circulaient à présent librement dans mon corps. Par torrents. C'était des émotions positives, agréables. Et en même temps cela m'a désarçonné. C'était presque fatiguant. &lt;/p&gt;
&lt;p&gt;C'est l'activité qui m'a le plus marquée de la semaine.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;C'était une semaine hors du temps. &lt;/p&gt;
&lt;p&gt;Chaque jour, je me suis senti reconnaissant de la chance de vivre cette expérience. &lt;/p&gt;
&lt;p&gt;Voici 7 leçons que je retiens de cette retraite : &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Atteindre l'éveil (la conscience de soi) est la meilleure chose que tu peux faire pour aider les autres.&lt;/strong&gt; Nous avons tendance à vouloir nettoyer la neige qui est sur le toit du voisin avant de balayer devant notre porte. Or, il y a un risque d'erreur important à chercher à aider les autres avant d'avoir géré son cas. Aide-toi avant de chercher à aider les autres. Atteindre l'éveil va t'aider à faire face à tes problèmes. Résoudre ses problèmes permet ensuite d'aider les autres à résoudre le même problème. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Samatha est la base de la méditation.&lt;/strong&gt; Samatha permet de développer sa capacité de concentration et de vider son esprit pour en faire un lac calme et silencieux. Elle est un pré-requis aux pratiques de méditation plus avancées. Lorsque tu parviens aux stades 7, 8, 9, elle a en elle même un impact immense sur ton quotidien.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commencer avec de courtes durées&lt;/strong&gt; : Il est préférable de débuter la méditation avec des entrainements courts mais régulier. Lors des longues sessions, tu fatigues et risques d'installer des habitudes qui seront difficiles à défaire ensuite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limiter les takeways à 7 points&lt;/strong&gt; : Notre mémoire à long terme est presque infinie, mais notre mémoire à court terme est limitée à sept emplacements. Lorsqu'on essaie d'embarquer davantage d'éléments, la mémoire à court terme sature et on oublie tout. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les religions sont des véhicules.&lt;/strong&gt; Les religions encapsulent des connaissances et des éléments de sagesse sous forme d’histoires et de pratiques. Ces histoires facilitent le partage et la mémorisation. Le risque ici est de s’attacher à la forme plutôt qu'au fond et de tomber dans une forme de cargo cult. Ou encore, que la forme prenne de plus en plus de place et finisse par diluer la substance (ce que Bruce Lee appelait &lt;em&gt;The classical mess&lt;/em&gt; dans le cas des écoles de &lt;em&gt;Kung-fu&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ta responsabilité actuelle porte sur tes actions à venir.&lt;/strong&gt; Tu n'as aucune responsabilité vis-à-vis de tes actions passées. Tu n'as à culpabiliser. C'était ton karma. Mais tu as une responsabilité absolue quant à tes actions à venir.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C'est bien pour toi, et c'est maintenant.&lt;/strong&gt; Lorsque tu as accès à ces connaissances, c'est bien pour toi, et c'est maintenant. Les Thibétains ont une forte culture du secret, et la probabilité de trouver une personne qui possède à la fois la connaissance et la pratique est faible - même à l'époque d'internet. J'ai trouvé chez Pierre et Maxime des personnes de confiance pour m'accompagner dans cette pratique. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Aussi, je sors de cette semaine avec une résolution ferme : &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pratiquer Samatha (Shi-né) de façon assidue dans jusqu'à réaliser le stade 9.&lt;/strong&gt;&lt;/p&gt;</content></entry><entry><title>Comment créer un site personnel en Python avec Pelican</title><link href="https://me.petitapetit.io/quickstart-pelican" rel="alternate"/><published>2024-07-27T00:00:00+02:00</published><updated>2024-07-27T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-07-27:/quickstart-pelican</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Dans cet article, on va voir comment créer un site personnel :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avec le générateur de site statique Pelican (écrit en Python)&lt;/li&gt;
&lt;li&gt;hébergé sur Github Pages&lt;/li&gt;
&lt;li&gt;à partir de contenu Markdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le résultat final ressemble à &lt;a href="http://petitalx.io/"&gt;ça&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you don't give a shit about&lt;/em&gt; le contexte et mon processus de …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Dans cet article, on va voir comment créer un site personnel :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avec le générateur de site statique Pelican (écrit en Python)&lt;/li&gt;
&lt;li&gt;hébergé sur Github Pages&lt;/li&gt;
&lt;li&gt;à partir de contenu Markdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le résultat final ressemble à &lt;a href="http://petitalx.io/"&gt;ça&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you don't give a shit about&lt;/em&gt; le contexte et mon processus de décision, tu peux sauter directement à la section "Créer le site". Si à un moment tu es perdu dans les explications, tu peux toujours regarder le &lt;a href="https://github.com/petitalx/petitalx.github.io"&gt;code source de mon site&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Contexte&lt;/h2&gt;
&lt;p&gt;Cela fait plusieurs mois que j'expérimente différents supports d'écriture :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je me suis lancé sur &lt;a href="https://www.linkedin.com/in/petitalx/"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;j'ai créé une &lt;a href="https://100produits.substack.com/"&gt;newsletter sur Substack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;j'écris deux pages par jour dans un carnet physique depuis 20 jours&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une difficulté que je rencontre est que je n'ai pas de ligne éditoriale claire, pas de promesse. &lt;/p&gt;
&lt;p&gt;J'ai envie d'écrire sur des sujets variés : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de retours d'expérience de projets techniques&lt;/li&gt;
&lt;li&gt;des "récits" de voyage&lt;/li&gt;
&lt;li&gt;des points d'avancement de projet&lt;/li&gt;
&lt;li&gt;des tutoriels&lt;/li&gt;
&lt;li&gt;des résumés de livres&lt;/li&gt;
&lt;li&gt;des réflexions&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et je n'ai pas envie de m'imposer de façon "push". &lt;/p&gt;
&lt;p&gt;Pour ces deux raisons, un site personnel me semble être une bonne solution.&lt;/p&gt;
&lt;p&gt;Aussi je veux réaliser 30 projets en Python en complément de The Developer's Brain. Créer un site personnel en python apparaît comme bon challenge !&lt;/p&gt;
&lt;h2&gt;Cadrer le projet&lt;/h2&gt;
&lt;p&gt;Ma motivation est de "claim a personal domain", de m'installer dans un coin de la carte.&lt;/p&gt;
&lt;p&gt;Ce qui va être important pour moi est de :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;livrer&lt;/li&gt;
&lt;li&gt;choisir une solution simple&lt;/li&gt;
&lt;li&gt;avoir un processus de décision clair dans le choix des technologies&lt;/li&gt;
&lt;li&gt;faire un projet en python pour pousser mon expertise sur ce langage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les risques que je perçois sont : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de tomber dans un rabbit hole lors du choix de la technologie&lt;/li&gt;
&lt;li&gt;de me torturer le cerveau pour choisir une solution d'hébergement&lt;/li&gt;
&lt;li&gt;de choisir un outil trop complexe pour mon besoin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK.&lt;/p&gt;
&lt;p&gt;À présent, je suis mis en garde.&lt;/p&gt;
&lt;h2&gt;Choisir la technologie&lt;/h2&gt;
&lt;p&gt;Pour créer un site avec Python, j'ai d'abord pensé aux technologies suivantes : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Django : un framework fullstack populaire pour créer des applications web&lt;/li&gt;
&lt;li&gt;Flask : un framework léger pour créer des applications web&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je me suis posé la question &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quelle framework choisir pour créer un site personnel ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Je suis tombé sur ce fil Reddit &lt;a href="https://www.reddit.com/r/Python/comments/166xf22/best_framework_for_portfolio_website/"&gt;Best framework for portfolio website?&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Les réponses sont unanimes : utiliser Django ou Flask revient à se compliquer la vie. Un générateur de site statique est plus simple, moins cher et plus rapide.&lt;/p&gt;
&lt;p&gt;Un générateur de sites statique est une application qui prends en entrée des fichiers textes et les converti en fichiers HTML.&lt;/p&gt;
&lt;p&gt;Les générateurs de site statique ont les avantages suivants : 
- Simplicité : ils sont en général plus simple à appréhender que les frameworks Fullstack qui présentent davantage de fonctionnalités comme de l'authentification, des interactions avec une base de donnée. 
- Prix : il existe de nombreuses solutions d'hébergement gratuit
- SEO : les pages sont statiques et donc facilement indexables par les moteurs de recherche&lt;/p&gt;
&lt;p&gt;Je me suis donc tourné vers les générateurs de site statique.&lt;/p&gt;
&lt;h2&gt;Choisir un générateur de site statique&lt;/h2&gt;
&lt;p&gt;Les solutions les plus tendances sont : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jekyll&lt;/li&gt;
&lt;li&gt;Hugo&lt;/li&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Jekyll&lt;/strong&gt; existe depuis 2008. L'outil est écrit en Ruby. Plus de &lt;a href="https://trends.builtwith.com/fr/cms/Jekyll"&gt;180k&lt;/a&gt; sites web sont construit avec, et c'est la technologie qui est proposée par défaut pour Github Pages. Il bénéficie communauté, d'un écosystème de plugins. La &lt;a href="https://jekyllrb.com/showcase/"&gt;galerie&lt;/a&gt; démontre qu'il est possible de faire des sites esthétiques et complets avec. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo&lt;/strong&gt; existe depuis 2013. Il est écrit en Go et se veut le générateur statique le plus rapide (en termes de compilation). Il est utilisé par &lt;a href="https://trends.builtwith.com/fr/cms/Hugo"&gt;presque 300k sites&lt;/a&gt;. Il dispose de fonctionnalités de localisation, et peut être associé à des frameworks CSS comme Tailwind. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt; est un générateur de site statique qui permet de combiner Svelte, React et Vue. Je l'ai tout de suite écarté car un wrapper autour de frameworks indique une complexité élevée. &lt;/p&gt;
&lt;p&gt;Et python ?&lt;/p&gt;
&lt;p&gt;Il existe bien un générateur de sites statiques en python : Pelican.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pelican&lt;/strong&gt; existe depuis 2010 et a été créé par &lt;a href="http://blog.notmyidea.org/"&gt;Alexis Metaireau&lt;/a&gt;. Il est utilisé par seulement &lt;a href="https://trends.builtwith.com/websitelist/Pelican"&gt;3500 sites&lt;/a&gt;, mais il dispose d'une documentation claire. Et comme il est écrit en python, je pourrai contribuer au code source au besoin. Au première abord, il dispose de toutes les fonctionnalités dont j'ai besoin : il y a une section blog, des pages on peut ajuster le style.&lt;/p&gt;
&lt;p&gt;Je choisis donc Pelican.&lt;/p&gt;
&lt;h2&gt;Choisir une solution de déploiement&lt;/h2&gt;
&lt;p&gt;Il existe des dizaines de solutions pour déployer un site statique, parmi lesquelles :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS&lt;/li&gt;
&lt;li&gt;Netlify&lt;/li&gt;
&lt;li&gt;Github Pages&lt;/li&gt;
&lt;li&gt;Mon propre serveur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je veux une solution gratuite et simple.&lt;/p&gt;
&lt;p&gt;J'ai entendu beaucoup de bien de Github Pages. &lt;/p&gt;
&lt;p&gt;Je vais tester cette solution.&lt;/p&gt;
&lt;h2&gt;Créer le site&lt;/h2&gt;
&lt;p&gt;Ok, on entre dans le vif du sujet.&lt;/p&gt;
&lt;p&gt;Dans la suite de l'article, on va suivre ensemble les étapes que j'ai suivi pour construire mon site personnel https://petitalx.io. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;petitalx&lt;/code&gt; est mon username Github. Naturellement, vous pouvez remplacer toutes les occurrences de &lt;code&gt;petitalx&lt;/code&gt; par votre username. Vous ne pourrez pas copier coller toutes les instructions. Mais cela fera travailler votre mémoire mécanique :).&lt;/p&gt;
&lt;p&gt;Une version vidéo de ce tutoriel est disponible sur ma chaîne Youtube : https://www.youtube.com/watch?v=cdcrieAV6Wk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Créer un walking skeleton&lt;/h3&gt;
&lt;p&gt;Notre premier objectif est de configurer et déployer un site le plus rapidement possible. On pourra ensuite itérer pour ajuster le style, ajouter du contenu, et simplifier le code.&lt;/p&gt;
&lt;p&gt;Pré-requis :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avoir un terminal unix&lt;/li&gt;
&lt;li&gt;avoir Python installé&lt;/li&gt;
&lt;li&gt;avoir un compte Github&lt;/li&gt;
&lt;li&gt;connaître le ba-ba de git&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Initialisation du projet&lt;/h4&gt;
&lt;p&gt;On commence par créer le projet en local.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev
mkdir&lt;span class="w"&gt; &lt;/span&gt;petitalxio
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;petitalxio

python&lt;span class="w"&gt; &lt;/span&gt;--version&lt;span class="w"&gt; &lt;/span&gt;
python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;venv&lt;span class="w"&gt; &lt;/span&gt;.venv
&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Créer un fichier requirements.txt avec la ligne&lt;code&gt;pelican[markdown]&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;pelican[markdown]&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Puis installer les dépendances&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lancer l'assistant de création de site&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pelican-quickstart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;créer le site dans un dossier &lt;code&gt;_pelican&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;choisir &lt;code&gt;fr&lt;/code&gt; comme langue par défaut&lt;/li&gt;
&lt;li&gt;ne pas expliciter de préfixe d'url&lt;/li&gt;
&lt;li&gt;désactiver la pagination&lt;/li&gt;
&lt;li&gt;renseigner &lt;code&gt;Europe/Paris&lt;/code&gt; comme timezone&lt;/li&gt;
&lt;li&gt;demander à générer le fichier Makefile&lt;/li&gt;
&lt;li&gt;répondre pour l'upload avec FTP, SSH, Dropbox, S3 et Rackspace&lt;/li&gt;
&lt;li&gt;répondre oui à la question "Do you want to upload your website using GitHub Pages?"&lt;/li&gt;
&lt;li&gt;répondre ui à la question "Is it your personal page" ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK ! &lt;/p&gt;
&lt;p&gt;Pour visionner le site en local, lancer les commandes&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio/_pelican
pelican&lt;span class="w"&gt; &lt;/span&gt;content
pelican&lt;span class="w"&gt; &lt;/span&gt;--listen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="" src="_assets/creer-son-site-personnel-avec-pelican-et-ghp-2.png"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remarque : vous pouvez ensuite supprimer le dossier &lt;code&gt;_pelican/output&lt;/code&gt; car dans la section suivante nous allons changer le chemin de sortie des fichiers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Publier sur Github Pages&lt;/h4&gt;
&lt;p&gt;Rendez-vous sur votre compte Github : https://github.com/. &lt;/p&gt;
&lt;p&gt;Créer un nouveau repository &lt;code&gt;username.github.io&lt;/code&gt; où &lt;code&gt;username&lt;/code&gt; est votre nom d'utilisateur Github. Ne pas initialiser le README ni le .gitignore à ce stade.&lt;/p&gt;
&lt;p&gt;De retour sur votre poste, se replacer à la racine du projet&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Créer un fichier &lt;code&gt;.gitignore&lt;/code&gt; avec le contenu suivant&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.venv  
**__pycache__**/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(par exemples avec la commande &lt;code&gt;echo ".venv \n**__pycache__**/" &amp;gt; .gitignore&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Initialiser le dépôt git&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;# petitalx.github.io&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;README.md
git&lt;span class="w"&gt; &lt;/span&gt;init
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;README.md
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;first commit&amp;quot;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;branch&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;main
git&lt;span class="w"&gt; &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;https://github.com/petitalx/petitalx.github.io.git
git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;La façon la plus simple de déployer le site est de générer le contenu du site à la racine du répertoire. Pour faciliter les manipulations, on va modifier le &lt;code&gt;Makefile&lt;/code&gt; afin que la commande &lt;code&gt;make publish&lt;/code&gt; génère le site à la racine du répertoire.&lt;/p&gt;
&lt;p&gt;Ouvrir le fichier &lt;code&gt;_pelican/Makefile&lt;/code&gt; et modifier la ligne &lt;code&gt;OUTPUTDIR&lt;/code&gt; tel que&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;OUTPUTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="k"&gt;)&lt;/span&gt;/..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lancer &lt;code&gt;make publish&lt;/code&gt; depuis &lt;code&gt;~/dev/petitalxio/_pelican&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depuis la racine du répertoire, lancer &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;status
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;generate content&amp;quot;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Vérifier que le déploiement est en cours dans le volet "&lt;strong&gt;Actions&lt;/strong&gt;" du répertoire Github :&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/creer-son-site-personnel-avec-pelican-et-ghp-3.png"&gt;&lt;/p&gt;
&lt;p&gt;Une fois le déploiement terminé, votre site est accessible à l'adresse &lt;code&gt;https://username.github.io&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/premiere-mise-en-ligne.png"&gt;&lt;/p&gt;
&lt;p&gt;Notre site personnel est en ligne ! &lt;/p&gt;
&lt;p&gt;🚀&lt;/p&gt;
&lt;h3&gt;Ajouter les premiers contenus&lt;/h3&gt;
&lt;p&gt;Dans cette section, on va voir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comment créer une nouvelle page&lt;/li&gt;
&lt;li&gt;comment créer un nouvel article&lt;/li&gt;
&lt;li&gt;comment publier les mises à jour&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Depuis le dossier &lt;code&gt;_pelican&lt;/code&gt;, lancer la commande &lt;code&gt;make devserver&lt;/code&gt; afin de lancer le serveur et d'activer le mode auto-reload. Le mode auto-reload va générer les fichiers html locaux de façon automatique dès lors qu'un fichier est modifié.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Création d'une page now&lt;/h4&gt;
&lt;p&gt;À quoi bon créer un site personnel si l'on y met pas une page "now" ? 🤷‍♂️.&lt;/p&gt;
&lt;p&gt;Une page now est une page qui indiquer au visiteur ce que l'on fait actuellement. Ton portfolio donne une bonne idée de ce que tu as réalisé par le passé. Ta page now donne un aperçu de ce qui t'occupe actuellement. &lt;/p&gt;
&lt;p&gt;L'idée est de mettre dedans ce que tu raconterais à un amis que tu n'as pas vue depuis un an : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;où est-ce que tu vis &lt;/li&gt;
&lt;li&gt;quels sont tes challenges&lt;/li&gt;
&lt;li&gt;sur quels projets est-ce que tu travailles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Créer un fichier &lt;code&gt;now.md&lt;/code&gt; dans le dossier &lt;code&gt;~/dev/petitalxio/_pelican/content/pages&lt;/code&gt; : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
&lt;span class="gu"&gt;Title: Now&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;

&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;Mis à jour le 28 juillet 2024 à Rouen, France.&lt;/span&gt;

Je travail actuellement pour EDF en tant que Software Engineer où je développe un service de détection d&amp;#39;anomalies sur des séries temporelles.

Mon axe de travail sur les prochains mois sont de rassembler et synthétiser les 99 meilleurs livres sur le Software Engineering. 

En complément de ce projet axé sur la théorie, je réalise réaliser 100 challenges en Python. Ce site constitue l&amp;#39;un des challenges.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Et regarder le résultat sur la page http://127.0.0.1:8000&lt;/p&gt;
&lt;p&gt;Si vous cherchez des exemples de pages "now", consultez &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://sive.rs/now (la page du boss)&lt;/li&gt;
&lt;li&gt;https://nownownow.com/ (l'annuaire des pages now)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Création d'un premier article&lt;/h4&gt;
&lt;p&gt;Créer un fichier &lt;code&gt;mon-premier-article.md&lt;/code&gt; dans le dossier &lt;code&gt;~/dev/petitalxio/_pelican/content/&lt;/code&gt; : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Title: J&amp;#39;ai publié mon premier article !
Date: 2024-07-27

Hello les amis.

Voici mon premier article de blog. 

Il est expose les différents formats de markdown

Une liste non numérotée : 

&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;a
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b

Une liste numérotée : 

&lt;span class="k"&gt;1.&lt;/span&gt; Cookie
&lt;span class="k"&gt;2.&lt;/span&gt; Beurre

Une citation 

&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;When the student is ready, the master will appear - Bruce Lee&lt;/span&gt;

Un échantillon de code &lt;span class="sb"&gt;`print(&amp;quot;hello&amp;quot;)`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Visualiser et publier les mises à jour&lt;/h4&gt;
&lt;p&gt;Pour visionner le résultat en local, lancer : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio/_pelican
make&lt;span class="w"&gt; &lt;/span&gt;devserver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pour déployer, lancer&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio/_pelican
make&lt;span class="w"&gt; &lt;/span&gt;publish

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio/
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;update content&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Vous avez maintenant toutes les clés en main pour ajouter du contenu sur votre site. &lt;/p&gt;
&lt;p&gt;Pour aller plus loin, &lt;a href="https://docs.getpelican.com/en/stable/content.html"&gt;la documentation&lt;/a&gt; est là pour vous aider.&lt;/p&gt;
&lt;h3&gt;Mettre en forme&lt;/h3&gt;
&lt;p&gt;À présent que l'on a travaillé le fond, passons à la forme. &lt;/p&gt;
&lt;p&gt;Dans cette section, je te propose de faire évoluer le design de notre site personnel vers &lt;a href="https://petitalx.io/"&gt;ce style&lt;/a&gt;, qui est un mélange entre &lt;a href="https://sive.rs/"&gt;le site personnel de Derek Sivers&lt;/a&gt;, et la simplicité de http://bettermotherfuckingwebsite.com/&lt;/p&gt;
&lt;p&gt;Pour cela, on va repartir d'un thème "simple".&lt;/p&gt;
&lt;h4&gt;Mise en place d'un thème explicite&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;récupérer les fichiers suivants https://github.com/getpelican/pelican/tree/main/pelican/themes/simple/templates et les copier dans &lt;code&gt;_pelican/themes/petitalxio/templates&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ajouter &lt;code&gt;THEME = "themes/petitalxio"&lt;/code&gt; dans &lt;code&gt;pelicanconf.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;régénérer le site &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout est à plat ! &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/site-avec-theme-simple.png"&gt;&lt;/p&gt;
&lt;h4&gt;Ajout d'une feuille de style CSS&lt;/h4&gt;
&lt;p&gt;Coller le contenu suivant dans un fichier &lt;code&gt;_pelican/themes/petitalxio/static/style.css&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;650&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Segoe UI&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Helvetica Neue&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Noto Sans&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Apple Color Emoji&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Segoe UI Emoji&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Segoe UI Symbol&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Noto Color Emoji&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;media&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#c9d1d9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#0d1117&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#58a6ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &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;Puis ajouter la référence à la feuille de style dans le fichier template &lt;code&gt;base.html&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    {% block head %}
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ SITENAME|striptags }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;generator&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Pelican&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="cm"&gt;&amp;lt;!-- 👇👇👇👇👇👇👇 --&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{ SITEURL }}/theme/style.css&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="cm"&gt;&amp;lt;!-- 👆👆👆👆👆👆👆 --&amp;gt;&lt;/span&gt;

      &lt;span class="cm"&gt;&amp;lt;!-- ... suite du template --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Régénérer, et le tour est joué.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/site-avec-theme-bettermotherfuckingwebsite.png"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention, pour que pelican embarque la feuille de style sans configuration supplémentaire, il est nécessaire qu'elle se trouve dans un dossier qui soit nommé "static" placé à la racine du thème. L'exemple &lt;code&gt;themes/petitalxio/static/style.css&lt;/code&gt; valide ces conditions. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finalement, lancer les commandes suivantes afin de déployer votre site web :&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;shell
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/dev/petitalxio/_pelican
make&lt;span class="w"&gt; &lt;/span&gt;publish

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;..
git&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt;  &lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;update website&amp;quot;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Simplifier le template&lt;/h4&gt;
&lt;p&gt;On va à présent simplifier les templates suivants : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page.html&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;article.html&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Commençons par le fichier &lt;code&gt;base.html&lt;/code&gt; : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/simplify-base-screenshot.jpg"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;supprimer le bloc &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;supprimer le bloc &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Continuer avec &lt;code&gt;index.html&lt;/code&gt; : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/simplify-index-template.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remplacer le &lt;code&gt;{% block content_title %}&lt;/code&gt;par un simple &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; et en profiter pour traduire le texte&lt;/li&gt;
&lt;li&gt;remplacer la liste de miniatures par une simple liste de titres d'articles&lt;/li&gt;
&lt;li&gt;supprimer le bloc dédié à la pagination car on l'a désactivé pendant le quickstart&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Poursuivre avec le template article.html : &lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/simplify-article-template.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;supprimer les directives liées à la traduction&lt;/li&gt;
&lt;li&gt;simplifier l'affichage du titre pour utiliser une simple balise &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;supprimer les blocs de métadonnée du footer liée à l'auteur, à la catégorie et aux tags&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et terminer avec &lt;code&gt;page.html&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/simplify-page-template.jpg"&gt;&lt;/p&gt;
&lt;h4&gt;Création d'une page Home personnalisée&lt;/h4&gt;
&lt;p&gt;Créer un fichier &lt;code&gt;home.md&lt;/code&gt; dans `_pelican/content/pages/&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---  
Title: Home
---  

&lt;span class="gu"&gt;## Moi en 10 secondes  &lt;/span&gt;

Je suis un développeur Python.  

Je suis né à Rouen.  

Et j&amp;#39;habite actuellement à Rouen.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;[now](now)&lt;/code&gt; résulte en un lien html relatif à la l'url courante. Si on est à l'url &lt;code&gt;petitalx.io/pages/home.html&lt;/code&gt;, cela donne &lt;code&gt;petitalx.io/pages/now&lt;/code&gt;. Le pré-processeur &lt;code&gt;{filename}&lt;/code&gt; ne semble pas fonctionner chez moi.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ajouter les lignes suivantes dans le fichier &lt;code&gt;pelicanconf.py&lt;/code&gt;. Cela va créer une variable globale &lt;code&gt;HOME&lt;/code&gt; qui va contenir le contenu HTML.  &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# En tête de fichier :&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pelican.readers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MarkdownReader&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pelican.settings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_CONFIG&lt;/span&gt;

&lt;span class="c1"&gt;# Plus bas :&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_CONFIG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MarkdownReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;content/pages/home.md&amp;quot;&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 à présent faire injecter le contenu de la variable HOME dans le template de l'index. Ajouter la ligne &lt;code&gt;{{ HOME }}&lt;/code&gt; à la suite de la ligne &lt;code&gt;{% block content %}&lt;/code&gt; dans le fichier &lt;code&gt;_pelican/themes/petitalxio/templates/index.html&lt;/code&gt;. Le début de votre fichier doit alors resembler à cela : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends &amp;quot;base.html&amp;quot; %}  
{% block content %}  

    {{ HOME }}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Régénérer, et admirer le résultat.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/creer-son-site-personnel-avec-pelican-et-ghp-4.png"&gt;&lt;/p&gt;
&lt;h3&gt;BONUS : Code minimalism&lt;/h3&gt;
&lt;p&gt;Ok, on a bien simplifié le design ! &lt;/p&gt;
&lt;p&gt;Maintenant, j'aimerais simplifier le code. &lt;/p&gt;
&lt;p&gt;Cette étape peut paraître superflu. 
Mais vous allez voir, après on se sent mieux.&lt;/p&gt;
&lt;p&gt;Chaque fichier, chaque ligne de code qui est dans notre projet représente un cout. C'est une ligne de plus à lire lorsque l'on cherche à faire évoluer notre site, un fichier en plus à maintenir, une information supplémentaire à traiter.&lt;/p&gt;
&lt;p&gt;Dans cette section, on va éliminer le code inutile. &lt;/p&gt;
&lt;h4&gt;Ne plus générer les pages qui ne nous servent pas&lt;/h4&gt;
&lt;p&gt;Commencer par désactiver la génération des pages "Author", "Authors", "Tag", ... en ajoutant ces lignes au fichier &lt;code&gt;pelicanconf.py&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;TAG_SAVE_AS = &amp;#39;&amp;#39;  
TAGS_SAVE_AS = &amp;#39;&amp;#39;  
CATEGORY_SAVE_AS = &amp;#39;&amp;#39;  
CATEGORIES_SAVE_AS = &amp;#39;&amp;#39;  
AUTHOR_SAVE_AS = &amp;#39;&amp;#39;  
AUTHORS_SAVE_AS = &amp;#39;&amp;#39;  
ARCHIVES_SAVE_AS = &amp;#39;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nettoyer le dossier des templates pour ne conserver que les 4 fichiers suivants : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;templates
├── article.html
├── base.html
├── index.html
├── page.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Nettoyer le fichier de configuration&lt;/h4&gt;
&lt;p&gt;Nettoyer le fichier &lt;code&gt;pelicanconf.py&lt;/code&gt;
&lt;img alt="" src="_assets/pelican-website-conf.jpg"&gt;
- supprimer la variable &lt;code&gt;LINKS&lt;/code&gt; : c'était une variable custom du template initial
- supprimer la variable &lt;code&gt;SOCIAL&lt;/code&gt; : idem, elle ne sert plus
- supprimer la variable &lt;code&gt;DEFAULT_PAGINATION&lt;/code&gt; qui utilise déjà la valeur par défaut
- supprimer le code commenté relatif à &lt;code&gt;RELATIVE_URLS&lt;/code&gt;
- supprimer le lignes qui annulent la génération du feed&lt;/p&gt;
&lt;h4&gt;Nettoyer le Makefile&lt;/h4&gt;
&lt;p&gt;Le Makefile généré par &lt;code&gt;pelican-quickstart&lt;/code&gt; met à disposition des commandes utiles.&lt;/p&gt;
&lt;p&gt;Je te propose de ne conserver que le strict minimum pour développer efficacement : 
- &lt;code&gt;help&lt;/code&gt; : affiche l'aide du Makefile
- &lt;code&gt;clean&lt;/code&gt; : supprime les fichiers HTML et XML de sortie
- &lt;code&gt;devserver&lt;/code&gt; : lance le serveur local et le hot reload
- &lt;code&gt;publish&lt;/code&gt; : génère le site la config serveur
- &lt;code&gt;github&lt;/code&gt; : génère le site et upload notre repository sur Github&lt;/p&gt;
&lt;p&gt;En regardant le détail de commande, on se rend compte que la commande &lt;code&gt;clean&lt;/code&gt; est dangereuse en l'état. Comme on génère le site à la racine du repo, lancer &lt;code&gt;make clean&lt;/code&gt; entraine la suppression de tous nos fichiers !&lt;/p&gt;
&lt;p&gt;Aussi, la commande Github utilise &lt;code&gt;ghp&lt;/code&gt;. Ce n'est pas l'option que nous avons choisi pour déployer. &lt;/p&gt;
&lt;p&gt;Voici la version du fichier mise à jour (à partir de la ligne help) : &lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;button class="copy-button" onclick="copyToClipboard(this)"&gt;Copy code&lt;/button&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Makefile for a pelican Web site                                           &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;                                                                          &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Usage:                                                                    &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;   make clean                          remove the generated files         &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;   make publish                        generate using production settings &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;   make devserver [PORT=8000]          serve and regenerate together      &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;   make github                         upload the web site                &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;                                                                          &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html   &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Set the RELATIVE variable to 1 to enable relative urls                    &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@echo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;                                                                          &amp;#39;&lt;/span&gt;

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;/**.html&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/feeds&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/pages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/theme&amp;quot;&lt;/span&gt;

&lt;span class="nf"&gt;devserver&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PELICAN&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-lr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;INPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;CONFFILE&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PELICANOPTS&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PELICAN&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;INPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PUBLISHCONF&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PELICANOPTS&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;github&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;-A
&lt;span class="w"&gt;    &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Update site&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;GITHUB_PAGES_BRANCH&lt;span class="k"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt; &lt;span class="n"&gt;devserver&lt;/span&gt; &lt;span class="n"&gt;publish&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Enfin, il est aussi possible de supprimer le fichier &lt;code&gt;_pelican/tasks.py&lt;/code&gt; qui fait doublon avec notre Makefile.&lt;/p&gt;
&lt;p&gt;Et voilà.&lt;/p&gt;
&lt;p&gt;Le projet est finalement très simple :)&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Mes notes de second cerveau&lt;/li&gt;
&lt;li&gt;J'ai pris une décision confiant !!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ressources utiles &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La documentation de Pelican : https://docs.getpelican.com/en/stable/index.html&lt;/li&gt;
&lt;li&gt;La documentation de Github Pages : https://pages.github.com/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je pose ici quelques pistes pour aller plus loin avec Pelican : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;créer son propre générateur&lt;/li&gt;
&lt;li&gt;créer son propre plugin&lt;/li&gt;
&lt;li&gt;créer le thème &lt;em&gt;bettermotherfuckingwebsite&lt;/em&gt; pour contribuer&lt;/li&gt;
&lt;li&gt;prendre une &lt;a href="https://github.com/getpelican/pelican/issues"&gt;issue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>13 Enseignements du DevOps</title><link href="https://me.petitapetit.io/devops-takeaways" rel="alternate"/><published>2024-05-21T06:12:57.176000+00:00</published><updated>2024-05-21T06:12:57.176000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-05-21:/devops-takeaways</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Ces derniers mois, j'ai beaucoup lu sur le DevOps. &lt;/p&gt;
&lt;p&gt;Dans l'&lt;a href="https://100produits.substack.com/p/devops-101"&gt;édition précédente&lt;/a&gt;, nous avons approché une outline de la discipline formée par les grands principes et techniques qui la composent. &lt;/p&gt;
&lt;p&gt;Dans cette édition, j'ai regroupé les réalisations que j'ai eues et les concepts qui m'ont le plus marqué …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Ces derniers mois, j'ai beaucoup lu sur le DevOps. &lt;/p&gt;
&lt;p&gt;Dans l'&lt;a href="https://100produits.substack.com/p/devops-101"&gt;édition précédente&lt;/a&gt;, nous avons approché une outline de la discipline formée par les grands principes et techniques qui la composent. &lt;/p&gt;
&lt;p&gt;Dans cette édition, j'ai regroupé les réalisations que j'ai eues et les concepts qui m'ont le plus marqué. &lt;/p&gt;
&lt;p&gt;Avant de plonger, voici un récapitulatif des livres que j'ai lu.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Inspiration et mindset :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;The Phoenix Project&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The DevOps Handbook&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The Goal - A process of ongoing improvement &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Toyota Production System (TPS)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Lean Startup&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Technique et outils :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Quick Start Kubernetes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Learn Kubernetes in a Month of Lunches&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Automating DevOps With GitLab CICD Pipelines&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;OpenShift in Action (beaucoup de contenu déprécié)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Et voici les idées qui m’ont le plus marqué. 🤸&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Les concepts&lt;/h1&gt;
&lt;h2&gt;Shit happens&lt;/h2&gt;
&lt;p&gt;Le fait que des problèmes apparaissent est normal. &lt;/p&gt;
&lt;p&gt;L'enjeux n'est pas de prévenir et d'anticiper tous les problèmes, mais que lorsqu'un problème survient, il soit détecté rapidement, que l'on s'empresse de le résoudre, puis que des mesures soient prises afin que le cas n'apparaisse plus - ou à minima qu'il soit documenté afin que si le problème apparaît de nouveaux, il soit rapide à résoudre.&lt;/p&gt;
&lt;p&gt;Jusqu'à présent, je voyais les problèmes comme des échecs dans le processus de développement. Maintenant, je considère que les problèmes et &lt;strong&gt;les erreurs font partie du processus&lt;/strong&gt;. Ils peuvent être accueillis sans jugement et &lt;strong&gt;l'enjeu est de savoir comment tu y réponds&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Lorsque l'on renverse la perspective, on se rend compte que les problèmes sont même d'excellentes opportunités d'apprendre. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;La roue des problèmes&lt;/h2&gt;
&lt;p&gt;Les problèmes sont le point de départ du processus d'amélioration continue. &lt;/p&gt;
&lt;p&gt;Améliorer un système consiste simplement à :&lt;/p&gt;
&lt;p&gt;1. Identifier les problèmes que l'on rencontre&lt;/p&gt;
&lt;p&gt;2. Les prioriser&lt;/p&gt;
&lt;p&gt;3. Les adresser un par un&lt;/p&gt;
&lt;p&gt;4. Implémenter / diffuser la solution&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Parmi les outils à notre disposition pour identifier les problèmes dans le cycle de développement, on retrouve : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le daily, les rétrospectives, les 1:1, les pre-mortems et les post mortems : pour identifier au plus tôt les problèmes humains ainsi que les blocages et les problèmes d'organisation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;la télémétrie et les outils de monitoring pour identifier les problèmes en production&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les outils d'intégration continue (tests, analyseurs statiques) pour identifier les problèmes dans le code que l'on s'apprête à intégrer dans la codebase.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;l'état d'esprit &lt;em&gt;fail fast&lt;/em&gt; qui consiste à faire exploser les problèmes rapidement afin de réduire la distance entre la détonation et la source du problème&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Pour les prioriser les problèmes identifiés, on peut : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;tenir à jour une liste des problèmes qu’on rencontre&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;utiliser des outils comme StepSize pour suivre les zones de code à refactorer&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Pour les adresser et réussir à avancer, avoir un mindset d'expérimentation aide. Parfois il faut payer pour voir. Lorsque Netflix a produit sa première série, ils pensaient que cela n'allait pas marcher. Ils ont quand même mis le paquet, pour voir, et l'expérimentation c'est avéré un succès. Quand, on en est toujours au même point six mois après avoir identifié un problème, notre système d'amélioration est cassé.&lt;/p&gt;
&lt;p&gt;Pour partager les enseignements au niveau de l'équipe et de l'entreprise, il est intéressant de documenter l'erreur et la solution dans un système de gestion des connaissances accessible à tous comme GitLab, Confluence, Notion. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Mean time to recover (MTTR)&lt;/h2&gt;
&lt;p&gt;Le Mean Time To Recover mesure le temps moyen que met un système à revenir à la normale après l'apparition d'un problème. C'est une métrique clé chez les Top performeurs. Lorsqu'il se compte en secondes, en minutes, en heures, il est un indicateur de bonne performance et de réactivité. Lorsqu'il se compte en jours, en semaines et en mois, il laisse supposer de profonds problèmes d'organisation.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-culbuto.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Mr Culbuto a un très bon MTTR. On peut l’incliner et il revient vite à la normale. Be a culbuto.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le coût du retard à détecter et du report de correction&lt;/h2&gt;
&lt;p&gt;Le coût d'un problème est proportionnel à sa durée d'existence.&lt;/p&gt;
&lt;p&gt;On s'accorde souvent sur le fait que plus un problème est détecté tardivement, plus son coût est élevé. Un bug détecté dans la chaîne d'intégration continue n'impacte personne et est rapide à résoudre car le contexte est encore frais. S'il est détecté en production par les utilisateurs, il a un impact négatif sur leur expérience, sur l'image qu'ils se font du service et il est plus cher à résoudre puisqu'il faut en plus gérer les éventuels dégâts qui ont été causés par le problème.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-bug-cost.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Plus un problème est détecté tardivement, plus son coût de résolution est élevé.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Mais détecter un problème rapidement ne fait pas tout. Il faut ensuite l'adresser. Tant qu'il n'est pas adressé, il reste dans un coin de ta tête. Il se rappelle à toi chaque dans le backlog. Il encombre le work-in-progress. Il devient un cailloux dans la chaussure avec lequel on s'habitue à marcher. Une charge mentale quoi.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;L'enjeu ici est donc &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;de détecter les problèmes au plus tôt, puis &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de les résoudre au plus vite.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Jump into action.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Les kaizen blitzes&lt;/h2&gt;
&lt;p&gt;Un Kaizen Blitz est une période ou l'équipe met en pause de développement de nouvelles fonctionnalités pour régler un problème qu'elle rencontre. &lt;/p&gt;
&lt;p&gt;On peut voir cela comme : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;un sprint de refactoring&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un hackathon d'amélioration continue&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un grand coup de ménage avec un tour de lave-vaisselle&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Voici quelques exemples de Kaizen Blitz : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Le Christmas Refactoring chez Agicap. Après l'arrivée de Thomas Pierrain chez Agicap, les équipes ont mis en pause le développement de nouvelles features pendant 3 semaines au moment de fêtes de Noël afin de se concentrer sur des tâches de refactoring et d'amélioration du code existant. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;L'expérimentation Operation Inversion chez LinkedIn où les équipes ont mis en pause le développement de nouvelles features pendants plusieurs mois afin de concentrer leur énergie sur l'amélioration du pipeline de déploiement et la réduction de la Dette technique. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-clean-blitz.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Logo d’une marque de tablettes de lave-vaisselle&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le Chaos Monkey&lt;/h2&gt;
&lt;p&gt;Il n'y a rien de pire qu'un système de sécurité qui ne fonctionne pas : tu te comportes comme si tu étais en sécurité, mais tu es sans filet.&lt;/p&gt;
&lt;p&gt;Une bonne façon de tester la résistance du système face aux problèmes que l'on s'attend à subir est d'en injecter soi-même régulièrement. Une fois le système testé dans des conditions favorables, il est intéressant de s'intéresser aux scénarios en conditions d'intempéries. &lt;/p&gt;
&lt;p&gt;Le Chaos Monkey de Netflix est une implémentation extrême. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-chaos.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Une illustration du Chaos Monkey de Netflix par sketchplanations.com&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Afin de vérifier que le système a bien le niveau de résilience attendu, il tue volontairement des processus et des serveurs de façon aléatoire. De cette façon, les mécanismes de résilience sont régulièrement sollicités et l'on est serein sur le fait qu'ils fonctionnent bien. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Dans sa démarche d’anti-fragilité, Netflix va même encore plus loin avec toute une armée de monkeys :&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-simians.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Une illustration des différents daemons qui opèrent sur le cloud de Netflix par @geosley&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le Flow&lt;/h2&gt;
&lt;p&gt;Le flow désigne le flux des items dans la chaîne de valeur. &lt;/p&gt;
&lt;p&gt;Tout l'enjeu du Lean Manufacturing, du DevOps et de l'agilité est de fluidifier le flux des opérations pour permettre au système de réagir rapidement aux demandes du marché, tout en réduisant les dépenses opérationnelles et en travaillant avec juste ce qu'il faut d'inventaire.&lt;/p&gt;
&lt;p&gt;Le flow est au centre de l'attention.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le Work In Progress&lt;/h2&gt;
&lt;p&gt;Le work-in-progress (WIP) est la somme des tâches démarrées et non terminées. &lt;/p&gt;
&lt;p&gt;Comme l'explique Erik (le mentor) dans The Phoenix Project :&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Work-in-progress is the silent killer&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Lorsque l'on se retrouve bloqué sur une tâche, nous avons tendance à démarrer de nouvelles tâches plus faciles en attendant de trouver une solution pour la première. On cherche à "paralléliser". Rapidement, les tâches ouvertes et non complétées s'accumulent. Cette accumulation de tâche démarrée ne crée aucune valeur et représente une charge mentale qui grossit et pèse de plus en plus sur l'équipe.&lt;/p&gt;
&lt;p&gt;Limiter le WIP a deux grands bénéfices. D'abord, il permet de réduire l'encombrement dans la chaîne de création de valeur, et ainsi d'améliorer le temps écoulé entre une idée et sa réalisation (lead time). Ensuite, il accentue la gêne causée par les bloqueurs, ce qui les rend d'autant plus urgents à résoudre.&lt;/p&gt;
&lt;p&gt;Taiichi Ohno, un des pères fondateurs du Lean Manufacturing, compare la pratique de limiter le WIP à &lt;em&gt;drainer la rivière de l'inventaire afin de révéler tous les problèmes qui entravent un flux rapide&lt;/em&gt;.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-river.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Limiter le WIP révèle les problèmes&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Limiter le WIP permet d’identifier les problèmes. &lt;/p&gt;
&lt;p&gt;Cela nous permet ensuite de les adresser et de gagner en efficacité.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Bien vivre dans un système complexe&lt;/h2&gt;
&lt;p&gt;Il est possible de travailler dans un système complexe de façon sûre en suivant les principes suivants : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Transparence opérationnelle : les problèmes sont révélés&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Les problèmes sont résolus afin de gagner en connaissance&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Les connaissances crées localement sont diffusées et exploitées globalement&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Les pratiques sont perpétuées par le management&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;La Loi de Gall&lt;/h2&gt;
&lt;p&gt;La loi de Gall est une règle empirique énoncée par John Gall dans _Systemantics: How Systems Really Work and How They Fail_ : &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Dis autrement : si tu veux un système complexe qui fonctionne, construit un système simple qui fonctionne puis améliore-le de façon itérative. Pars de spécifications minimales, et affine. N’essaie pas de résoudre tous les problèmes d’un coup. Identifie les besoins essentiels, réduit le focus, et démarre avec un MVP.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-increments.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Knowledge holders&lt;/h2&gt;
&lt;p&gt;Un détenteur de connaissance est une personne qui accumule du savoir sur le fonctionnement de l'entreprise mais le garde pour lui, de façon intentionnelle ou non. &lt;/p&gt;
&lt;p&gt;Ce type de profil se trouve régulièrement dans une posture de Héro car il est la personne qui te sort de l'ornière. Il est hautement valorisé par l'entreprise, mais il est aussi un de ses points faibles.&lt;/p&gt;
&lt;p&gt;D'abord parce que ces personnes ne sont &lt;strong&gt;pas scalables&lt;/strong&gt;. Lorsque les projets grandissent, elles deviennent un bottleneck car tout le monde dépend de ces personnes pour avancer.&lt;/p&gt;
&lt;p&gt;Ensuite, elles présentent un risque pour l'entreprise, si elles gagnent au Loto.&lt;/p&gt;
&lt;p&gt;Les knowledge holders sont souvent des personnes talentueuses techniquement. Pour capitaliser sur leur connaissance, il est intéressant &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;de tracer toutes les demandes qui leur parviennent puis&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;documenter les actions qu'elles réalisent régulièrement.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Les 4 formes de travail dans l'IT&lt;/h2&gt;
&lt;p&gt;On distingue 4 formes de travail dans l'IT&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;les projets business à destination des clients externes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les projets internes à l'entreprise&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les changements et évolutions&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le travail non planifié (⚠️)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Le travail non planifié est la matière noire du système : il est invisible, mais bien présent. Et &lt;strong&gt;il empêche de travail qui a été planifié d'être exécuté&lt;/strong&gt;. Il est souvent constitué d'incidents de production et de travail urgent mais non important. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-unplanned-work.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Lorsque l’on se retrouve à travailler dans la réaction, aka mode Pompier, les tâches sont dominées par du travail non planifié.&lt;/p&gt;
&lt;p&gt;Le travail non planifié est à traquer et à éliminer. La meilleure façon de le suivre est de rendre le travail visible grâce à des outils de management visuel comme kanban ou scrum board.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;L’Amélioration Continue&lt;/h2&gt;
&lt;p&gt;Améliorer ce que tu fais est plus important que ce que tu fais : peu importe d'où tu pars. Dès lors que tu es capable de t'améliorer, tu es capable de faire mieux. Et plus vite tu t'améliores, plus vite tu dépasses ton niveau initial.&lt;/p&gt;
&lt;p&gt;L'amélioration continue consiste à améliorer les processus et le travail quotidien. C'est un processus et une habitude qui se manifeste par des activités comme les Kaizen Blitzes, les post-mortems, les rétrospectives.&lt;/p&gt;
&lt;p&gt;Le plus gros frein à l’amélioration continue est l’état d’esprit. Si on part du principe qu’une piste de solution à un problème ne va pas marcher, et que l’on préfère rester dans le confort de faire comme on a toujours fait, on stagne. On ne résout pas les problèmes que l’on rencontre. Et par inertie, la situation tend même à se détériorer.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-experimentations.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Une approche constructive consiste à expérimenter de façon structurée en explicitant les hypothèses que l’on cherche à vérifier, en donnant un cadre temporel à l’expérimentation, et en mesurant les résultats à l’issue de l’expérimentation. &lt;/p&gt;
&lt;p&gt;Certaines vont améliorer les choses. D’autres vont les empirer. &lt;/p&gt;
&lt;p&gt;Il s’agit alors d’absorber ce qui fonctionne pour toi, et de rejeter ce qui ne l’est pas.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Voilà pour aujourd'hui. &lt;/p&gt;
&lt;p&gt;J’espère que tu auras trouvé ici certaines idées qui te seront utiles au quotidien :)&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;À la prochaine !&lt;/p&gt;
&lt;p&gt;Alex&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</content></entry><entry><title>DevOps 101</title><link href="https://me.petitapetit.io/devops" rel="alternate"/><published>2024-05-06T06:33:41.967000+00:00</published><updated>2024-05-06T06:33:41.967000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-05-06:/devops</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;C'est dernier mois, je me suis demandé, c'est quoi le DevOps ?&lt;/p&gt;
&lt;p&gt;Est-ce &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;un mouvement ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un ensemble de techniques ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un ensemble de principes ? &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un peu tout cela ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Avant de m'immerger dans la discipline, je percevais avant tout sa dimension outillage : les technologies de virtualisation comme Docker et Kubernetes, les plateformes …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;C'est dernier mois, je me suis demandé, c'est quoi le DevOps ?&lt;/p&gt;
&lt;p&gt;Est-ce &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;un mouvement ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un ensemble de techniques ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un ensemble de principes ? &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un peu tout cela ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Avant de m'immerger dans la discipline, je percevais avant tout sa dimension outillage : les technologies de virtualisation comme Docker et Kubernetes, les plateformes de déploiement continu comme GitLab, les outils quoi.&lt;/p&gt;
&lt;p&gt;Mais comme le dit bien Christopher Little :&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;DevOps isn’t about automation, just as astronomy isn’t about telescopes.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Nous avons tendance à réduire les choses à une partie de leur manifestation. &lt;/p&gt;
&lt;p&gt;C'est réducteur. &lt;/p&gt;
&lt;p&gt;Mais alors, c'est quoi le DevOps ?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Le contexte &lt;/h1&gt;
&lt;h2&gt;Le cœur du système&lt;/h2&gt;
&lt;p&gt;Aujourd'hui, l'IT est au cœur de toutes les entreprises. Que ce soit dans la tech, les assurances, l'automobile, le voyage ou le prêt-à-porter, les capacités d'une entreprise à proposer de nouveaux services, améliorer l'expérience utilisateur ou encore cibler son marché reposent sur la technologie. Aussi, lorsque l'IT échoue, c'est toute l'entreprise qui est mise à mal. &lt;/p&gt;
&lt;h2&gt;La spirale infernale&lt;/h2&gt;
&lt;p&gt;Dans beaucoup d'organisations, un cercle vicieux s'est mis en place. &lt;/p&gt;
&lt;p&gt;L'entreprise a bâti sa richesse sur du code. Au fil des ajouts, ce code est devenu de plus en plus complexe. Par entropie, par manque de connaissances et par absence de priorisation, la qualité tend à diminuer. Au début, cela ne pose pas de problème. Mais par accumulation, la situation se complique. La complexité du projet ne cesse de croître en même temps que sa qualité décroît. On ajoute plus de personnes sur le projet. Cela complique la communication et la coordination. Les équipes subissent du turnover. L'information se perd. Les modifications apportées à ce code hérité tendent à échouer en production et créent des interruptions de service qui impactent les utilisateurs. &lt;/p&gt;
&lt;p&gt;Pour compenser les déconvenues, les équipes promettent davantage. La pression s'accroît pour livrer plus, plus rapidement, et le temps disponible pour rembourser la dette technique se réduit encore.&lt;/p&gt;
&lt;p&gt;Enfin, la situation s'enlise complètement. Alors que le temps nécessaire à intégrer des évolutions s'envole, le temps qui s'écoule entre une idée et sa réalisation  (Lead Time) s'effondre. Rapidement, plus personne n'ose faire de modifications sur la codebase. On touche un minimum de choses pour éviter de tout casser (ce qui implique qu'on ne range plus, et que le désordre grandit). La communication devient compliquée. On passe en mode pompier. C'est-à-dire qu’on passe plus de temps à réagir aux problèmes de production que de répondre au changement attendu par le marché. &lt;/p&gt;
&lt;p&gt;Cette spirale a des coûts humains et économiques importants : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le temps passé sur le maintien en conditions opérationnelles (MCO) et du travail non planifié (Unplanned Work) explose&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;la peur de faire des changements et de faire des erreurs conduit à de l'immobilisme&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le sentiment d'impuissance augmente le risque de burnout&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Cette spirale prend sa source dans un conflit bien connu.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le conflit central&lt;/h2&gt;
&lt;p&gt;Le conflit à l'origine de la spirale infernale de l'IT (The Downward Spiral) est celui qui oppose les développeurs aux opérationnels. Il repose sur la tension entre le besoin de &lt;strong&gt;stabilité&lt;/strong&gt; qui est porté par les équipes opérationnelles (les &lt;em&gt;ops&lt;/em&gt;) et le besoin de répondre au changement en livrant de &lt;strong&gt;nouvelles fonctionnalités&lt;/strong&gt; qui est porté par les développeurs (les &lt;em&gt;devs&lt;/em&gt;). &lt;/p&gt;
&lt;p&gt;Les premiers sont accusés de tout casser et de déployer du code bogué sans réfléchir aux conséquences (YOLO). &lt;/p&gt;
&lt;p&gt;Les deuxièmes, de ne pas être assez réactifs, de traîner à livrer les infrastructures et de ralentir le rythme des livraisons. &lt;/p&gt;
&lt;p&gt;Lorsque le travail est organisé en série, ce deux responsabilités sont isolées dans deux départements distincts. Cette séparation est une source de frictions, de désalignement. Le DevOps vise à faire tomber le mur de la confusion qui les sépare et à recréer de la collaboration entre les deux rôles. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-battle.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Transformation DevOps : du conflit à la connexion.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Dans les années 2010, les géants de la tech comme Meta, Netflix, Etsy, American Airlines, ont entrepris une Transformation Devops et ont inspiré de nombreux départements tech.&lt;/p&gt;
&lt;p&gt;Les principes et les techniques qu'ils ont mis en œuvre sont les suivants.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;La carte mentale du DevOps&lt;/h1&gt;
&lt;p&gt;Comme modélisé par Gene Kim dans son article de 2012, les principes et techniques du DevOps peuvent être classés selon trois axes : le flow, le feedback et l'amélioration.&lt;/p&gt;
&lt;p&gt;Ces axes sont présentés sous une forme narrative dans &lt;em&gt;The Phoenix Project&lt;/em&gt; puis développés plus en détails dans &lt;em&gt;The DevOps Handbook&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;Voici la cartographie que j'ai tirée de ces lectures : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-outline.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Carte mentale du DevOps&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Chaque point pourrait faire l'objet d'un article, aussi, je ne vais pas tout détailler ici. Mais je trouve que cette carte permet déjà de se donner un bon aperçu de ce qu'est le devops, de ses principes et de ses valeurs. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Et donc, c'est quoi le DevOps ?&lt;/h1&gt;
&lt;p&gt;La définition que je retiens est la suivante. &lt;/p&gt;
&lt;p&gt;Le DevOps est un ensemble de principes et de techniques qui permettent de réconcilier le développement avec les opérations, en transformant les conflits en rapports de collaboration, afin d’aligner tout le monde sur les objectifs de l’entreprise. Enfant du &lt;em&gt;lean manufacturing&lt;/em&gt; et du &lt;em&gt;mouvement agile&lt;/em&gt;, il fournit des outils pour travailler de façon confortable et sûre dans un système technologique complexe. &lt;/p&gt;
&lt;p&gt;Dans la prochaine édition, je vous présenterai en détail les concepts qui m'ont le plus marqué ainsi que ceux qui m'ont le plus amusé. &lt;/p&gt;
&lt;p&gt;D’ici là, portez-vous bien :)&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;Alex&lt;br/&gt;&lt;/p&gt;</content></entry><entry><title>Appliquer la Théorie des Contraintes à la création de contenu.</title><link href="https://me.petitapetit.io/toc-contenu" rel="alternate"/><published>2024-04-23T05:38:39.875000+00:00</published><updated>2024-04-23T05:38:39.875000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-04-23:/toc-contenu</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;La semaine dernière, on a passé en revue les principes de la théorie de contraintes. &lt;/p&gt;
&lt;p&gt;Cette semaine, j'ai appliqué la méthode à mon processus de création de contenu. &lt;/p&gt;
&lt;p&gt;Et vous allez voir, les résultats sont surprenants. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Rappels&lt;/h1&gt;
&lt;p&gt;Pour rappel, les grandes étapes de la méthode sont les suivantes : &lt;/p&gt;
&lt;p&gt;1 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;La semaine dernière, on a passé en revue les principes de la théorie de contraintes. &lt;/p&gt;
&lt;p&gt;Cette semaine, j'ai appliqué la méthode à mon processus de création de contenu. &lt;/p&gt;
&lt;p&gt;Et vous allez voir, les résultats sont surprenants. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Rappels&lt;/h1&gt;
&lt;p&gt;Pour rappel, les grandes étapes de la méthode sont les suivantes : &lt;/p&gt;
&lt;p&gt;1. Définir le but&lt;/p&gt;
&lt;p&gt;2. Identifier la contrainte&lt;/p&gt;
&lt;p&gt;3. Exploiter la contrainte&lt;/p&gt;
&lt;p&gt;4. Subordonner les opérations à la contrainte&lt;/p&gt;
&lt;p&gt;5. Élever la contrainte&lt;/p&gt;
&lt;p&gt;6. Répéter avec la contrainte suivante&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Cette méthode comporte 3 grandes phases que je me représente de cette façon : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-double-loop.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;On identifie d'abord le but. Puis la contrainte. On l'optimise en considérant trois angles (exploiter, subordonner, élever). Et on répète avec la contrainte suivante. De temps en temps, il peut être intéressant de vérifier que notre but n'a pas changé. &lt;/p&gt;
&lt;h1&gt;Mise en pratique&lt;/h1&gt;
&lt;h2&gt;Quel est mon but ?&lt;/h2&gt;
&lt;p&gt;Bonne question. &lt;/p&gt;
&lt;p&gt;Est-il &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;de développer ma crédibilité ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de générer des opportunités professionnelles ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;d'apprendre ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de faire de l'argent ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de développer une communauté ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de vendre des produits ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de gagner en statut ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de partager mes expériences ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de mieux réfléchir ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de rencontrer des personnes partageant mon état d'esprit ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de me faire des amis ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Écrire en ligne permet chacune de ces choses. &lt;/p&gt;
&lt;p&gt;Mais je pense que mon principal objectif est d'apprendre. &lt;/p&gt;
&lt;p&gt;Viser de créer du contenu me donne un cadre afin de moins m'éparpiller. Cela me donne une raison supplémentaire de lire. Reformuler ce que je lis me permet d'identifier les parties le plus importantes. Et publier en ligne est un prétexte pour prendre le temps d'étudier les sujets qui sont importants pour moi, aller au fond des choses, et tirer les leçons de mes expériences.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;OK, mon objectif est donc d'apprendre.&lt;/p&gt;
&lt;p&gt;Et ensuite, quelle est ma contrainte ?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Identifier la contrainte&lt;/h2&gt;
&lt;p&gt;Pour identifier ma contrainte, j'ai commencé par mettre à plat ma chaîne de valeur.&lt;/p&gt;
&lt;p&gt;L'image ci-dessus représente le Kanban de mon flux d'apprentissage. Je sélectionne des livres. Je les lis. Je les digère en les décomposant en notes autoportantes (voir &lt;a href="https://100produits.substack.com/p/explorer-le-monde-avec-un-second"&gt;Explorer le monde avec un second cerveau&lt;/a&gt;). Cela me donne des idées d'article. Je les écris un par un, les publie, puis ma stratégie éditoriale est de les décliner sur LinkedIn afin de donner de la visibilité à mes activités.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-learning-pipeline.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Cette mise à plat me permet d'identifier les étapes qui sont mes principales contraintes : &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;l'écriture&lt;/strong&gt; : mes lectures, mes notes et idées d'articles s'accumulent en amont de la phase "écriture" où j'ai actuellement une capacité d'un article par semaine — dans les bonnes semaines.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;la déclinaison en post pour la diffusion&lt;/strong&gt; : depuis janvier, je me suis concentré sur la newsletter afin d'écrire depuis un coin de la carte, sans me confronter à l'idée de m'exposer, et sans chercher à définir une ligne éditoriale claire. &lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;C'est amusant, car les goulots d'étranglement ne sont pas là où je le pensais.&lt;/p&gt;
&lt;p&gt;J'ai tendance à beaucoup lire. Si je me laissais aller, je ne ferais que ça. C'est mon chemin de moindre résistance. Dès que j'ai un instant, je tends naturellement à me mettre dans un endroit calme et plonger dans un livre. &lt;/p&gt;
&lt;p&gt;Prendre des notes, écrire et mettre en pratique sont des activités qui me demandent davantage d'effort. &lt;/p&gt;
&lt;p&gt;Ces dernières semaines, j'ai consacré plus de temps à distiller mes notes car je pensais que c'était là mon principal bottleneck. Mais de toute évidence, mon facteur limitant est au niveau de l'écriture.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Exploiter la contrainte&lt;/h2&gt;
&lt;p&gt;Écrire est ma principale contrainte.&lt;/p&gt;
&lt;p&gt;Avant de considérer d'y consacrer plus de ressources, je peux déjà voir comment tirer le meilleur parti du temps dont je dispose déjà. &lt;/p&gt;
&lt;p&gt;Voici quelques actions que j'ai mises en place : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;alimenter une piscine d'idées&lt;/strong&gt; où je capture les idées d'articles que je pourrais publier afin de toujours avoir un sujet prêt à creuser et en pas me lancer en quête d'idée au moment d'écrire.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;commencer par 10 minutes de méditation&lt;/strong&gt; pour être pleinement concentré pendant 1h&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;couper toutes les notifications de mon téléphone&lt;/strong&gt; pour ne pas être interrompu&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;couper d'internet&lt;/strong&gt; pour ne pas être aspiré dans un &lt;em&gt;rabbit hole&lt;/em&gt;. Le fait de lire régulièrement et de distiller les notes que je prends sur mes lectures assure que je dispose de plus de 50% de la matière première pour mon article au moment de l'écrire. Si une information me manque, je peux toujours faire de tête et vérifier ensuite.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Subordonner les opérations à la contrainte&lt;/h2&gt;
&lt;p&gt;Là, ça devient intéressant. &lt;/p&gt;
&lt;p&gt;Lorsque j'ai commencé à travailler dans mon second cerveau, ma chaîne de valeur devait avoir cette forme-là : une espèce d'entonnoir avec beaucoup d'inputs (livres, podcasts), quelques notes, et peu de publications. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-content-kanban.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Ma liste de livres sélectionnés s'étend loin en dessous de l'écran, les livres lus s'accumulent dans la colonne "en cours de distillation", et on observe un déséquilibre fort entre la quantité d'entrée et la quantité de sorties.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Pour bien fonctionner, le corps a besoin d'inspirer et d'expirer. &lt;/p&gt;
&lt;p&gt;Pour bien fonctionner, le cerveau a besoin d'inspiration et de création. &lt;/p&gt;
&lt;p&gt;Une des révélations du Lean Manufacturing est que le travail qui s'accumule entre deux opérations est une source de gâchis. L'objectif d'une chaîne de production est de fluidifier les opérations et l'écoulement des items. Il est bon d'en avoir un peu de stock entre chaque étape pour constituer un buffer et garantir que l'opérateur en aval puisse continuer à avancer en cas de problème sur l'opérateur en amont. Mais ce buffer ne doit pas dépasser une taille raisonnable. &lt;/p&gt;
&lt;p&gt;Appliqué à mon pipeline, je réalise que lire des dizaines de livres sans prendre des notes, sans les mettre en pratique, et sans partager ce que j'en retiens est une source de gâchis. &lt;/p&gt;
&lt;p&gt;Voici les actions que je mets désormais en œuvre afin de réduire l'empilement en amont de la phase d'écriture : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Durcir les critères de sélection de mes inputs&lt;/strong&gt; : il n’y a rien de pire que de faire bien un truc que tu aurais dû ne pas faire du tout. Je lis essentiellement des livres et essaye de choisir les ouvrages de références. Je me désabonne facilement d'une newsletter ou d'un compte qui ne m'intéresse plus ou ne m'inspire plus.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Réduire le temps passé à consommer&lt;/strong&gt; : tu ne peux pas juste inspirer, inspirer, inspirer. Lorsque je prends les transports, mais ai déjà 4-5 livres en attente d'être distillés, je préfère ne rien faire, contempler le paysage, écouter un divertissement, ou lire de la fiction plutôt qu'ajouter les notes d'un audio book dans le pipeline. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Travailler par séries&lt;/strong&gt; : ouvrir différents sujets conduit à de l'éparpillement et demande un effort de distillation supplémentaire. Dans &lt;a href="https://100produits.substack.com/p/mes-intentions-pour-2024"&gt;mes intentions pour 2024&lt;/a&gt;, j'ai identifié au préalable six sujets que je veux creuser cette année. Pour chaque sujet, j'ai constitué une liste de ressource. Je me discipline à explorer les sujets de façon séquentielle afin de simplifier le travail et distillation et permettre à mon esprit de digérer mes lectures.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Améliorer l'efficacité de ma prise de note&lt;/strong&gt; : une meilleure prise de note simplifie le travail de distillation. En ciblant mieux les informations à retenir (ce qui réduit la quantité de notes à distiller) et en les structurant mieux (ce qui facilite de travail de remise en forme).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Élever la contrainte&lt;/h2&gt;
&lt;p&gt;La façon la plus évidente d'élever cette contrainte est d'allouer plus de ressources à l'écriture. Ici, c'est-à-dire d'y passer plus de temps. &lt;/p&gt;
&lt;p&gt;Les deux premières actions que j'ai prises sont donc de &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;créer du temps pour écrire : une heure chaque jour&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;prioriser ce travail : commencer la journée par cela&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Cependant, ajouter du temps reste une approche "brute force". &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Une autre façon d'élever cette contrainte est d’améliorer ma capacité à écrire de façon à aller plus vite sur la même durée : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;en améliorant ma technique : cela viendra avec le temps&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;en me faisant accompagner : je vais travailler 6 semaines avec &lt;a href="https://jeancharleskurdali.substack.com/"&gt;JCK&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;en me formant : participer à des ateliers, faire des formations, ...&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Répéter avec la nouvelle contrainte&lt;/h2&gt;
&lt;p&gt;J'ai une grande marge d'amélioration sur chaque colonne de ma chaîne de valeur. &lt;/p&gt;
&lt;p&gt;Je peux améliorer &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;ma capacité de lecture&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;la qualité de mes notes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;mon processus de diffusion sur LinkedIn&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;La question à se poser ici est de savoir quel axe est prioritaire. &lt;/p&gt;
&lt;p&gt;Le modèle de la théorie des contraintes nous aide simplement à répondre à cette question et identifier la prochaine étape.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;La plus grande réalisation de ma plongée dans le DevOps est de voir l'inventaire comme un passif plutôt que comme un actif. Grâce à cette perspective, je mets moins d'effort dans la lecture et davantage dans l'écriture. &lt;/p&gt;
&lt;p&gt;Mon objectif est ici d'avoir un flux le plus fluide possible, avec pas plus de 5-6 élément par colonne et un lead time (temps entre le moment pour aller de gauche à droite) le plus court possible. &lt;/p&gt;
&lt;p&gt;Au premier abord, cette approche peut paraître froide et mécanique, mais je pense qu’elle fait également sens d’un point de vue bien-être. J’adore lire. Mais accumuler des lectures que je comprends mal et dont je ne fais rien est surtout une source de frustration. Aborder les choses de façon carré me permet finalement de mieux apprécier ce que je fais.&lt;/p&gt;
&lt;p&gt;À voir dans les prochains mois ce que cela va donner :)&lt;br/&gt;&lt;br/&gt;++&lt;br/&gt;&lt;br/&gt;Alex&lt;/p&gt;</content></entry><entry><title>Produire 2x + grâce à la Théorie des Contraintes</title><link href="https://me.petitapetit.io/toc" rel="alternate"/><published>2024-04-17T05:39:47.941000+00:00</published><updated>2024-04-17T05:39:47.941000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-04-17:/toc</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Depuis le mois de février, mon focus est d'apprendre le DevOps. &lt;/p&gt;
&lt;p&gt;En m'attaquant à cette compétence, je pensais baigner dans de la technique. &lt;/p&gt;
&lt;p&gt;Et il y en a. &lt;/p&gt;
&lt;p&gt;Mais j’ai aussi découvert une dimension culturelle super importante. &lt;/p&gt;
&lt;p&gt;Remonter aux origines du DevOps m'a conduit au Lean Manufacturing et …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Depuis le mois de février, mon focus est d'apprendre le DevOps. &lt;/p&gt;
&lt;p&gt;En m'attaquant à cette compétence, je pensais baigner dans de la technique. &lt;/p&gt;
&lt;p&gt;Et il y en a. &lt;/p&gt;
&lt;p&gt;Mais j’ai aussi découvert une dimension culturelle super importante. &lt;/p&gt;
&lt;p&gt;Remonter aux origines du DevOps m'a conduit au Lean Manufacturing et à des livres comme &lt;em&gt;Toyota Production System - Beyond large scale production&lt;/em&gt; de Taiichi Ohno ou encore &lt;em&gt;The Goal - A processus of ongoing improvements&lt;/em&gt; de Goldratt Eliyahu.&lt;/p&gt;
&lt;p&gt;Une des obsessions du Lean est de fluidifier les flux de production (le flow). &lt;/p&gt;
&lt;p&gt;Dans cette édition, on va voir comment des principes simples mais parfois contre-intuitifs nous permettent d'y parvenir. &lt;/p&gt;
&lt;p&gt;Dans cette édition, on s'attaque à la Théorie des Contraintes.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Le livre&lt;/h1&gt;
&lt;p&gt;The Goal est une &lt;em&gt;business fiction&lt;/em&gt; écrite par Eliyahu M. Goldratt, un scientifique et gourou de la gestion d'entreprise Israëlien et Jeff Cox. Il a été publié pour la première fois en 1984, introduit la Théorie des Contraintes, et a été vendu à plus de 10 millions d'exemplaires. &lt;/p&gt;
&lt;p&gt;L’intrigue du livre est la suivante : Alex Rogo est directeur d'usine dans une ville avec un grand passé industriel mais un avenir incertain. Il aime l'atmosphère de l'usine, et s'applique dans son travail, mais les résultats ne sont pas au rendez-vous. Le site est menacé de fermeture. Il dispose de trois mois pour renverser la vapeur.&lt;/p&gt;
&lt;p&gt;Au hasard d'un voyage, il recroise Jonah. Son ancien professeur de physique est aujourd'hui consultant pour de grandes entreprises. Jonah porte la connaissance de Goldratt et sera son mentor tout au long du récit. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;La méthode&lt;/h1&gt;
&lt;p&gt;Dans The Goal, Goldratt expose une méthode d’amélioration en 1+5 étapes : &lt;/p&gt;
&lt;ol start="0"&gt;&lt;li&gt;&lt;p&gt;Définir l’objectif&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Identifier la contrainte&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Exploiter la contrainte&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Subordonner à la contrainte&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Élever la contrainte&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Répéter&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Préalable : définir l'objectif&lt;/h2&gt;
&lt;p&gt;Cela paraît évident, mais la question n'est pas triviale.&lt;/p&gt;
&lt;p&gt;Que cherche-t-on à réaliser, exactement ?&lt;/p&gt;
&lt;p&gt;Dans les premiers chapitres de &lt;em&gt;The Goal&lt;/em&gt;, Jonah interroge Alex. Et Alex s'interroge. &lt;/p&gt;
&lt;p&gt;Quel est le premier but d'une entreprise ? &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;fournir des emplois ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;la technologie ? &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;réduire les coûts ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;produire ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;implémenter les bonnes pratiques ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;être &lt;em&gt;compliant&lt;/em&gt; ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;satisfaire ses utilisateurs ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire de l'argent ? &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Vous avez un avis ? &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Pour ma part, je n'étais pas bien sûr lorsqu'ils ont soulevé la question. &lt;/p&gt;
&lt;p&gt;Et ce manque de certitude est intéressant car souligne plusieurs problèmes. &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;Derrière les évidences, l'objectif n'est pas toujours clair. Or, il est difficile d'atteindre une cible lorsqu'on ne sait pas où elle est. À l'aveugle, on peut vite se retrouver à lancer des fléchettes en face alors que la cible est dans notre dos. Pour aller d'un point A à un point B, il faut commencer par définir le point B.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Si chaque personne a une vision différente du but de l'entreprise, il va être difficile d'avancer tous ensemble dans la même direction.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/devops-alignement.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Mettez-vous d’accord ^^’.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;La réponse de Jonah est simple. &lt;/p&gt;
&lt;p&gt;Les entreprises poursuivent un seul but, et ce but est généralement le même : &lt;/p&gt;
&lt;div class="pullquote"&gt;&lt;p&gt;“faire de l'argent”&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Fournir des emplois, la technologie, produire, implémenter les bonnes pratiques, être &lt;em&gt;compliant&lt;/em&gt; et satisfaire les utilisateurs sont des moyens valables de parvenir au but de l’entreprise mais ne sont pas son objectif premier.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Étape 1 : Identifier la contrainte&lt;/h2&gt;
&lt;p&gt;On connaît désormais notre objectif. 🎉&lt;/p&gt;
&lt;p&gt;L'étape suivante consiste à identifier ce qui nous freine vers notre objectif : la contrainte, que l'on peut aussi voir comme un goulot d'étranglement (bottleneck).&lt;/p&gt;
&lt;p&gt;Pour la repérer, nous allons visualiser notre système et introduire la notion de chaîne de valeur. &lt;/p&gt;
&lt;p&gt;La chaîne de valeur est l'ensemble des étapes qui vont des premières étapes de la création de valeur à la livraison chez le client. &lt;/p&gt;
&lt;p&gt;Comme le dit la sagesse populaire et le confirme la physique, la force d'une chaîne dépend de son maillon le plus faible. De la même façon, le débit d'un système dépend de son élément le plus contraignant.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Quelques simulations&lt;/h3&gt;
&lt;p&gt;L'essence de la théorie des contraintes est résumée par cette citation de Goldratt : &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;In any value stream, there is always a direction of flow, and there is always one and only constraint. Any improvement not made at that constraint is an illusion. If we improve a work center that is positioned before the constraint, work will merely pile up at the bottleneck even faster, waiting for work to be performed by the bottlenecked work center. On the other hand, if we improve a work center positioned after the bottleneck, it remains starved, waiting for work to clear the bottleneck. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Voici un système simple avec trois éléments, une direction de flow et une contrainte. Dans la suite, nous allons faire varier ses caractéristiques et mesurer le débit de chaque version. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim6.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;La contrainte (an centre) détermine le débit du système — Débit = 56&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Si l’on optimise le système en &lt;strong&gt;aval&lt;/strong&gt; de la contrainte, on obtient le flux suivant :&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim2.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Simulation d’une optimisation en aval de la contrainte : l’amélioration en aval n’a aucun impact sur le débit du système. — Débit = 53&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Si l’on optimise le système en &lt;strong&gt;amont&lt;/strong&gt; de la contrainte, on obtient le flux suivant :&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim3.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Simulation d’une optimisation en amont de la contrainte. L’amélioration en amont a en fait un impact négatif sur le débit du système. — Débit = 43&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Jusque-là, ces simulations confirment l’affirmation de Goldratt. Les améliorations en amont et en aval n’ont pas donné de résultats positifs. Pour obtenir une amélioration globale, on va commencer par identifier la contrainte. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Mettre le doigt sur le bottleneck&lt;/h3&gt;
&lt;p&gt;Dans un système matériel comme usine, les goulots d'étranglement sont simples à identifier : il suffit de se promener dans l'usine. Lorsque tu tombes sur une machine avec beaucoup de travail qui s'accumule devant, tu tiens ton bottleneck.&lt;/p&gt;
&lt;p&gt;Dans un système virtuel, un travail préalable est nécessaire pour &lt;strong&gt;rendre le travail visible&lt;/strong&gt;. Les contours de la chaîne de valeur peuvent être dessinés en réunissant les parties prenantes du système dans un atelier. Ensuite, le flux des opérations peut être monitoré avec des techniques de management visuelles comme Kanban.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-trello.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Illustrations d’un Kanban pour le développement d’une MarketPlace. Les tickets sont insérés à gauche puis déplacé dans chaque colonne jusqu’au déploiement. Si le travail s’accumule à l’étape “à déployer” (DevDone), c’est que le pipeline de déploiement est un bottleneck :).&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Le bottleneck peut être n'importe où sur ta chaîne de valeur.&lt;/p&gt;
&lt;p&gt;Et il peut être de différente nature : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;une machine critique&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;une personne&lt;/strong&gt; : dans les équipes informatiques, il arrive que certaines personnes soient le bottleneck. Loin d'être des incompétents, ce sont en général des personnes talentueuses. Elles connaissent bien le système et l'entreprise repose dessus pour résoudre ses problèmes. Ce sont des &lt;em&gt;Knowledge Holders&lt;/em&gt;. Mais ces personnes sont une faiblesse dans le processus car elles sont les seules à savoir réaliser certaines opérations. Or leur temps disponible n'est pas scalable et elles présentent un risque pour l'entreprise si elles gagnent au Loto. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;le marché&lt;/strong&gt; : la demande du marché borne ta capacité de vente. Si tu produis plus que ce dont le marché a besoin, alors tes inventaires vont exploser. L'argent que tu investis reste coincé dans le système, et la santé financière de l'entreprise est en danger.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim4.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Lorsque le marché est le bottleneck.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Une fois la contrainte identifiée, on va pouvoir commencer à optimiser le processus. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Aparté : les optimisations aveugles dans le logiciel&lt;/h3&gt;
&lt;p&gt;Notons encore une fois que commencer à "optimiser" le système avant ce travail d'identification est une source de gâchis. &lt;/p&gt;
&lt;p&gt;Il faut garder en tête que&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Travailler sur des parties non-critiques représente un &lt;strong&gt;coût d'opportunité&lt;/strong&gt; : l'effort aurait pu être dépensé ailleurs et apporter davantage de résultats.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tout à un prix&lt;/strong&gt;. Le revers des optimisations est qu'elles se font souvent au prix de coupler le code et de dénormaliser des structures de données. Cela améliore les performances mais réduit la maintenabilité et complexifie le débogage.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Par exemple, le Domain Driven Design (DDD) est une approche du design logicielle qui supporte le bien la complexité métier. La contrepartie du DDD est que les performances sont réputées mauvaises. La maintenabilité se fait au prix de la performance. À l’inverse, le code assembleur permet d'obtenir des performances incomparables. Mais il est très difficile de lire, d'écrire et de maintenir un programme en assembleur. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Étape 2 : Exploiter la contrainte&lt;/h2&gt;
&lt;p&gt;La contrainte détermine le débit du système. &lt;/p&gt;
&lt;p&gt;Comment tirer un maximum de la contrainte ?&lt;/p&gt;
&lt;p&gt;On peut ici appliquer 4 techniques : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;le buffer.&lt;/strong&gt; S'assurer que la contrainte à toujours au moins un élément à traiter : une heure perdue sur la contrainte est une heure perdue dans tout le système. La contrainte ne doit jamais être en attente.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;l’offload.&lt;/strong&gt; Décharger la contrainte : si certaines tâches réalisées par la contrainte peuvent être réalisées par d'autres ressources alors leur réaffecter. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;les contrôles.&lt;/strong&gt; Placer les contrôles qualités en amont de la contrainte : si les inputs de la contrainte sont défectueux et que le défaut est détecté après être passé par la contrainte, le produit sera rejeté et on aura gâché un temps précieux.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;la protection.&lt;/strong&gt; Empêcher le &lt;em&gt;travail non planifié&lt;/em&gt; de perturber les tâches importantes. Le travail non planifié est la matière noire du système. Il est souvent invisible et chasse le travail planifié. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;La vidéo ci-dessus illustre l’impact du travail non planifié sur la fluidité du système.&lt;/p&gt;
&lt;div class="native-video-embed" data-attrs='{"mediaUploadId":"0aaeb071-8c99-4afd-84cf-654d538e1575","duration":null}' data-component-name="VideoPlaceholder"&gt;&lt;/div&gt;
&lt;p&gt;Sur la route 1, on observe le travail tiré du backlog. &lt;/p&gt;
&lt;p&gt;Tout se passe bien, jusqu’à l’entrée en jeu de la route 2. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Étape 3 : Subordonner les opérations à la contrainte&lt;/h2&gt;
&lt;p&gt;Cette étape est la plus contre-intuitive. &lt;/p&gt;
&lt;p&gt;Une des grandes leçons du Lean Manufacturing est que l'inventaire est un passif, et que les excès d’inventaire sont une source de gâchis : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;lorsque notre liste de tâches à accomplir s'accumule, cela génère du stress et nous rend plus susceptible de commettre des erreurs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;lorsque la quantité de pièces "à traiter" s'accumule devant une étape de la production, cela génère du stock. Lorsque ce stock grandit, il faut le gérer : créer un endroit pour le stocker, le stocker, le ressortir au bon moment. Cela entraîne des coûts de manutentions et des risques d'abîmer les pièces.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Produire à fond la caisse en amont de la contrainte est une source de gâchis. &lt;/p&gt;
&lt;p&gt;Parfois, il vaut mieux ne rien faire.&lt;/p&gt;
&lt;p&gt;Le lead-time est le temps moyen que prend un élément pour aller de gauche à droite de la chaîne de valeur. Le stock fait exploser le &lt;em&gt;lead-time&lt;/em&gt; et crée des bouchons dans le pipeline.&lt;/p&gt;
&lt;p&gt;Subordonner les opérations à la contrainte permet de garder un bon lead-time. &lt;/p&gt;
&lt;p&gt;Un bon lead-time permet de "win in the marketplace".&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim5.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Ralentir la production en amont de la contrainte et produire plus. — Débit = 74&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Étape 4 : Élever la contrainte&lt;/h2&gt;
&lt;p&gt;C'est le moment de sortir le cric.&lt;/p&gt;
&lt;p&gt;Une façon d'augmenter le débit des opérations est simplement d'augmenter la capacité de la contrainte. &lt;/p&gt;
&lt;p&gt;Dans le cas d'une machine, il s'agit d'en ajouter une de même capacité en parallèle. Dans le cas &lt;em&gt;d'un détenteur de connaissance&lt;/em&gt;, il s'agit de documenter chaque action qu'il réalise. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim7.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Étape 6 : Répéter avec la nouvelle contrainte&lt;/h2&gt;
&lt;p&gt;La théorie des contraintes est un processus d'amélioration continu. Une fois le bottleneck traité, un autre va apparaître à un autre endroit de la chaîne de valeur. C'est désormais cette contrainte qui va dimensionner le flux des opérations. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Lorsque l’on cherche à améliorer un système, notre première réaction est souvent de chercher à ajouter de la capacité. C’est notre tendance à penser de façon additive. &lt;br/&gt;&lt;br/&gt;Ce que nous montre la théorie des contraintes, c’est que cette stratégie n’est pas la seule façon d’atteindre nos objectifs. Conduite à l’aveugle, ajouter de la capacité peut même desservir le système.&lt;/p&gt;
&lt;p&gt;Une fois clarifié notre but et identifié la contrainte, plusieurs approches s’offrent à nous. Désoptimiser des ressources en amont pour les mettre au service de la contrainte. Adapter la cadence à la contrainte. Décharger la contrainte. Protéger son temps. Ou enfin augmenter la capacité de la contrainte. &lt;/p&gt;
&lt;p&gt;Un des grandes leçons que je retiens de cette théorie est que tu peux faire mieux avec ce que tu as déjà. Dans bien des cas, il suffit de réorganiser les ressources existantes et de corriger les déséquilibres pour améliorer le système.&lt;/p&gt;
&lt;p&gt;Dans le prochain épisode, je te partagerai comment j’ai appliqué les principes de la Théorie des contraintes à mon processus de création de contenu et les changements surprenants que cela m’a amené à considérer.&lt;/p&gt;
&lt;p&gt;D’ici là, est-ce que tu vois un domaine ou tu pourrais l’appliquer ?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Portez-vous bien et à la semaine prochaine :)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Alex&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-button-wrap" data-attrs='{"url":"https://100produits.substack.com/p/produire-2x-grace-a-la-theorie-des?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share","text":"Share"}' data-component-name="CaptionedButtonToDOM"&gt;&lt;div class="preamble"&gt;&lt;p class="cta-caption"&gt;Thank you for reading 100 produits. This post is public so feel free to share it.&lt;/p&gt;&lt;/div&gt;&lt;p class="button-wrapper" data-attrs='{"url":"https://100produits.substack.com/p/produire-2x-grace-a-la-theorie-des?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share","text":"Share"}' data-component-name="ButtonCreateButton"&gt;&lt;a class="button primary" href="https://100produits.substack.com/p/produire-2x-grace-a-la-theorie-des?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share"&gt;&lt;span&gt;Share&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Bonus &lt;/h1&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/lxnd-dune/theory-of-constraints"&gt;le code que j’ai utilisé pour créer les animations est disponible ici&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;l’autre hack pour optimiser le système est de réduire la taille des lots :&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/toc-anim1.gif"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Lorsque l’on réduit la taille des lots, il même possible d’augmenter la cadence sans entraîner de blocage dans le système.&lt;/p&gt;&lt;/div&gt;</content></entry><entry><title>Comment se ruiner (sans l'aide de la chance)</title><link href="https://me.petitapetit.io/vaucelles" rel="alternate"/><published>2024-04-08T06:57:37.128000+00:00</published><updated>2024-04-08T06:57:37.128000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-04-08:/vaucelles</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Dans cette édition, je te propose un post-mortem de mon premier achat immobilier. &lt;/p&gt;
&lt;p&gt;Mon objectif ici est de&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;identifier les erreurs qui m'ont amené à cette situation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;comprendre comment ne pas les reproduire &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;C'est parti.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;Fin novembre 2021, je valide ma période d'essai en CDI. Après de premiers …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Dans cette édition, je te propose un post-mortem de mon premier achat immobilier. &lt;/p&gt;
&lt;p&gt;Mon objectif ici est de&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;identifier les erreurs qui m'ont amené à cette situation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;comprendre comment ne pas les reproduire &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;C'est parti.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;Fin novembre 2021, je valide ma période d'essai en CDI. Après de premiers pas en sortie d'école difficiles, j'obtiens enfin la stabilité que je recherchais. Et je vais pouvoir faire un prêt ! &lt;/p&gt;
&lt;p&gt;Avec un salaire de 45 000 € brut annuel, j'ai l'impression d'être le roi du monde. En tout cas, je me dis que ce salaire Parisien doit être au-dessus du salaire moyen Caennais et que cela devrait me permettre de trouver un super appart.&lt;/p&gt;
&lt;p&gt;Pendant les vacances de Noël 2021, on voit une annonce pour une maison en copropriété. &lt;/p&gt;
&lt;p&gt;L'annonce nous interpelle. &lt;/p&gt;
&lt;p&gt;Cela lance les recherches. &lt;/p&gt;
&lt;p&gt;Pendant un mois, on se plonge à corps perdu dans la recherche de bien. On multiplie les visites. Les taux d'emprunt menacent de remonter. Le marché est tendu. Il y a beaucoup d'acquéreurs mais peu de biens disponibles à la vente. Il faut être réactif. &lt;/p&gt;
&lt;p&gt;Après un mois de recherches, le bien que l'on avait vu en décembre revient sur le marché. Je m'étais donné un mois pour trouver. On le visite. Et on se lance. &lt;/p&gt;
&lt;p&gt;Pendant les travaux, on découvre des problèmes de structures sur un plafond partagé avec deux appartements voisins. On alerte le syndicat de copropriété. &lt;/p&gt;
&lt;p&gt;Puis les mauvaises découvertes s'enchaînent. &lt;/p&gt;
&lt;p&gt;Un maître d’œuvre est mandaté pour étudier les travaux à réaliser et obtenir des devis. &lt;/p&gt;
&lt;p&gt;Il rend sa liasse en septembre 2023&lt;/p&gt;
&lt;p&gt;Nous sommes en mars 2024. Les travaux n'ont toujours pas été votés. Encore moins programmés. Et la gestionnaire du syndic vient de démissionner. &lt;/p&gt;
&lt;p&gt;Il est clair que je n'ai pas eu de chance. &lt;/p&gt;
&lt;p&gt;Mais il est aussi clair que j'ai fait beaucoup d'erreurs. &lt;/p&gt;
&lt;p&gt;C'est parti pour la revue.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Créer les conditions pour se planter&lt;/h1&gt;
&lt;h2&gt;1 - Ne pas se préparer&lt;/h2&gt;
&lt;p&gt;Pour commencer, je me suis lancé dans ce projet sans aucune préparation : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;je n'y connaissais rien en immobilier&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je n'avais qu'une idée vague de mon budget&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;mon apport n'était pas créé&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Ce manque de préparation m'a pénalisé à plusieurs niveaux. &lt;/p&gt;
&lt;p&gt;Premièrement, j'ai commencé à constituer l'apport au début du projet d'achat puis terminé de le constituer pendant la période de recherche de prêt. C'est passé, mais cela a largement réduit mes options d'achat. &lt;/p&gt;
&lt;p&gt;Ensuite, je n'avais aucune idée des pièges à éviter. &lt;/p&gt;
&lt;p&gt;Voici les deux choses que je ferai différemment aujourd'hui : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Acquérir les bases de l'immobilier&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Utiliser le principe d'inversion pour identifier les principales erreurs à ne pas commettre&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Afin de dégrossir un nouveau sujet, une méthode simple consiste à : &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;lister 10 des vidéos les plus vues de la thématique sur Youtube&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les regarder plus ou moins en accéléré et lister les concepts&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;organiser le champs des concepts sur une Mind Map&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;regarder les vidéos les plus vues sur chaque concept&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Dans le cadre de cet article, j'ai pris 2h pour réaliser les 3 premières étapes : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/vaucelles-research.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Les grands concepts à creuser pour préparer un achat immobilier&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;C'est tout bête, mais ce travail préparatoire aurait déjà pu m'éviter bien des ennuis.&lt;/p&gt;
&lt;p&gt;Une fois le cadre posé, il est intéressant de consacrer encore une heure à identifier les erreurs à éviter absolument. C'est ce que l'on appelle le principe d'inversion. &lt;/p&gt;
&lt;p&gt;Lorsqu'il s'est préparé à traverser l'Amazonie, l'explorateur Mike Horn s'est concentré sur une chose : identifier les aliments qui pouvaient le faire mourir. Plutôt que de mémoriser tous les aliments comestibles (qui sont plus nombreux), il s'est concentré sur ceux à éviter absolument (moins nombreux).&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/vaucelles-dying.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Mémoriser la liste des plantes à éviter plutôt que la liste des plantes OK.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;2 - Être dans l'urgence et la peur de passer à côté&lt;/h2&gt;
&lt;p&gt;En plus de ne pas se préparer, je me suis placé/trouvé dans une situation d'urgence :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;les taux d’emprunt menaçaient de remonter de façon imminente&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le marché était déséquilibré et il fallait être réactif &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je voulais trouver rapidement pour pouvoir changer de travail (et casser mon CDI)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je me suis donné un mois pour trouver&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;J'avais peur de passer à côté d'une bonne affaire. Peur de ne plus avoir d'occasion d'investir si je ne le faisais pas maintenant. Et je voulais tout faire en même temps. Acheter un bien. Trouver un nouveau travail. M'investir dans l'amélioration de mes compétences professionnelles.&lt;/p&gt;
&lt;p&gt;Les situations d'urgence créent les pires conditions pour prendre des décisions. &lt;/p&gt;
&lt;p&gt;Ce que je retiens ici est que&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;pour un achat conséquent comme une maison, il vaut mieux rater une bonne affaire que de faire une mauvaise affaire. Parce que des bonnes affaires, il y en aura toujours. Une mauvaise affaire peut foutre en l'air ta vie sur les 20 prochaines années. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;la peur de passer à côté est un guide terrible. Il est normal d'avoir peur lorsque tu t'engages dans un projet important. S'éloigner de sa zone de confort crée de l'insécurité. Mais la peur de manquer est différente. C'est une énergie négative. Lorsque tu dois choisir de t'engager ou non dans une situation, et que tes motivations sont portées par la peur de manquer plutôt que l'envie d'y aller, n'y va pas.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Voici deux techniques que j'ai trouvées pour réduire ma peur de manquer : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;S'exposer à l'inconfort&lt;/strong&gt;. C'est une des réflexions que je me fais en trek. Lorsque je suis sur un canoë, que je dors en tente, et mange de façon rustique. Je n'ai pas grand-chose, mais je suis bien. Cela me rappelle que j'ai besoin de peu de choses pour vivre et être heureux. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explorer plutôt que posséder&lt;/strong&gt; : posséder, c'est interdire aux autres d'accéder à une ressource. Explorer, c'est aller sur des chemins que de toute façon peu de personnes empruntent. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Se couper des générateurs de peur&lt;/strong&gt; : chaînes d'information, journaux, créateur de contenu qui jouent sur la peur de manquer pour capter l'attention. Je me désabonne/bloque systématiquement de ce type de compte. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;3 - Tomber dans le biais des coûts irrécupérables&lt;/h2&gt;
&lt;p&gt;Le biais des coûts récupérables est un biais qui consiste à prendre une décision sur la base des coûts déjà investis plutôt que sur une analyse de la situation à l'instant présent et l'évaluation des meilleures options. &lt;/p&gt;
&lt;p&gt;Voici comment il a opéré dans mon cas. &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;Nous avons dédié un mois intense à cette recherche. Comme changer de travail était aussi une de mes priorités, je me suis retrouvé face à ce dilemme : prendre dans ce qu'il y a, ou reporter le projet d'un an ou plus. Clairement, le fait d’avoir investi beaucoup de temps et d'énergie dans ce mois a influencé ma décision. Je n'avais pas envie d'avoir "perdu" mon temps. Or, c'est une erreur. Cette phase nous a permis d'apprendre et de clarifier notre projet, ce qui nous aurait permis de mieux nous préparer pour la suite. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Un autre phénomène en jeu est qu'au fil que le projet avance, de plus en plus de personnes sont embarquées dans le projet : la banque, l'agence immobilière, les vendeurs. Mettre fin au projet implique d'infliger des pertes à ces personnes. Ce qui ajoute une pression pour aller au bout. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le jour de la signature de l'acte définitif, j'ai eu une dernière occasion de me retirer. Le notaire m'a pris de court en ne me l'annonçant que sur le moment et en me faisant signer une procuration. Mais j'aurais tout de même pu me retirer. La encore, ma décision a été influencée par le temps que j’avais déjà investi dans ce projet. Le temps passé à faire visiter la maison par des artisans, le temps passé à réfléchir aux aménagements. Et puis j'avais envie de passer à l'action. J'avais envie de me lancer dans un projet de restauration. Je me suis dit que trop attendre les conditions parfaites conduisait à l'inaction. Alors je me suis lancé.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Le biais des coûts irrécupérable est un mécanisme est au centre de beaucoup d’échecs de grandes ampleurs.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Dans The Personal MBA, Josh Kaufman raconte comment son grand-père qui s'était engagé dans un projet de construction ambitieux a coulé sa boîte en persistant et escaladant les investissements. Alors que rapidement, il était clair que le projet partait dans une mauvaise direction.&lt;/p&gt;
&lt;p&gt;L'application Nibble présente le cas du concorde. La France et l'Angleterre ont investi des sommes importantes pour rassembler les meilleurs et construire l'avion le plus rapide du monde. Après le prototypage, ils ont identifié plusieurs problèmes, parmi lesquels : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;l’impossibilité de voler sur le territoire : lorsqu'il passe le mur du son, un bang supersonique se produit. Il n'est pas concevable de faire cela au-dessus des villes. La carte des vols possibles a donc été réduite aux vols offshore. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;une capacité d'embarquement réduire : de par sa forme dynamique, le nombre de personnes qu'il était possible d'embarquer à bord était limitée. Ce paramètre rendait encore plus difficile d'amortir le coût du vol.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Ces éléments d'infaisabilité économique auraient dû sonner la fin du projet. On aurait pu se dire, “On a essayé, ça ne marche pas pour les raisons suivantes, projet suivant”. Mais par égo, par aversion à la perte, et au regard des sommes déjà investies, nous avons poursuivi. Ce qui a encore multiplié les pertes par 10.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;La bonne façon d'aborder un problème est donc d'ignorer les sommes déjà investies.&lt;/p&gt;
&lt;p&gt;Et de se poser la question. Indépendamment des montants déjà investis.&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;quel est le meilleur investissement que je peux faire de mon temps aujourd'hui ? &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;quel est le meilleur investissement que je peux faire de mon argent aujourd'hui ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Peut-être que ce sera de poursuivre. &lt;/p&gt;
&lt;p&gt;Peut-être que ce sera de pivoter et partir sur autre chose. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;4 - Ne pas écouter son intuition et ses émotions&lt;/h2&gt;
&lt;p&gt;Mon intuition m'a plusieurs fois lancé des signaux négatifs : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;lors de la visite je n'étais pas enthousiaste&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le jour de la signature, je n'étais pas "heureux"&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;day 1, j'avais déjà des angoisses liées à certains éléments de structure &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Je me suis rassuré en me disant qu'en passant par une agence sérieuse cela réduisait le risque. Qu'au pire, je revendrais. Et qu'on arriverait toujours à en tirer quelque chose bien décoré.&lt;/p&gt;
&lt;p&gt;Aujourd'hui, je suis plus à l'écoute de ces intuitions. &lt;/p&gt;
&lt;p&gt;Si je ne le sens pas, ou que profondément je n'ai pas envie. Je n'y vais pas. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;J'ai signé l'acte définitif mi-avril 2022. Cela va donc faire deux ans que je suis propriétaire du bien. Il n'est pas habitable tant que les travaux n'ont pas été réalisés. Et je ne peux pas le vendre non plus à cause de l'incertitude qui plane au-dessus. &lt;/p&gt;
&lt;p&gt;À ce stade, les impacts négatifs sont les suivants&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;un coût d'opportunité de 20 000 € (800€ x 24 mois) &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des dizaines de journées passées en travaux, en relance, en visite d'artisan, ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un coût psychologique : charge mentale, stress lié à l'incertitude et au fait d'être coincé&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un mariage annulé&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des travaux importants à prévoir (la liasse pèse 160 000 € avant répartition)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Après, comme toute expérience, elle vient avec son lot de choses positives : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;je rêve de choses moins matérielles &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je vis dans un loft industriel, comme dans un film&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je nage régulièrement&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Par chance, je ne suis pas sans ressources. Même si les pertes sont importantes, je devrais m'en sortir. Mais je réalise qu'un mauvais achat peut littéralement détruire ta vie. &lt;/p&gt;
&lt;p&gt;L'année dernière il s'est passé 6 mois où j'étais à la fois au chômage. &lt;/p&gt;
&lt;p&gt;Certains jours, cette charge financière me donnait le vertige. &lt;/p&gt;
&lt;p&gt;Mais cela permet aussi de se recentrer sur l'essentiel. De trouver son minimum vital.&lt;/p&gt;
&lt;p&gt;Il est possible d'aller plus loin dans les apprentissages. &lt;/p&gt;
&lt;p&gt;Mais en voici déjà quelques-uns.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;À la prochaine :) &lt;/p&gt;
&lt;p&gt;Alex&lt;/p&gt;</content></entry><entry><title>Explorer le monde avec un second cerveau</title><link href="https://me.petitapetit.io/second-cerveau" rel="alternate"/><published>2024-03-25T07:20:20.082000+00:00</published><updated>2024-03-25T07:20:20.082000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-03-25:/second-cerveau</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Mon second cerveau fête ses un an.&lt;/p&gt;
&lt;p&gt;C'est l'occasion de revenir en arrière pour un premier bilan.&lt;/p&gt;
&lt;p&gt;Au programme aujourd’hui : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le problème initial&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;c’est quoi un second cerveau&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;qu’est-ce que cela a changé dans ma vie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ressources et conseils pour démarrer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;conclusion&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Point de départ&lt;/h1&gt;
&lt;p&gt;Il …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Mon second cerveau fête ses un an.&lt;/p&gt;
&lt;p&gt;C'est l'occasion de revenir en arrière pour un premier bilan.&lt;/p&gt;
&lt;p&gt;Au programme aujourd’hui : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le problème initial&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;c’est quoi un second cerveau&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;qu’est-ce que cela a changé dans ma vie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ressources et conseils pour démarrer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;conclusion&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Point de départ&lt;/h1&gt;
&lt;p&gt;Il y a 4 ans, je me suis mis à lire de façon régulière. &lt;/p&gt;
&lt;p&gt;J'ai eu une première phase économie politique, écologie et science sociale. Puis j'ai centré mes lectures autour du software engineering pour qu'elles m'aident à progresser dans mon métier. Certaines idées m'ont marqué. Elles m'ont conduite à aborder la vie de façon différente. Mais lorsque je repense aux livres que j'ai lus à cette époque, j'ai du mal à les restituer. &lt;/p&gt;
&lt;p&gt;Très vite, je me suis aperçu que j'oubliais tout rapidement.&lt;/p&gt;
&lt;p&gt;Alors j'ai commencé à prendre des notes et à souligner des passages dans les livres. Mais je manquais de méthode, et la valeur ajoutée de ces notes restait faible.&lt;/p&gt;
&lt;p&gt;Début 2023, je me suis donné pour objectif de "créer du contenu". &lt;/p&gt;
&lt;p&gt;Le problème de gérer mes connaissances est devenu une de mes priorités. &lt;/p&gt;
&lt;p&gt;Je suis tombé sur la formation Atomic Knowledge d'Eliott Meunier.&lt;/p&gt;
&lt;p&gt;Et ça a été une révélation. &lt;/p&gt;
&lt;p&gt;Depuis, j'ai complété sa formation avec plusieurs ressources sur le sujet comme &lt;em&gt;Building a Second Brain&lt;/em&gt; de Tiago Forte ou &lt;em&gt;How to Take Smart Notes&lt;/em&gt; de Sönke Ahrens.&lt;/p&gt;
&lt;p&gt;Voilà un an que je travaille avec ce système de second cerveau. &lt;/p&gt;
&lt;p&gt;Et voici où j'en suis.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Le second cerveau&lt;/h1&gt;
&lt;p&gt;Un second cerveau est un nom accrocheur pour parler d'un système de gestion des connaissances personnelles. &lt;/p&gt;
&lt;p&gt;C'est un endroit où&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;organiser ses connaissances&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;réaliser ses projets&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;penser&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;stocker ses souvenirs&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Le terme a été conceptualisé par Tiago Forte à travers son travail sur la productivité et la gestion de connaissance. Son livre &lt;em&gt;Building a Second Brain&lt;/em&gt; a été publié en Juin 2022. Et ce système s'inscrit dans la tradition des common place books utilisés par les auteurs et les scientifiques comme Newton, Da Vinci, Mark Twain, Lovecraft.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/second-cerveau-vue.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;La vue graphique de mon second cerveau&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;La structure de mon second cerveau&lt;/h1&gt;
&lt;p&gt;Derrière ce mot accrocheur de second cerveau, on retrouve un simple système de notes qui sont connectées entre elles et organisées dans des dossiers. La vue graphique est impressionnante. Mais au quotidien, j'utilise davantage la vue à plat.&lt;/p&gt;
&lt;p&gt;La structure de mon second cerveau est la suivante : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/second-cerveau-folder.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;En fond : Obsidian, le logiciel que j'utilise pour explorer et créer des notes. Au-dessus : l'explorateur de fichier Mac. Obsidian offre une interface conviviale pour manipuler et visualiser les notes.&lt;/p&gt;&lt;/div&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;INBOX&lt;/strong&gt; : le point d'entrée du système. C'est ici que vont les notes instantanées que je prends sur mon téléphone ainsi que mes notes de lecture. Par exemple si lors d'une conversation on me parle d'un restaurant et que je veux noter l'adresse, je crée une note sur mon téléphone et c'est ici qu'elle atterrit. De la même façon, les notes que je prends en lisant sur tablette sont synchronisées dans ce dossier.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;PROJETS&lt;/strong&gt; : le système est organisé par utilité plutôt que par thème. L'accent est mis sur la progression des projets en cours. C'est pour cette raison que la rubrique projet apparaît en haut. Cette structure renforce l'idée que l'information est au service des projets - et non l'inverse. Un projet typique contient : &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;une fiche qui clarifie pourquoi je réalise ce projet&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;une roadmap ou todo-liste&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des notes de journaling les jours où je travaille dessus&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des notes vers des inspirations et autres ressources en lien avec le projet&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;CASQUETTES&lt;/strong&gt; : une caractéristique fondamentale des projets est qu'ils ont une durée limitée dans le temps. La rubrique casquette est ici pour toutes les responsabilités qui n'ont pas de fin. Je stocke des informations plus générales comme les codes du WIFI sur mon lieu de vie et des informations relatives à mon travail.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;RESSOURCES&lt;/strong&gt; : le dossier ressources regroupe les sujets et les grandes questions auxquelles je m'intéresse. Elles ne sont pas directement liés à un projet mais pourront servir à des projets futurs. Par exemple : Software Engineering, Comment prendre de meilleures décisions (Prise de décision), Comment être en bonne santé (Santé), ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;ARCHIVE&lt;/strong&gt; : c'est ici que je place les projets terminés et les notes d'avant la mise en place du système que je n'ai pas remises en formes (notes récupérées de Notion, ...)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;GARDEN&lt;/strong&gt; : le foyer des notes permanentes. C'est la destination du contenu non périssable (evergreen). On y retrouve des fiches sur des concepts qui seront toujours pertinents dans 30 ans. Par exemple, une fiche sur le &lt;em&gt;Biais des coûts irrécupérables&lt;/em&gt;. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;L'objectif de cette structure est d'être orientée action. La finalité d'un second cerveau n'est pas de documenter le monde d'une façon neutre et exhaustive. Cela représente un travail de titan, et Wikipédia est là pour ça. La vocation du second cerveau est davantage de collecter les informations qui sont nécessaires à la réalisation de projets, et de permettre de réutiliser ces informations.&lt;/p&gt;
&lt;h1&gt;Ce qui a changé un an plus tard&lt;/h1&gt;
&lt;h2&gt;Une autre façon de prendre des notes&lt;/h2&gt;
&lt;p&gt;Il y a encore un an, mes notes ressemblaient à ça :&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/second-cerveau-before.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Notes que j'ai prise sur Turn The Ship Around lors du book club de Tech Rocks&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Je soulignais les passages que je trouvais intéressant, sans faire de distinction entre les faits, les opinions, les principes et les anecdotes. J'avais du mal à détacher les informations de la source originale. Je me retrouvais avec une note géante difficile à réutiliser ailleurs.&lt;/p&gt;
&lt;p&gt;J'essayais de gober l'éléphant. &lt;/p&gt;
&lt;p&gt;Aujourd'hui, elles ressemblent à ça : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/second-cerveau-demo.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;À gauche mes notes de lecture du livre "Vie intentionnelle" de JCK. Pour chaque chapitre, on retrouve la liste des concepts qui m'ont marqué. À droite, on voit le détail de la note Dichotomie du contrôle&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Lorsque je lis, je prête désormais attention aux concepts. &lt;/p&gt;
&lt;p&gt;Un livre est un ensemble de concepts mis en perspective. &lt;/p&gt;
&lt;p&gt;La première chose que je cherche à identifier est donc le concept qui est en train d'être présenté. Puis je cherche à voir en quoi il consiste, ses bénéfices, ses limites, ses inconvénients, et comment il est illustré. &lt;/p&gt;
&lt;p&gt;La force des concepts est que :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;ils sont indépendants de la source et faciles à mobiliser dans d'autres contextes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ils ont une longue durée de vie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ils sont systématiques à documenter : description, exemple, références&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ils sont simples à connecter à d'autres concepts : similaires, différents&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ils sont des centres de gravité qui attirent les informations complémentaires&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ils compressent l'information : un concept porte en lui plusieurs dizaines de mots&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;La prise de notes orientée concept a transformé mon rapport à l'information.&lt;/p&gt;
&lt;h2&gt;Moins d'outils, plus de clarté&lt;/h2&gt;
&lt;p&gt;Avant de mettre en place ce système, mes notes étaient éparpillées un peu partout : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;des passages surlignés au crayon dans mes livres&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des listes de film à regarder sur SensCritique&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des tâches de projet dans Trello&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des notes sur mes projets dans Notion&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des images d'inspirations sur Pinterest&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des notes instantanées sur iNote&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;des notes instantanées sur Whatsapp&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Avec ce système, j'ai pu réduire le nombre d'outil que j'utilise.&lt;/p&gt;
&lt;p&gt;Toutes les informations importantes pour moi sont centralisées dans un seul endroit.&lt;/p&gt;
&lt;p&gt;Et j’y accède de n'importe où : sur ordinateur comme sur téléphone.&lt;/p&gt;
&lt;h2&gt;Une meilleure visibilité&lt;/h2&gt;
&lt;p&gt;Avec ce système, j'ai une meilleure visibilité sur le travail en cours. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;La page d'accueil me rappelle le cadre que je me suis fixé et mes grands objectifs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;L'INBOX donne une vue sur les informations entrantes. Lorsqu'il y en a trop et que je sature, cela se voit et me renvoi le message de ralentir sur mes lectures.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le dossier PROJETS me permet de suivre les projets en cours.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le dossier ARCHIVES m’aide à apprécier les projets que j'ai réalisé et clôturés. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;C’est aussi un bon outil pour apprendre à se connaître. Mes notes de journaling y sont regroupées. Et les clusters de la vue graphique révèlent mes centres d’intérêt. &lt;/p&gt;
&lt;h2&gt;Le plaisir de lire&lt;/h2&gt;
&lt;p&gt;Lire est devenu un jeu. &lt;/p&gt;
&lt;p&gt;Il y a quelque chose de ludique à collecter des concepts en lisant. &lt;/p&gt;
&lt;p&gt;Ce qui est paradoxal, c'est que prendre des notes demande du temps. Lorsque c'est un livre que je veux creuser, je passe en fait plus de temps à prendre des notes et à clarifier les concepts qu'à lire. &lt;/p&gt;
&lt;p&gt;Je passe donc plus de temps sur chaque livre. &lt;/p&gt;
&lt;p&gt;Mais à présent, je sais que ce que je lis ne sera pas perdu. &lt;/p&gt;
&lt;p&gt;Et au final, je lis plus de livres.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Quelques conseils pour démarrer&lt;/h1&gt;
&lt;p&gt;Si je devais recommencer de zéro, voici les ressources que je suivrai :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;La formation &lt;a href="https://fr.atomicthinking.fr/knw"&gt;Atomic Knowledge&lt;/a&gt; d'Eliott pour un système complet et opérationnel&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;La formation Atomic Notes d'Eliott (à venir) pour un système léger et opérationnel&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le livre Building a Second Brain de Tiago Forte pour aller plus loin et être un chef&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;La formation &lt;a href="https://www.buildingasecondbrain.com/foundation"&gt;Building a Second Brain&lt;/a&gt; de Tiago Forte (en anglais)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le livre &lt;em&gt;How to take smart notes&lt;/em&gt; - pour améliorer sa prise de notes&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Aussi, n'hésite pas à me contacter si c'est un sujet qui t'intéresse :)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Enfin, voici 3 conseils que j'aurais aimé que l'on m'imprime en capitale pour débuter : &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cibler (ou être patient)&lt;/strong&gt; : plus le sujet que tu documentes est ciblé, plus les résultats vont être rapides. Pendant les premiers mois, j'ai lu sur des sujets dispersés : design logiciel, techniques de développement, développement produit, écriture, création de contenu, entrepreneuriat, gestion des connaissances personnelles, psychologie. Or, ouvrir un nouveau thème demande un effort conséquent car au départ, tu crées beaucoup de fiches. Lorsque j'ai passé un mois uniquement concentré sur les échecs, j'ai pu constater que le système portait ses fruits rapidement. Actuellement, je fais un focus sur le DevOps et je suis impressionné par ma vitesse de progression. Cibler un domaine précis est une bonne stratégie pour débuter.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aller de l’avant&lt;/strong&gt;. Le passé c'est l'passé comme dit Yanns. Lorsque j'ai découvert cette méthode, je me suis dit, wow, je voudrais que toutes les connaissances que j'ai touchées soient dans ce système. J'ai commencé à ajouter les notes des livres que j'avais déjà lues. Spoiler : c'est long et ne présente aucun intérêt. Sauf si le livre a un rapport avec un projet en cours, mais en dehors de cela, c'est un processus assez frustrant. Il faut accepter que le passé soit "perdu", et aller de l'avant.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt; &lt;strong&gt;Keep it stupid simple&lt;/strong&gt; : il existe des centaines de plugins dans Obsidian et il est possible d'aller loin dans la customisation. Certaines extensions apportent de la plus-value. Certaines apportent surtout de la complexité. Le plus simple, le mieux. Le but est d'élaborer avec ses idées, pas de configurer des plugins.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Si je devais revenir 10 ans en arrière et changer une chose, &lt;/p&gt;
&lt;p&gt;ce serait de mettre en place un second cerveau. &lt;/p&gt;
&lt;p&gt;Avant même d'acheter du bitcoin.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Aujourd'hui, j'aurais : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;créé des connaissances solides sur le design graphique et ses grandes figures&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;documenté la scène électronique des années 2010&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;documenté ma phase de vie artistique et les projets que j'ai réalisé&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;capitalisé sur les concepts importants croisés dans des livres, des expositions&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;rencontré des exemples et des illustrations de concepts dans ma vie quotidienne&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;créé des fiches sur certains cours qui étaient géniaux&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;tenu un journal de toute ma vingtaine&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Vraiment, c'est la chose que je changerais si je devais revenir 10 ans en arrière. &lt;/p&gt;
&lt;p&gt;Et donc, je compte bien travailler avec cet outil pendant les dix prochaines années.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;À la prochaine :)&lt;/p&gt;
&lt;p&gt;Alexandre&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</content></entry><entry><title>3 principes tirés de l'Almanach de Naval</title><link href="https://me.petitapetit.io/almanach-naval" rel="alternate"/><published>2024-03-18T07:51:23.125000+00:00</published><updated>2024-03-18T07:51:23.125000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-03-18:/almanach-naval</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Pour commencer l'année, j'ai écouté l'Almanach de Naval Ravikant en audio book. &lt;/p&gt;
&lt;p&gt;Cette édition explore les trois concepts qui m’ont le plus marqué dans ce livre.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Naval Ravikant&lt;/h1&gt;
&lt;p&gt;Naval est un entrepreneur américain de la Silicon Valley. Il est né à New Delhi, a fondé plusieurs entreprises, et …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Pour commencer l'année, j'ai écouté l'Almanach de Naval Ravikant en audio book. &lt;/p&gt;
&lt;p&gt;Cette édition explore les trois concepts qui m’ont le plus marqué dans ce livre.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Naval Ravikant&lt;/h1&gt;
&lt;p&gt;Naval est un entrepreneur américain de la Silicon Valley. Il est né à New Delhi, a fondé plusieurs entreprises, et a investi dans des centaines startups. &lt;/p&gt;
&lt;p&gt;Naval a grandi dans la pauvreté. Aujourd’hui, il est connu pour sa richesse financière, sociale, humaine et spirituelle. Dans une expérience de pensée, il s’est posé la question suivante : “si je devais vivre 100 vies, comment est-ce que je m’y prendrais pour être riche dans 99% des cas” ? &lt;/p&gt;
&lt;p&gt;Autrement dit : comment devenir riche, sans l’aide de la chance ? &lt;/p&gt;
&lt;p&gt;Dans son Tweet storm "How to get rich (without being lucky)", il mobilise les concepts de Specific Knowledge, Effet de Levier, distingue les différents types de Chance, et rappelle les différences entre Richesse, Argent, Statut.&lt;/p&gt;
&lt;p&gt;Ses trois thèmes de prédilection sont le bonheur, la santé et la richesse. Il aime lire, et un de ses jeux est de résumer ses lectures en quelques phrases sur Twitter, sous la forme d’aphorismes. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/naval.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;L'Almanach&lt;/h1&gt;
&lt;p&gt;L'Almanach de Naval Ravikant a été composé par Eric Jorgenson, un autre entrepreneur. Il a été publié début 2023. Il regroupe les idées de Naval et les présente sous forme de citations piochées sur Twitter et dans des podcasts. Le livre aborde les thèmes de la richesse et du bonheur.&lt;/p&gt;
&lt;p&gt;En dehors des concepts phare de Naval, voici les idées qui m'ont le plus marqué dans le livre et les réflexions que j'en tire. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;3 concepts tirés de l’Almanach&lt;/h1&gt;
&lt;h2&gt;Les bases&lt;/h2&gt;
&lt;p&gt;Les bases sont hyper importantes. &lt;/p&gt;
&lt;p&gt;Elles ont plusieurs avantages : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;elles sont limitées en quantité &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;elles apportent 80% de la valeur &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;elles servent de fondations pour développer une expertise&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Si l'on prend les mathématiques par exemple, les bases vont être l'arithmétique (addition, soustraction, …) et disons les probabilités. Ces notions sont très limitées si l'on considère l'étendue des mathématiques. Cependant, on les mobilise au quotidien et elles nous aident à prendre de meilleures décisions. Enfin, si tu veux aller plus loin dans l'exploration des mathématiques, ces notions sont fondamentales.&lt;/p&gt;
&lt;p&gt;Les bases sont d'autant plus importantes que tu ne peux pas être expert sur toutes les disciplines. En avoir conscience permet de faire des choix intelligents. Par exemple, je peux pousser à fond mon expertise en développement logiciel, et me concentrer sur les bases pour les disciplines annexes (DevOps, Marketing, Vente, ...).&lt;/p&gt;
&lt;p&gt;Les pendants des bases sont les concepts avancés. Les concepts avancés sont attrayants parce qu'ils te donnent l'air que tu t'y connais (jeu de status). Mais ce sont aussi les concepts qui sont les moins vérifiés. Et ils sont un piège, car un bon moyen de tout mélanger au départ.&lt;/p&gt;
&lt;p&gt;En pratique, lorsque tu abordes une nouvelle discipline : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Écarter les concepts avancés pour se concentrer sur les bases&lt;/strong&gt;. Ce mois-ci, j'ai décidé de passer le cap du DevOps et de me former à Docker, Kubernetes, OpenShift et Gitlab. Avoir les bases de ces technos est indispensable à mon job, mais je n'ai pas besoin d'être un expert. J'ai donc choisi de lire "Getting started with docker" qui ambitionne de te donner les bases en une journée plutôt que "Docker Deep Dive" qui ambitionne de faire de toi un expert de Docker.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remonter aux racines, aux sources&lt;/strong&gt;. Si tu t'intéresses à la Théorie de l'évolution, Richard Dawkins est auteur contemporain réputé, mais tu ferais mieux de commencer par L'Origine des espèces de Darwin. Remonter aux sources peut être intimidant au départ. Mais si tu es sérieux sur ton apprentissage, tu veux te confronter aux idées originales. Aussi, pour un ouvrage de référence, il existe souvent des dizaines de livres qui déclinent les grands principes présentés initialement. Remonter à la source permet de réduire sa pile de lecture de façon drastique, d’avoir le cadre théorique, et de pouvoir lire les déclinaisons de façon plus efficace.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Pour la santé, la concentration et l'énergie, avant d'aller chercher des régimes impossibles à tenir et des compléments alimentaires, commencer par tenir les bases : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;faire de l'exercice quotidiennement (marche, nage, vélo)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;manger des produits non transformés et éviter les sucres rapides&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;passer 8 à 9h au lit pour avoir au minimum 7h de sommeil&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Enfin, une troisième manière de considérer les bases que j'aime beaucoup est la suivante. Si tu veux accomplir des choses dans la vie, commence par maîtriser les bases : faire ton lit le matin, faire la vaisselle, te faire à manger. Si déjà, tu te trouves débordé par ces tâches, comment espères-tu gérer le reste ?&lt;/p&gt;
&lt;p&gt;Je réalise à quel point on a tendance à chercher des solutions compliquées à des problèmes avant même d'avoir essayé d'appliquer le B.A.-BA. 😅&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/naval-bases.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le long terme&lt;/h2&gt;
&lt;p&gt;Penser long terme, c'est jouer avec la force du temps. &lt;/p&gt;
&lt;p&gt;Avec le temps, l'impact des habitudes s'accumule et les petites actions du quotidien génèrent des effets spectaculaires. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;si tu déposes 10€ par jour sur un livret pendant 100 ans à 6% sans faire de retrait, tu auras versé 360 000 €, pour un capital final de 23 786 539€.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;lire 10 pages par jour permet de lire 12 livres par an&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;méditer 10 minutes chaque matin pour gagner en sérénité et clarté d'esprit&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;manger un sandwich, avec une pâtisserie et un soda chaque midi, est inoffensif pris isolément, mais conséquent sur ta santé et ta productivité à long terme&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;De même, l'impact des décisions que nous prenons aujourd'hui sont sujettes aux effets cumulés : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;quitter un job dans lequel tu t'ennuies&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;quitter une personne toxique, même si sur le coup, tu y es attaché&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;voir au-delà des premiers obstacles d'une relation pour tisser des liens plus solides&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;avoir des conversations franches&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;dédier une heure à apprendre une compétence plutôt que regarder Netflix&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Nous avons tendance à penser court-terme.&lt;/p&gt;
&lt;p&gt;Voici 5 façons de prendre des décisions de long terme&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;se projeter&lt;/strong&gt;, poser la question au toi de dans 10 ans : quelle décision tu te remercieras d'avoir pris dans 10 ans ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;penser aux conséquences de l’habitude&lt;/strong&gt;. Les actions prises indépendamment n’ont souvent aucun impact. Mais elles font partie d’une habitude, et par effet cumulé, ont un immense impact. Du coup, quelles seront à long terme les conséquences sur ta santé et ton niveau de concentration de manger un cookie tous les jours à 10h ? Quel peut être l’impact sur tes compétences et ton réseau si tu Stream du code tous les midis pendant 10 ans ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;réduire son exposition aux sources de gratification instantanée&lt;/strong&gt; : se tenir à l'écart des sources de dopamine et de gratification instantanée le plus longtemps possible dans la journée (e-mails, LinkedIn, messages, sucres)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;investir plutôt que dépenser&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire le choix le plus difficile&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Penser long terme demande du courage et de la discipline.&lt;/p&gt;
&lt;p&gt;Penser long terme est plus facile à dire qu’à faire.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/naval-long-term.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div class="pullquote"&gt;&lt;p&gt;Choix faciles, vie difficile. Choix difficile, vie facile&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le taux horaire&lt;/h2&gt;
&lt;p&gt;Depuis que je considère mon temps comme une de mes ressources les plus précieuses, je suis frappé de voir à quel point, de façon générale, nous ne valorisons pas notre temps. &lt;/p&gt;
&lt;p&gt;Un de conseil que Naval donne dans son livre est le suivant : &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Évaluez votre temps à un taux horaire, et dépensez impitoyablement pour gagner du temps à ce taux. Vous ne vaudrez jamais plus que ce que vous pensez valoir.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Derrière ce conseil, il y a cette idée que si ce que tu es en train de faire&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;tu ne le fais pas par envie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;tu ne le fais pas pour gagner de l'argent non plus&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;et que le déléguer est en dessous de ton taux horaire&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;alors pourquoi est-ce que tu le fais ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;On passe notre temps à gagner de l’argent. &lt;/p&gt;
&lt;p&gt;Alors qu’on ferait mieux d’utiliser notre argent pour gagner du temps. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Très tôt, Naval raconte s'être fixé un taux horaire à 5000 €. &lt;/p&gt;
&lt;p&gt;😅&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Après, cela m'interroge. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Supposons que dans mon travail, je sois à 100€ de l'heure. Si je me fixe un taux ambitieux à 500€ de l'heure, alors est-ce que je dois déléguer mon travail ? En vrai, c'est une idée qui mériterait d'être étudiée. En gardant en tête que globalement, j'ai un travail qui me passionne, et que les contraintes de confidentialités ne permettent pas forcément cela.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Si tu te fixes un taux horaire au-delà de tes capacités financières, est-ce que tu seras en mesure de tenir cette position ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Dans l'objectif de mettre en place ce principe, j'ai commencé à noter heure par heure ce que je réalise dans mes journées afin de mieux identifier ce que je pourrais déléguer ou automatiser. &lt;/p&gt;
&lt;p&gt;Voici une ébauche : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;faire les courses : 2h/sem. Idée : se faire livrer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire la vaisselle : 30 min/jour. Idée : prendre un lave-vaisselle&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;retourner un objet défectueux en magasin : 2h ? Idée : laisser tomber.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire le ménage : temps à mesurer. Idée : prendre une femme de ménage&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;réserver des billets de train, des Airbnb, déclarer la TVA : temps à mesurer. Prendre une assistante virtuelle ?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/naval-space.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;J'ai beaucoup aimé lire ce livre. Il est réputé difficile à résumer, car peu structuré, et pourtant, j'ai l'impression que ce manque de structure est justement ce qui m'a permis d'en extraire quelques concepts essentiels. &lt;/p&gt;
&lt;p&gt;Est-ce qu’un de ces concepts t’inspire ?&lt;/p&gt;
&lt;p&gt;Cette édition représente encore un virage dans la ligne éditoriale. Je me rends compte que cette newsletter prend de plus en plus la forme d’une newsletter personnelle. Dans les prochaines éditions, je pense continuer à faire des bilans sur les projets que je fais, et parler des concepts qui me marquent dans mes lectures.&lt;/p&gt;
&lt;p&gt;Qu’est-ce que tu penses de ce type de format axé sur une lecture ?&lt;/p&gt;
&lt;div class="poll-embed" data-attrs='{"id":156292}' data-component-name="PollToDOM"&gt;&lt;/div&gt;
&lt;p&gt;À très vite :)&lt;/p&gt;
&lt;p&gt;Alex&lt;/p&gt;</content></entry><entry><title>Mes intentions pour 2024</title><link href="https://me.petitapetit.io/2024" rel="alternate"/><published>2024-03-11T07:20:27.145000+00:00</published><updated>2024-03-11T07:20:27.145000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-03-11:/2024</id><summary type="html">&lt;p&gt;En 2023, mon but était de me lancer dans l'entrepreneuriat sur un mode &lt;em&gt;indie-maker&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;La flot de la vie m'a conduit sur une autre voie. &lt;/p&gt;
&lt;p&gt;Aujourd'hui, je travaille comme Software Engineer en freelance.&lt;/p&gt;
&lt;p&gt;En 2024, j'ai envie de jouer ce jeu à fond et me concentrer sur 3 choses : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;ma …&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;En 2023, mon but était de me lancer dans l'entrepreneuriat sur un mode &lt;em&gt;indie-maker&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;La flot de la vie m'a conduit sur une autre voie. &lt;/p&gt;
&lt;p&gt;Aujourd'hui, je travaille comme Software Engineer en freelance.&lt;/p&gt;
&lt;p&gt;En 2024, j'ai envie de jouer ce jeu à fond et me concentrer sur 3 choses : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;ma mission freelance&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;partager du contenu en ligne&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;construire des relations saines et profondes avec mon entourage&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Vision à 5 ans&lt;/h2&gt;
&lt;p&gt;Dans 5 ans : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;j'ai 37 ans 😲&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai liquidé toutes mes dettes et les problèmes de la maison sont du passé&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai besoin de peu pour vivre&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai vécu un an dans un pays anglophone et parle bien anglais&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je suis bien dans mon couple et nous avons un enfant&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Mes objectifs pour 2024&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;j'ai livré Tamis sur JO, sur IJ et une première version du moteur de règles&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je suis hyper à l'aise pour déployer et designer des applications&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai fait un trek à pied d'une semaine&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai fait le tour de la Manche à vélo&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai publié +20 articles de Newsletter&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai fait +50 &lt;em&gt;practice hours&lt;/em&gt; sur Twitch&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai publié +50 posts sur LinkedIn&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;je maitrise les bases de la prise de décision&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai des relations saines et profondes avec mon entourage&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'ai mis 40 000 € de côté sur l'année pour me libérer du poids de la maison&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Les compétences que j'ai envie de développer&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Software Engineering&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le Software Engineering est la montagne que j’ai envie de gravir et de maîtriser. Les trois axes sur lesquels j’aimerai mettre l’accent cette année sont :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;DevOps - la compétence dont j'ai le plus besoin à court terme&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Software Design - la compétence la plus &lt;em&gt;overlooked&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Produit - la compétence complémentaire au développement&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Création de contenu&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Écrire en ligne&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Développement personnel&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Prendre de bonnes décisions&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Amour et relations&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Habitudes&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;DevOps&lt;/h3&gt;
&lt;p&gt;Dans le cycle de vie d'un logiciel, le déploiement est le dernier maillon de la chaîne. Si tu maitrises les front-end, tu peux développer des applications sympa sur ta machine perso. Si tu maitrises le backend, tu peux développer des outils plus sophistiqués. Mais pour que ton outil soit accessible et puisse être utilisé, il faut le déployer. Et c'est là qu'intervient le DevOps. &lt;/p&gt;
&lt;p&gt;Le DevOps est une compétence indispensable et difficile à acquérir. Aujourd'hui, ne pas avoir la base me pénalise dans mon travail, ce sera donc mon premier focus de l'année.&lt;/p&gt;
&lt;p&gt;Ma stratégie est la suivante : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Lire et écouter des livres inspirationnels&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Lire des livres théoriques&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pratiquer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pratiquer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pratiquer&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Ma progression : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Inspiration et mindset&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;✅ The Phoenix project&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;✅ The Goal - A process of ongoing improvement 🌟&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;✅ Toyota Production System (TPS)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The DevOps Handbook&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Accelerate&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Docker&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Quick Start Docker&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Learn Docker in a Month of Lunches&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Docker Deep Dive&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Kubernetes&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;✅ Quick Start Kubernetes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Learning Kubernetes in a month of Lunch&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;GitLab&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;✅ Automating DevOps With GitLab CICD Pipelines&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Software Design&lt;/h3&gt;
&lt;p&gt;Le design logiciel est une des compétences la plus survolée du métier. La plupart des devs ne s'y intéressent pas vraiment. Pire, la plupart des personnes pensent savoir. Lorsque les choses tournent mal, la même musique revient. C'est la faute des autres : ceux qui étaient là avant, ceux qui nous ont légués ce bazar. &lt;/p&gt;
&lt;p&gt;Il est facile de reconnaitre un logiciel mal fait : le code est incompréhensible. C'est le résultat d'un produit simple, sur lequel on a travaillé de façon tactique, chaque incrément apportant son lot de complexité. &lt;/p&gt;
&lt;p&gt;Voici les livres que j'aimerais bien lire ou relire pour constituer me constituer un socle solide sur le sujet : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Clean Code - Uncle Bob&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The Pragmatic Programmer &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Tidy First? - Kent Beck&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The Laws of Simplicity - John Maeda&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Patterns of enterprise Architecture - Martin Fowler&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Fundamentals of Software Architecture: An Engineering Approach&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Software Engineering - The Hard Parts&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Code Is for Humans: A Guide to Human-Centric Software Engineering&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Modern Software Engineering: Doing What Works to Build Better Software Faster&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;99 Bottles of OOP - Sandy Metz&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A Philosophy of Software Design - John Ousterhout&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Elegant Objects - Yegor Bugayenko&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Working Effectively with Legacy Code&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Seven Languages in Seven Weeks&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Domain-Driven Design: Tackling Complexity in the Heart of Software&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Grokking Simplicity: Taming Complex Software With Functional Thinking&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Le livre de John Maeda n’a rien à voir avec le développement logiciel à proprement parler. Mais le grand ennemi du logiciel et la complexité. J’espère trouver dans ce livre des idées universelles pour répondre à ce challenge.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Amour et relations&lt;/h3&gt;
&lt;p&gt;Je dédie énormément de temps à mon travail et à mes recherches. J'ai peu d'amis mais ils sont importants pour moi. De la même façon, je suis très proche de ma famille. &lt;/p&gt;
&lt;p&gt;Il est difficile de voir tout le monde régulièrement et j’ai un rythme qui peut être difficile à suivre. Mais en contrepartie, je veux être disponible pour eux et présent à 100% lorsque nous sommes ensemble. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Crucial Conversations: Tools for Talking When Stakes are High&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Les mots sont des fenêtres (ou bien ce sont des murs) - Marshall Rosenberg&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;How to Win Friends and Influence People - Dale Carnegie&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Méthode Socratique - ce qui sauve le couple d'Alex Rogo dans The Goal&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;En toute franchise (Radical Candor) - Kim Scott&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Prendre de bonnes décisions&lt;/h2&gt;
&lt;p&gt;Ces dernières années, certaines de mes décisions m'ont mise dans des situations vraiment mauvaises. Du coup, je réalise à quel point la capacité à faire preuve de discernement et à prendre de bonnes décisions est importante.&lt;/p&gt;
&lt;p&gt;Quand on y réfléchit, la vie se joue sur quelques bonnes ou mauvaises décisions par an. Et au travail, choisir sur quoi tu vas travailler et sur quoi tu ne vas pas travailler est presque plus pondérant que l'effort que tu vas y consacrer. &lt;/p&gt;
&lt;p&gt;Comme beaucoup de choses, la prise de décision est une compétence qui peut s'apprendre. La question qui vient ensuite est donc : ok, comment ?&lt;/p&gt;
&lt;p&gt;Je vois plusieurs dimensions. &lt;/p&gt;
&lt;p&gt;La base, c'est de se connaître :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Faire de l'introspection&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Explorer et expérimenter pour vérifier les hypothèses qui ressortent de l'introspection&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Pour la dimension technique et théorique : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Thinking Fast and Slow - Daniel Kahneman&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Great mental models - Shane Parrish&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Principles: Life and Work - Ray Dalio&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Decisive: How to Make Better Choices in Life and Work&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Clear Thinking: Turning Ordinary Moments into Extraordinary Results&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Optionality - Meadows&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Rationality: From AI To Zombies&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Invent and Wander: The Collected Writings of Jeff Bezos&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Solopreneur d’Elite - Thibaut Louis&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Incerto - Nassim Nicholas Thaleb&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Ecrire en ligne&lt;/h2&gt;
&lt;p&gt;L'année dernière, j'ai testé différents formats d'écriture en ligne : publier des articles de newsletter, filmer des CodeKata puis les diffuser sur Youtube, poster des essais sur LinkedIn. Je n'ai pas une audience de dingue, mais j'ai adoré le processus. &lt;/p&gt;
&lt;p&gt;Écrire en ligne est un des plus gros hack de notre époque. Cela te permet&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;de clarifier et confronter tes idées&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de te tromper en public, te faire corriger, et apprendre plus vite&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de rencontrer des personnes qui partagent ton état d'esprit&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de générer des opportunités professionnelles&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de se faire des amis&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;de créer une audience à qui tu pourras apporter des services&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Le tout, sans avoir besoin de demander la permission à personne. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Liste de lecture : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;The Art and Business of Online Writing - Nicolas Cole&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Publier de la M*rde avec Style - Valentin Decker, Killian Talin&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Influence et manipulation - Robert B. Cialdini&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pixar Storytelling: Rules for Effective Storytelling Based on Pixar's Greatest Films&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Les Essais - Montaigne (pour inspiration sur la forme)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Storyworthy&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The Boron Letters - Gary Halbert&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cashvertising - Drew Eric Whitman&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Projets : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streamer de façon régulière sur Twitch&lt;/strong&gt; : depuis longtemps, j'ai un énorme biais pour la théorie. Je lis plein de truc, que je ne comprends pas toujours bien, et que je ne met pas en pratique. Je vois Twitch comme une opportunité de me forcer à pratiquer de façon régulière. De me donner rendez-vous au moins 3x/semaine pour pratiquer une heure. J'ai des dizaines d'idées de sujet en tête et plusieurs idée de personnes que j'aimerai inviter pour dynamiser le format. Mais mon Minimum Viable Live est très simple : juste honorer le rendez-vous, me connecter, et pratiquer, même si je n'ai rien de préparé en avance.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Publier des formats long dans une Newsletter&lt;/strong&gt; : les newsletter sont réputées être un média difficile à lancer, mais c'est le canal qui me parle le plus. Il offre un bon équilibre de longueur de format et offre une grande liberté sur le contenu éditorial. Je vois la newsletter comme la colonne vertébrale de ce que j’ai envie de publier, le premier point de sortie des idées que je synthétise. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; : c'est le seul réseau social sur lequel je passe du temps. Mon idée est de dériver entre 1 et 3 post par article de newsletter afin de la diffuser.   &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Ma journée idéale en 2024&lt;/h2&gt;
&lt;p&gt;Bon, ça nous fait un programme ambitieux tout ça. Je sais sur quels axes je veux progresser. Je visualise concrètement les grandes lignes de comment je compte m'y prendre. Il ne reste plus que le volet opérationnel.&lt;/p&gt;
&lt;p&gt;Voici ma journée idéale :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;06h00 : Réveil, café&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;07h00 : Écriture&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;08h00 : Lecture&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;09h00 : Marche&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;09h30 : Daily + 4 pomodoros&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;12h00 : Lunch break&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;13h00 : Session de pratique en live sur Twitch&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;14h00 : Back to my mission (8 pomodoros)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;18h00 : Task shooting&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;18h30 : Piscine&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;20h00 : Dîner + chill &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;21h30 : Au dodo&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;22h00 : 😴&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Mon travail m'amène à me rendre régulièrement sur Paris. Lorsque j'y vais, je me lève à 6h pour prendre le train de 7h. Caler mon rythme de levé sur cette heure me permet d'avoir de la régularité. &lt;/p&gt;
&lt;p&gt;Mon objectif de sommeil est de dormi 8h par nuit. Je ne veux pas passer sous la barre de 7h de sommeil. Lorsque je manque de sommeil, toute ma journée est foutue. Je n'arrive pas à me concentrer, je suis irritable, c'est la cata.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;L'année dernière, j'étais parti dans tous les sens sur mes projets. Cette année, j'ai essayé de les recentrer et de les aligner de façon cohérente. J'ai écarté la dimension entrepreneuriale qui était trop orthogonale au reste. Je vais me concentrer sur ma mission freelance et la création de contenu sera mon levier pour progresser plus vite.&lt;/p&gt;
&lt;p&gt;Il y a beaucoup de ressources listées dans cet article. Je ne pourrais pas tout lire, et l’important est avant tout de mettre en pratique les insights collectés. Les sessions Twitch joueront donc un rôle essentiel à cet égard. &lt;/p&gt;
&lt;p&gt;Enfin, je pense approcher chaque compétence par phase. Actuellement je suis dans ma phase DevOps. J’ai un peu nagé le premier mois, mais là, je sens que les choses commencent à s’aligner dans ma tête. &lt;/p&gt;
&lt;p&gt;Si je n’ai pas le temps de tout faire, ce n’est pas grave. &lt;/p&gt;
&lt;p&gt;C’est n’est qu’une feuille de route.&lt;/p&gt;
&lt;p&gt;Aller, assez passé de temps sur la planification. C'est parti pour l'exécution :)&lt;/p&gt;
&lt;p&gt;Et toi, quelles sont tes intentions pour 2024 ?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</content></entry><entry><title>Ma rétrospective de 2023</title><link href="https://me.petitapetit.io/2023" rel="alternate"/><published>2024-01-31T21:51:13.204000+00:00</published><updated>2024-01-31T21:51:13.204000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-01-31:/2023</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Il paraît qu'on a jusqu'à fin janvier pour se souhaiter une bonne année.&lt;/p&gt;
&lt;p&gt;J'imagine que c'est aussi valable pour la rétrospective. &lt;/p&gt;
&lt;p&gt;In-extremis, voici ma rétrospective de 2023. 😁&lt;/p&gt;
&lt;h1&gt;Revue générale de l’année&lt;/h1&gt;
&lt;p&gt;2023 a été chaotique. &lt;/p&gt;
&lt;p&gt;Les problèmes liés à la maison que j'ai acheté en 2022 ont pris …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Il paraît qu'on a jusqu'à fin janvier pour se souhaiter une bonne année.&lt;/p&gt;
&lt;p&gt;J'imagine que c'est aussi valable pour la rétrospective. &lt;/p&gt;
&lt;p&gt;In-extremis, voici ma rétrospective de 2023. 😁&lt;/p&gt;
&lt;h1&gt;Revue générale de l’année&lt;/h1&gt;
&lt;p&gt;2023 a été chaotique. &lt;/p&gt;
&lt;p&gt;Les problèmes liés à la maison que j'ai acheté en 2022 ont pris une nouvelle dimension. &lt;/p&gt;
&lt;p&gt;Côté pro, on s'est planté sur l'obtention de la levée de fond. L'entreprise s'est retrouvée à court de cash. Et j'ai quitté mon travail.&lt;/p&gt;
&lt;p&gt;Côté couple, nous avons traversé des passes difficiles. &lt;/p&gt;
&lt;p&gt;On a emménagé a l’atelier à l'improviste. &lt;/p&gt;
&lt;p&gt;J’ai consacré quelques mois à builder des projets comme j'en avais profondément envie. &lt;/p&gt;
&lt;p&gt;J’ai consacré beaucoup de temps à lire, écrire et prendre des notes. &lt;/p&gt;
&lt;p&gt;L'année se termine d'une façon que je n'aurais jamais imaginée en janvier. Je suis de retour chez EDF avec Christophe. La rémunération freelance me donne une bouffée d'oxygène. &lt;/p&gt;
&lt;p&gt;Toutes les difficultés ne sont pas passées, mais l’année termine sur une perspective positive.&lt;/p&gt;
&lt;h1&gt;Analyse de mes objectifs de l'année&lt;/h1&gt;
&lt;h2&gt;Objectif 1 : Améliorer mes compétences en Software Craftsmanship&lt;/h2&gt;
&lt;p&gt;Cet objectif était particulièrement en lien avec mon travail chez Marvin. Et à cet égard, mon expérience là-bas a été hyper formatrice. J’ai une compréhension plus fine du DDD, de l’ATDD, de l’architecture CQRS, de l'architecture hexagonale. J’ai pu mettre en pratique tous ces concepts, et Expérimenter les limites de la technique sans une bonne approche produit. &lt;/p&gt;
&lt;p&gt;Parmi les points qui m’ont fait le plus progresser cette année :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Mon expérience chez Marvin avec une équipe passionnée par ces pratiques&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enregistrer des screencast de Kata : regarder des exécutions possibles en ligne, répéter et expliquer les concepts rencontrés dans des sessions filmées a été hyper formateur. C’est une révélation.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Participer aux katas de Yoan Thirion (Refactoring du Bouchonnois, Advent of Craft)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Lire le livre de Vladimir Khorikov sur les test unitaires, incroyable&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Objectif 2 : Améliorer ma capacité à amorcer des projets&lt;/h2&gt;
&lt;p&gt;J’ai eu plusieurs expériences significatives la dessus : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;la publication de mon livre des Fables de La Fontaine&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le déploiement d’un générateur de recommandation LinkedIn&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le développement d’une app pour s’entraîner aux échecs.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Ces expériences m’ont redonnées confiance dans ma capacité à construire de A à Z. &lt;/p&gt;
&lt;p&gt;C'est un objectif que j'aimerais encore approfondir : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;en acquérant les bases DevOps &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;en testant la formation de Pierre Criulanscy sur un projet&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;en me formant à des outils no-code&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;Objectif 3 : Développer mes compétences en coaching&lt;/h2&gt;
&lt;p&gt;Avec la Will Academy, j'ai découvert des outils d'écoute et de coaching. Le coaching est une compétence qui me faisait envie et aurait eu son utilité alors que j'étais lead de l'équipe. &lt;/p&gt;
&lt;p&gt;Mais un détail qui me gênait. &lt;/p&gt;
&lt;p&gt;J'avais envie d'aider et donner des conseils, et en même temps l'impression de ne rien vraiment maitriser ou savoir faire. Cela a aboutit à une de mes décisions majeure de l’année : &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Arrêter de vouloir donner des conseils aux autres. Ne pas donner de conseil que je n'ai pas testé auparavant. Commencer par m'occuper de mes affaires, appliquer ces conseils à mes projets, expérimenter et vivre des expériences. Tu donneras des conseils si on t'en demande. &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ce qui est intéressant, est que mon attrait pour cette compétence m’a incitée à me rapprocher de mon frère qui faisait une formation de dev à l'époque. Début 2023, nous avons commencé à échanger régulièrement sur nos projets. Au final, lorsque j'étais dans ma phase "builder", ces échanges m’ont beaucoup aidés à avancer.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Objectif 4 : Terminer les travaux de la maison&lt;/h2&gt;
&lt;p&gt;Cet objectif est complètement parti en vrille. &lt;/p&gt;
&lt;p&gt;Les choses m’ont échappées. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Les fenêtres, commandées en septembre 2022 devaient être livrées en février 2023 et sont finalement arrivées en octobre 2023.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Fin 2022 on avait découvert que le plafond de la salle de bain, qui est commun avec l'appartement du dessus et l'appartement voisin, manquait de s'effondrer. Les surprises ont continuées dans la même direction : le plafond de la chambre, les infiltrations dans le salon par la cours commune, les problèmes de charpente, ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;En juin 2023, Un maitre d'oeuvre a été mandaté pour établir la liste des travaux à réaliser et effectuer les appels d'offre. Il a livré sont étude fin septembre. 160 000 € de travaux. Pour un appartement payé 150. La répartition n'a pas encore été rendue par le syndicat de copropriété, mais c'est une petite copro. Et clairement, les choses s'annoncent mal ...&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Objectif 5 : Développer mon système d’habitude&lt;/h2&gt;
&lt;p&gt;Cette année, j'ai essayé de travailler sur mon système plutôt que sur des objectifs de résultats : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;viser à écrire une heure par jour (moyen) plutôt que de viser à écrire un article/mois (résultat)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;nager 10 minutes chaque jour (système) plutôt que "faire du sport" (envie vague)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Je n'ai pas énormément avancé sur cette objectif, mais j’ai fait plusieurs expérimentations comme lire après ma pause déjeuner, nager le matin avant d’aller travailler pendant 30 jours ou tester une application de suivi des habitudes. Mais rien de très structuré.&lt;/p&gt;
&lt;p&gt;J’aimerais aborder les habitudes de façon plus sérieuse cette année et traiter l'expérimentation d'habitudes comme des projets à part entière : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;identifier "pourquoi" je souhaite mettre en place cette habitude&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;identifier quels résultats j'espère obtenir&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;donner une durée limitée à l'expérimentation (par exemple 30 jours)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;prendre des notes au fil de l'eau sur ce que j'observe&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;tirer les conclusions de l'expérience et partager via ce que j'en retiens via un article ou un post&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Je suis enthousiaste à l’idée d’en faire des expérimentations.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Objectif 6 : Développer un business de location de véhicules &lt;/h2&gt;
&lt;p&gt;Fin 2022, j'ai acheté un véhicule dans l'idée de l'utiliser pour mes besoins ponctuels et le mettre en location le reste du temps. &lt;/p&gt;
&lt;p&gt;Mon objectif était de générer 3 000 € de revenus sur l'année. &lt;/p&gt;
&lt;p&gt;Entre &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le changement de véhicule&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les investissements en accessoires&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les amendes &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;et les rayures. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Je dois avoir généré au moins -2000€. &lt;/p&gt;
&lt;p&gt;Donc, j'ai perdu de l'argent, mais cela reste une expérience enrichissante. J'ai fait un bilan complet dans cet article : [J'ai mis un véhicule en location pendant un an. Voici ce que j'ai appris.](https://100produits.substack.com/p/jai-mis-un-vehicule-en-location-pendant). Et je réalise que faire cette expérience était une façon de **commencer par un test peu couteux** sur ce type de business modèle. &lt;/p&gt;
&lt;p&gt;A présent, je sais que les business de type AirBnB qui fonctionnent sur le même modèle ne sont pas fait pour moi. Et il vaut mieux enregistrer des pertes sur une voiture qu'en immobilier. Les ordres de grandeur sont bien différents.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Rétrospective de l’année par trimestre&lt;/h1&gt;
&lt;h2&gt;🏔️ Janvier à mars&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Je suis en plein brouillard mental. Je vis mal mon expérience chez Marvin et le fait que le produit ne tourne toujours pas.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je suis la formation Atomic Timing (organisation), puis Atomic Knowledge (gestion de connaissances). C’est le début de mon second cerveau 🧠. Mon rapport à l'information change de dimension.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je suis sans nouvelles des fenêtres. Je découvre que le plafond de la chambre est lui aussi en mauvais état. Les travaux sont suspendus.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;🧳 Avril à juin 2022&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Je quitte Marvin. Cela aura été une aventure ou j’ai beaucoup appris. J’éprouve un sentiment de libération. J'ai envie de me lancer dans une nouvelle phase de vie : construire des produits, les lancer, itérer, livrer rapidement, partager ces aventures en ligne. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je me pose sur les 40 exercices de l'&lt;a href="https://www.ulysselubin.com/introspection"&gt;Atelier d'introspection&lt;/a&gt; d'Ulysse pour mieux me connaître et confirmer la direction dans laquelle je veux me lancer&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;On déménage à l'improviste à l’atelier&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je commence à louer un espace au Moho où je rencontre Pierre, Marie.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;En juin, je réalise un premier projet de ma phase builder : &lt;a href="https://open.substack.com/pub/100produits/p/les-fables-de-la-fontaine-avec-midjourney?r=wxia8&amp;amp;utm_campaign=post&amp;amp;utm_medium=web&amp;amp;showWelcomeOnShare=true"&gt;Publier une version illustrée des Fables de La Fontaine avec ChatGPT et Midjourney&lt;/a&gt;. J'échange souvent avec Corentin qui m'encourage à aller au bout.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/lafontaine-store.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Mon édition des fables de la Fontaine sur Amazon&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;🏖️ Juillet à septembre&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Je réalise un deuxième projet : &lt;a href="https://100produits.substack.com/p/creer-un-generateur-de-recommandations"&gt;Créer un générateur de recommandations LinkedIn&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je descends l’Orne en canoë avec Corentin (meilleure expérience de 2023)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;On termine d’emménager à l'atelier&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je me prends de passion pour les échecs&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Financièrement, ça commence à être compliqué&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;(Photo orne avec Coco)&lt;/p&gt;
&lt;h2&gt;🛠️ Octobre à décembre&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Je suis à court d’argent. Je n’avais pas anticipé la taxe foncière, le rattrapage des impôts, le remboursement de mon ordinateur. Je prends la décision de trouver un mission freelance que je veux trouver grâce à de la création de contenu.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;J'interromps mon abonnement au Moho et travail à la bibliothèque publique&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je me lance sur LinkedIn avec comme ambition de publier un post par semaine. Sur incitation de Gabriel à tester Capte, je réalise un premier screencast et me lance dans le challenge d’en réaliser 10.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je suis la formation de la [Visual Hackademy](https://www.thevisualhackademy.com/) pour illustrer mes pensées et rencontre Victor.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Christophe me recontacte pour me proposer de rejoindre l'équipe Tamis. Je suis le processus de recrutement classique et rejoint l'équipe mi-novembre !&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;J’engage de plus en plus facilement avec d’autres comptes sur LinkedIn. Je fais plusieurs des challenges proposés par Yoan. Je participe régulièrement aux réflexions de Kajan. Ces interactions m’apportent du bonheur.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/retro2023-katas.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Ma première vidéo Youtube !!&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Leçons et réflexions&lt;/h1&gt;
&lt;h2&gt;Les grandes leçons de vie que je retiens de 2023&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Software engineering &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Les CodeKatas sont une source d'apprentissage sous-côtée&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;LinkedIn&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;La meilleure stratégie pour démarrer est simple : participer régulièrement au contenu d'un compte. (puis étendre à 2, 3, ...)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interagir avec 1 personne &amp;gt; scroller et liker 10 posts random&lt;/strong&gt; &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Comportements&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Avoir le courage d'aborder les sujets difficiles paie, dès le court terme &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offrir la possibilité à une personne d'apporter son soutien. &lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Ne pas imposer ses choix. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Se lancer dans une grande aventure sans le soutien de son entourage est une bonne recette pour que cela échoue. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Finance&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Tes dettes et obligations financières limitent fortement tes options et ta capacité de mouvement. Dans mon cas : emprunt maison + voiture + ordinateur + rattrapage des impôts.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Ne jamais signer un devis sur le coup. Attendre au moins une nuit.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Il vaut mieux rater une bonne affaire que de faire une mauvaise affaire. En immobilier, des bonnes affaires, il y en aura toujours. Une mauvaise affaire peut te ruiner&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Commencer avec des tests peu couteux : avant d’acheter quelque part, commence par louer dans le coin. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Comparer le potentiel retour sur investissement au risque que tu prends (Sharp ratio)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ne pas mourir : se demander quels sont les erreurs éviter permet d'éliminer la majorité des mauvaises décisions (principe d'inversion)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Organisation&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Planifier ses journées à l'avance aide à être davantage présent&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Terminer la journée par une activité comme une heure de sport aide à couper entre travail et famille&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Système &amp;gt;&amp;gt; Capacités cognitives&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;l'impact des drogues douces (alcool, tabac, sucre) est largement sous estimé&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;tout s’apprends et se développe : expertise, identité, valeurs &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ne pas ouvrir LinkedIn le matin&lt;/strong&gt;. Cela m'absorbe trop et flingue ma concentration pour la matinée.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Leçons de l'emménagement à l'atelier&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Vérifier que tu as tout avant de te lancer : Porter une attention double aux points de bascule : commencer par vérifier que tu as tout ce dont tu as besoin pour aller au bout, considérer ce que tu ne pourras plus faire une fois lancé (ex : plus d’eau)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Autant que possible, &lt;strong&gt;demander des conseils et retours d’expérience à une personne qui est déjà passée par là&lt;/strong&gt; (ex : pour les toilettes sèches)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Regarder ce qui existe, les principaux acteurs du marché : ex pour les toilettes sèches, regarder les principaux commerçants &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Faire les recherches sur ordinateur plutôt que sur téléphone : il est plus facile d’ouvrir plusieurs onglets &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ne pas se précipiter&lt;/strong&gt; : pour les panneaux et la découpe, je me suis précipité. Pour le projet des toilettes sèches, je me suis précipité aussi. J’aurais du prendre une journée supplémentaire pour m’informer sur les premiers liens Wikipedia, voir les erreurs classiques, regarder des vidéos YouTube, interroger Matthieu. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Ce dont suis-je le plus fier&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;d'avoir trouvé le courage de me lancer en ligne sur LinkedIn, Substack, Youtube&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;d’avoir descendu l’Orne en canoë. C'était incroyable.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;En quoi suis-je différent par rapport à 2022&lt;/h2&gt;
&lt;p&gt;Je crois que suis moins dans le "status game”.&lt;/p&gt;
&lt;p&gt;J'ai de moins en moins de motivation externes. Je ne cherche plus à impressionner les autres, à leur démontrer qu'ils avaient tord. J'ai profondément envie d'expérimenter, d'itérer, d'explorer. D'être dans l'action.&lt;/p&gt;
&lt;p&gt;J'ai envie de comprendre plus que de juger.&lt;/p&gt;
&lt;p&gt;Je préfère gagner qu'avoir raison. &lt;/p&gt;
&lt;p&gt;Dans mon travail, je suis davantage au service du produit que de ma vision de ce que devrait être l’architecture. L’architecture logicielle est un outil incroyable pour soutenir l’impact d’un projet. Mais l’impact du projet est plus important.&lt;/p&gt;
&lt;p&gt;Je ne cherche plus à être lead. Je préfère être simple contributeur. J’ai testé être lead. Cela ne m’a pas plus. Je retenterais certainement plus tard dans un autre contexte, mais pour aujourd’hui je suis en paix avec cela.&lt;/p&gt;
&lt;h2&gt;Ce que je vais chercher à améliorer en 2023&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Qualité de présence&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Prendre de meilleurs décisions &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Système d’expérimentation d’habitudes&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Build&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Partager mes expérimentations en ligne (via NL, LK, Insta)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Storytelling&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Vente&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Marketing&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;Ce pour quoi j'ai le plus de reconnaissance en cette fin année&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Notre chez nous a l’atelier est extraordinaire. On n’y trouve pas tous les confort moderne. Mais il est merveilleux. On a du volume, de la place. La décoration que fait Inès est superbe. Et on peut construire chaque espace à notre rythme et à notre façon. J’éprouve un grand sentiment de bien être à y vivre.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;L’arrivée de Pan-cake&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le temps que j’ai pu passer à développer des projets personnels. Ce temps a été plus court que prévu, mais il a été extrêmement formateur et libérateur. Cela reste une chance incroyable d'avoir pu en bénéficier. J’ai repris confiance dans ma capacité à builder des projets de zéro. Je suis moins dogmatique grâce à cette expérience.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Mon contrat avec EDF&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Mon rapprochement avec mon frère&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/retro2023-beach.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Ness, Poupinou, Pan-cake et moi&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Ce qui me donne de l’énergie et de la joie&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;nager&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ne plus boire d’alcool&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;construire (des logiciels, mon bureau, ...)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;interagir avec des entrepreneurs et créateurs de contenu&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;Ce qui me prend de l’énergie&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;les interruptions &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire deux choses en même temps&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les conflits&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;C’était vraiment une année bizarre. &lt;/p&gt;
&lt;p&gt;Rien ne s’est passé comme prévu. &lt;/p&gt;
&lt;p&gt;J’ai eu pas mal de ratés. Mais il y a eu aussi de bonne expériences. &lt;/p&gt;
&lt;p&gt;Pour 2024, je vais essayer de me concentrer sur moins d’objectifs. &lt;/p&gt;
&lt;p&gt;Et voilà :) &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Si ce type d’exercice t’intéresse, je te mets le liens vers le template de rétrospective que j’ai utilisé : &lt;a href="https://philopreneurs.notion.site/Template-Revue-Annuelle-2022-18a7bd8062384e0dbcfa67834a76be51"&gt;template de Jean-Charles Kurdali&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A la prochaine 👋&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p class="button-wrapper" data-attrs='{"url":"https://100produits.substack.com/subscribe?","text":"Subscribe now","action":null,"class":null}' data-component-name="ButtonCreateButton"&gt;&lt;a class="button primary" href="https://100produits.substack.com/subscribe?"&gt;&lt;span&gt;Subscribe now&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title>Passer de zéro à 1000 sur chess.com en un mois</title><link href="https://me.petitapetit.io/chess" rel="alternate"/><published>2024-01-22T06:43:06.369000+00:00</published><updated>2024-01-22T06:43:06.369000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-01-22:/chess</id><summary type="html">&lt;p&gt;Hello !&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;J’ai tenté de passer de zéro à 1000 sur Chess.com. C'est-à-dire, de, je connais en partie les règles et n’ai pas joué depuis +10 ans, à je m’en sors bien face à 80% des gens. Cette expérience m’a beaucoup marquée. Est-ce que j’y …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello !&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;J’ai tenté de passer de zéro à 1000 sur Chess.com. C'est-à-dire, de, je connais en partie les règles et n’ai pas joué depuis +10 ans, à je m’en sors bien face à 80% des gens. Cette expérience m’a beaucoup marquée. Est-ce que j’y suis parvenu ?&lt;/p&gt;
&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;Lors d’un weekend en famille, j’ai fait une partie contre &lt;a href="https://www.instagram.com/balochex/?hl=en"&gt;@balochex&lt;/a&gt;. Puis contre &lt;a href="https://www.instagram.com/alylfvre/"&gt;@alylfvre&lt;/a&gt;. Et je me suis fait éclater. J’ai juste perdu toutes mes parties. J’étais vraiment nul.&lt;/p&gt;
&lt;p&gt;Mais, j’ai découvert un nouveau format : le blitz. Fini les parties interminables où chaque joueur met 4h à avancer. Dans ce format, chacun dispose de 5 minutes de temps de jeu. La partie va vite, et c’est prenant. &lt;/p&gt;
&lt;p&gt;La semaine suivante, je partais en trek-canoë avec mon frère. Il m’avait partagé plusieurs fois des extraits de partie d’échecs de Magnus Carlsen dans l’année. Je savais donc qu’il avait un intérêt pour le jeu. &lt;/p&gt;
&lt;p&gt;Nous allions avoir des temps morts. &lt;/p&gt;
&lt;p&gt;Du coup. &lt;/p&gt;
&lt;p&gt;Un tour en ville.&lt;/p&gt;
&lt;p&gt;Et me voilà équipé d’un jeu portable, un timer, et un livre sur les bases !&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-setup.jpg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Déroulé&lt;/h1&gt;
&lt;p&gt;Du jour au lendemain, j’ai plongé dans les échecs. &lt;/p&gt;
&lt;p&gt;Lors de notre trek en canoë, on a beaucoup joué les premiers jours. Le midi, je lisais “Bien débuter aux échecs”. Et quand je suis rentré, les échecs sont devenus mon obsession du moment.&lt;/p&gt;
&lt;p&gt;J’ai joué à fond pendant à peu près un mois.&lt;/p&gt;
&lt;p&gt;Puis j’ai arrêté comme j’avais commencé : du jour en lendemain.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-canoe.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Une partie d’échecs au bord de l’Orne avec mon frère&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Comment progresser&lt;/h1&gt;
&lt;p&gt;Au début, je me suis lancé sans réelle stratégie. &lt;/p&gt;
&lt;p&gt;Je me suis procuré un livre sur les bases, un jeu d’échecs, et j’ai créé un compte sur Chess.com. Je jouais et lisais, mais ce n’était pas structuré.&lt;/p&gt;
&lt;p&gt;Au fil de l’eau, j’ai reconnecté mon expérience avec les conseils partagés par Josh Kaufman dans “The First 20 Hours: How to Learn Anything . . . Fast!” et les leçons partagées par Ulysse Lubin dans ses &lt;a href="https://www.ulysselubin.com/blog/100"&gt;challenges&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Au bout du compte, j’ai identifié les éléments suivants. &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Entretenir l’inspiration : &lt;/strong&gt;la série “The Queen Gambit” et le livre “Le joueur d’échecs” de Stefan Zweig m’ont inspiré. Plonger dans des œuvres de fictions a participé à me faire entrer en immersion dans la discipline. Je remarque que cela fonctionne pour tous les domaines : lire “On Writing” de Stephen King est une bonne façon de commencer son voyage dans l’écriture. Lire la biographie de Steve Jobs est une bonne façon d’entrer dans la discipline du “Produit”. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Établir la “table des matières” (outline) la discipline&lt;/strong&gt; pour en cerner les contours, se faire une idée du chemin à parcourir afin de devenir un “expert” sur la théorie. Pour cela, je me suis aidé de ChatGPT. J’utilise souvent le prompt suivant :&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;Je souhaite acquérrir les bases de [DISCIPLINE]. Peux-tu générer la outline de la discipline avec 3 niveaux d'imbrications ? Tu peux présenter le résultat sous la forme de listes imbriquées.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documenter chaque concept rencontré dans mon “second cerveau” : &lt;/strong&gt;au fil de l’eau, je capturais dans une liste tous les concepts rencontrés. Ensuite, je les documentais et les clarifiais. Faire cela m’a permis d’acquérir rapidement le vocabulaire des échecs et d’organiser les conseils stratégiques autour de chaque concept. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-concepts.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Outline (à gauche) et graphe (à droite) de mes notes sur les échecs dans Obsidian.&lt;/p&gt;&lt;/div&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-rook.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Ma fiche sur la Tour : j’y ai regroupé les conseils stratégiques récoltés au fil de l’eau comme “aime les colonnes ouvertes”. On voit aussi en bas de note que la tour est mentionnée dans la fiche sur le clouage. Et oui, la tour peut par exemple clouer la dame sur le roi.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;S’échauffer avec de courtes sessions de pratique intentionnelles&lt;/strong&gt; : commencer chaque séance de pratique par une minute d’entrainement à la lecture de l’échiquier grâce au &lt;a href="https://www.chess.com/vision"&gt;mode d’entrainement vision&lt;/a&gt;. Apprendre à lire l’échiquier naturellement est fastidieux. S’entraîner à lire pendant de longues sessions et ennuyant. Mais des sessions régulières d’une minute suffisent à acquérir cette compétence rapidement. De façon plus générale, certains développeurs commencent leur journée de travail avec un CodeKata pour s’échauffer. C’est la même idée.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Travailler la tactique grâce aux problèmes&lt;/strong&gt;. Chess.com propose de nombreux problèmes ou “puzzles”.  En faire beaucoup permet de reconnaître les situations qui offrent des opportunités tactiques. On parle parfois de “thèmes tactiques” et de “pattern matching”. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regarder des parties pédagogiques&lt;/strong&gt;. J’adore les vidéos partagées par Kévin Bordi sur sa chaîne BlitzStream. Les parties pédagogiques sont un excellent moyen de progresser.  C’est un format qui est à la fois divertissant et intéressant puisque Kevin partage des conseils tactiques et stratégiques en situation, au fil de l’eau. Un point fort de sa pédagogie est que dans ses vidéos pour les débutants, il se concentre sur les bases : le minimum vital qu’il te faut pour survivre dans la jungle des débutants. Cette approche permet de garder les idées claires et de ne pas s’embrouiller avec des concepts avancés quand tu galères déjà à aligner deux pas. &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pratiquer&lt;/strong&gt;. Depuis que j’ai lu “How to learn anything fast”, j’ai toujours en tête le schéma ci-dessous : la théorie renforce la pratique, mais seule la pratique permet d’acquérir une compétence. Tu peux lire tous les livres que tu veux. Si tu ne pratiques pas, tu ne vas pas progresser. J’ai joué 350 parties sur le mois. Cela fait une moyenne de 3,5 partie par jour. Avec le recul, cela ne me paraît même pas tant que ça. Mais clairement, pratiquer m’a permis de progresser :) &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-learn-schema.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Le mécanisme d’acquisition de compétence&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Ces techniques peuvent ensuite être organisées en brique d’entraînement et assemblées dans un programme. Voici quelques exemples.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Heure de pratique&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Exercice de vision (1 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Puzzles (24 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Parties rapides (25 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Heure théorie + pratique&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Parties pédagogiques (25 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Parties rapides (25 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Heure ciblée&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Puzzles centrés sur un thème (ex : faire mat avec un roi et dame, faire mat avec roi et tour, faire mat avec deux tours, …) (25 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Parties rapides (25 min)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pause (5 min)&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Les points à travailler émergent souvent des parties rapides. Si tu t’es fait avoir en fin de partie car tu as fait pat avec ton roi et ta tour, tu peux ajouter le Mat Tour-Roi à ta liste de thèmes à travailler :).&lt;/p&gt;
&lt;h1&gt;Bilan&lt;/h1&gt;
&lt;p&gt;À la fin du mois, j’ai atteint le niveau 500 sur Chess.com. &lt;/p&gt;
&lt;p&gt;On est loin du niveau visé. Je ne suis pas sorti de la mêlée. Je suis même en plein dedans. Mais je suis tout de même content du chemin parcouru.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/chess-rank.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Mon classement sur chess.com à la fin du mois. Je suis mieux classé que 35% des inscrits ! ou moins bien classé que 65% des inscrits. Tout dépend de comment on voit les choses ^^.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Parmi les choses qui ont bien marché, je vois en particulier ces 3 points : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;une approche hyper-focus (je vivais échec)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;se concentrer sur les bases (et laisser de côté les concepts avancés)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un bon équilibre entre théorie et pratique&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Plonger à fond dans une discipline sur une période de temps courte était une expérience incroyable. Je pense que si j’avais été organisé dès le départ, j’aurais pu atteindre le 80ᵉ centile. La démonstration reste à faire sur une prochaine expérience. Mais j’y crois.&lt;/p&gt;
&lt;p&gt;J’ai réalisé qu’apprendre de nouvelles choses est une compétence. En général, on a peur de se lancer. Mais en ciblant un seul problème, et en mélangeant théorie et pratique sur une période de temps pas si longue, il est possible de progresser  rapidement.&lt;/p&gt;
&lt;p&gt;Enfin, le cas de &lt;a href="https://www.youtube.com/channel/UCcXH6W9ey_h8LEx2lFxp5fg"&gt;Kevin Bordi&lt;/a&gt; de la chaîne  Blitzstream m’a inspiré. &lt;/p&gt;
&lt;p&gt;Il stream depuis des années avec régularité. Beaucoup de ses lives ne sont pas préparés, il partage simplement ce qu'il sait déjà au moment où les coups viennent. Et partager/enseigner les bases lui permettent de les consolider dans sa tête. &lt;/p&gt;
&lt;p&gt;Dans cette veine, j’ai commencé à publier des screencast de code kata. Je me suis donné pour objectif d’en faire 10 dans un premier temps. &lt;a href="https://www.youtube.com/@a-petit/featured"&gt;J’en suis à 5 à ce jour&lt;/a&gt;. Et cela fera l’objet d’une prochaine newsletter :).&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Merci de m’avoir lu jusqu’ici !&lt;br/&gt;&lt;br/&gt;Si tu penses que cet article peut intéresser quelqu’un, n’hésites pas à &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le transférer &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le partager&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;À la prochaine :)&lt;/p&gt;</content></entry><entry><title>J'ai mis un véhicule en location pendant un an</title><link href="https://me.petitapetit.io/location" rel="alternate"/><published>2024-01-08T07:04:54.678000+00:00</published><updated>2024-01-08T07:04:54.678000+00:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2024-01-08:/location</id><summary type="html">&lt;p&gt;Salut les gros,&lt;/p&gt;
&lt;p&gt;L’année dernière, j’ai testé de générer des revenus via la location de véhicule. &lt;/p&gt;
&lt;p&gt;Voici mon retour d’expérience.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/location-annonce.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;J'ai commencé à m'intéresser à l'entrepreneuriat fin 2022. &lt;/p&gt;
&lt;p&gt;Et au cours de l’année, il m’est arrivé de louer des utilitaires à plusieurs reprises pour …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Salut les gros,&lt;/p&gt;
&lt;p&gt;L’année dernière, j’ai testé de générer des revenus via la location de véhicule. &lt;/p&gt;
&lt;p&gt;Voici mon retour d’expérience.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/location-annonce.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Contexte&lt;/h1&gt;
&lt;p&gt;J'ai commencé à m'intéresser à l'entrepreneuriat fin 2022. &lt;/p&gt;
&lt;p&gt;Et au cours de l’année, il m’est arrivé de louer des utilitaires à plusieurs reprises pour réaliser des travaux. &lt;/p&gt;
&lt;p&gt;Lorsque j’ai découvert le retour d'expérience de Yoann Lopez (Snowball) dans la location de véhicule, j’ai décidé de me lancer dans l'expérience. &lt;/p&gt;
&lt;p&gt;Mon projet était alors le suivant : acheter un véhicule pour en disposer de façon ponctuelle sur mon temps libre (weekends en famille, travaux, ...) et le mettre sur une plateforme de location le reste du temps. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Objectif &lt;/h1&gt;
&lt;p&gt;L’objectif que je me suis fixé était simple : générer 3 000 € de revenus en un an.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Déroulé des opérations&lt;/h1&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Janvier 2023 : j'achète un utilitaire d'occasion. Je n'y connais rien en utilitaires. J'aurais aimé un Traffic mais c'est hors budget. Je pars sur un Nemo de 2018 avec 50 000 kilomètres pour 10 400 € TTC.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Première déconvenue : la voiture pas compatible avec le boitier qui permet de louer à distance. Je me rends sur place à chaque location pour donner les clés au locataire puis les récupérer. J'aime bien le contact client et cela donne lieu à des échanges sympas la majorité du temps. Mais, c'est chronophage et contraignant, car une location peut tomber à n'importe quelle heure.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Après quelques locations, je change de voiture. J'en profite pour prendre un modèle plus grand (Kangoo) car le Nemo, c'est vraiment minuscule. Je pars sur un Kangoo de 2019, 85 000 km à 11 100 € TTC. Je perds du temps et 2000 € à la revente du Nemo.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je bascule de OuiCar vers Getaround pour bénéficier de 4 mois gratuits sur la location du boitier. L'installateur met + d'un mois à venir. Je n'ai aucune demande de location. C'est plus une galère qu'autre chose car je garde le véhicule propre et garé loin de chez moi, pour rien.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je constate que le véhicule n'est pas rangé dans les utilitaires. La plateforme me rétorque que c'est normal. 🤨&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Juillet : au bout de 2 mois sans réservation, je &lt;s&gt;baisse&lt;/s&gt; casse les prix pour obtenir une première location&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Aout : c’est toujours aussi calme. Je ne pense même plus à rendre le véhicule indisponible dans le calendrier. Le véhicule se retrouve loué une semaine à un moment où je comptais dessus.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le véhicule est abimé par le premier locataire. Je ne dis rien, car les premiers avis sont importants.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le véhicule est abimé par le deuxième locataire. Là encore, je ne dis rien pour avoir une bonne note. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Septembre : C'est la fin du code promo et le boitier va désormais m'être facturé. J’ai eu deux locations en quatre mois. Je demande la désinstallation.  On m'annonce un mois pour avoir un entretien téléphonique pour planifier la désinstallation. En attendant, je paie. WTF ???&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Je rebascule sur OuiCar. J'ai plein de demandes de locations. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Getaround m'appelle. On identifie qu'effectivement le véhicule est mal catégorisé. On corrige cela et on boost mon annonce. Les locations redémarrent normalement.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Fin 2023, je redémarre une mission sur Paris. La rentabilité est trop basse et je ne souhaite plus y consacrer de temps. Je mets fin à l'expérience.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1&gt;Quelques chiffres&lt;/h1&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/location-revenues.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;OuiCar a été racheté par Turo et je n'ai pas récupéré mes données avant la transition 😅.&lt;/p&gt;
&lt;p&gt;On voit sur les locations que j'ai pu faire avec Getaround que le rendement au kilomètre est faible : 0,13 €. &lt;/p&gt;
&lt;p&gt;Aussi, pour chaque location, il faut prévoir de passer à la voiture, vérifier que tout est propre et aller la laver de temps en temps. Le rendement horaire est donc lui aussi assez mauvais. Et il faut prendre en compte les charges importantes qu'il y a dans ce business (achat du véhicule, assurance, entretien, frais de nettoyage).&lt;/p&gt;
&lt;h1&gt;Enseignements&lt;/h1&gt;
&lt;h2&gt;Les choix tranchés sont plus optimisables&lt;/h2&gt;
&lt;p&gt;J'ai choisi la voiture parce qu'elle pouvait &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;être louée facilement ET &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;servir à mon usage personnel.  &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Mais ce double usage présente plusieurs problèmes.&lt;/p&gt;
&lt;p&gt;Dans le choix du véhicule : pour un usage strictement personnel, je me serais certainement tourné vers la version 5 places modulable (type ludospace). Le modèle est moins cher, permet de faire du covoiturage, de déplacer plus de monde, et le volume utile reste grand si l'on retire les sièges. &lt;/p&gt;
&lt;p&gt;Dans la gestion au quotidien : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Emplacement&lt;/strong&gt; : pour la location, il est important que le véhicule soit garé dans un endroit accessible et avec un parking gratuit. Lorsque j'avais besoin du véhicule, je devais donc commencer par aller le chercher à vélo, puis le redéposer en rentrant de weekend. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Essence&lt;/strong&gt; : la gestion du niveau d'essence est une galère. Mon voyant manque de précision et le détecteur automatique de la plateforme n'est pas fiable non plus. L'idéal est de toujours donner le véhicule avec un plein à 100%. Mais cette approche implique de refaire le plein après chaque usage personnel, c'est trop contraignant.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disponibilité&lt;/strong&gt; : je n'ai qu'un besoin ponctuel de la voiture, mais lorsque j'en ai besoin, c'est souvent le weekend. Or, le weekend est aussi la période où il y a le plus de demande. Donc, fatalement,  comme j'ai régulièrement utilisé la voiture le weekend, cela a impacté le CA.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Même préparé, il te restera des surprises&lt;/h2&gt;
&lt;p&gt;Sur ce projet, je me suis dit : ce coup-ci, tu te prépares !&lt;/p&gt;
&lt;p&gt;J'ai lu les retours d'expérience de Yoan et ses conseils pour bien démarrer. J'ai pris mon temps pour surveiller les annonces de voitures sur Leboncoin afin d’y aller sans précipitations. J'ai balayé la documentation "propriétaire" de OuiCar dans le but de connaître les caractéristiques des véhicules éligibles à la location à distance. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Malgré cela, j’ai eu quelques ratés : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;le Nemo était un cas particulier. Il répond au critère d'éligibilité, mais le boitier ne fonctionne pas dessus.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;le Nemo est le plus petit modèle d'utilitaire. Ce que je voulais, c'est plutôt le volume du Berlingo / Kangoo.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Ce que j'aurais pu faire : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;demander confirmation de l'éligibilité à la plateforme avant de finaliser l'achat du véhicule&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;me rendre chez un concessionnaire spécialisé en utilitaires pour visualiser les différents modèles. Cela aurait nécessité que je loue un véhicule (disons 100 €) mais m'aurait permis d'économiser les 2000 € perdus à la revente.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Les premières locations sont clés&lt;/h2&gt;
&lt;p&gt;Lorsque l'annonce entre en ligne, la plateforme est particulièrement attentive aux premières locations. Obtenir 5 étoiles sur ses premières locations permet de rentrer dans les petits papiers de l'algo.  &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Avoir des évaluations 5 étoiles est simple, mais demande une attention constante&lt;/h2&gt;
&lt;p&gt;Les locataires sont plutôt enclins à laisser 5 étoiles. Et la recette est simple : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;être souriant&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un véhicule propre&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;avoir pensé à lui : câble de charge, couverture de déménagement, ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;une communication simple, claire et rassurante&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;demander au locataire si ça a été (lui témoigner de l'attention)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;indiquer au locataire que vous allez l'évaluez positivement et lui demander de vous évaluer&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Démarrer un business demande beaucoup d'énergie au départ&lt;/h2&gt;
&lt;p&gt;Cela m'aura pris un an pour stabiliser le projet (juste avant que je l'interrompe). Si j’étais hyper motivé au départ, les complications ont aussi fait que je suis vite passé à autre chose et ce projet a parfois été un boulet dans mon organisation. Il y a beaucoup d'investissements à faire au départ, mais une fois que l'affaire est rodée, la charge diminue drastiquement. &lt;/p&gt;
&lt;p&gt;Dans un contexte plus large, j'en retiens qu'il faut laisser du temps aux expérimentations pour qu'elles puissent exprimer leur potentiel. Par exemple, dans mon projet de publication de screencasts de code, je me suis donné comme objectif d'en publier 10 afin de pousser l'expérience.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Bilan&lt;/h2&gt;
&lt;p&gt;J'ai plutôt perdu de l’argent dans cette affaire. Mais en même temps, je me suis lancé un peu à l'aveugle, sans faire de calculs de rentabilité en amont. Pour que le business soit rentable, cela demanderait de s'y consacrer sérieusement et d'optimiser chaque aspect. Mais c'est une piste que je n'ai pas envie de creuser davantage.&lt;/p&gt;
&lt;p&gt;Les revenus générés par la plateforme m'ont tout de même bien servi à des moments où je n'avais pas beaucoup d'argent disponible. Et globalement, ça a été une bonne expérience. &lt;/p&gt;
&lt;p&gt;J'ai bien aimé les échanges avec les locataires. &lt;/p&gt;
&lt;p&gt;Je me suis rendu compte qu'il n'y a pas d'argent magique. &lt;/p&gt;
&lt;p&gt;Et cela me fait une histoire à raconter. &lt;/p&gt;
&lt;p&gt;Voilà,&lt;/p&gt;
&lt;p&gt;à plus pour la prochaine aventure !&lt;/p&gt;</content></entry><entry><title>Descendre l'Orne en Canoë</title><link href="https://me.petitapetit.io/canoe" rel="alternate"/><published>2023-08-20T00:00:00+02:00</published><updated>2023-08-20T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2023-08-20:/canoe</id><summary type="html">&lt;p&gt;J'ai descendu l'Orne en canoë, avec mon frère.&lt;/p&gt;
&lt;p&gt;C'était l'une des meilleures expériences de ma vie.&lt;/p&gt;
&lt;h2&gt;Bilan&lt;/h2&gt;
&lt;p&gt;J'avais prévu 5 jours. On a mis 7 jours (avec des journées de 8h).&lt;/p&gt;
&lt;p&gt;J'avais prévu d'y aller seul. 
Mon frère s'est joint à moi. 
Heureusement. 
Seul, cela aurait été très difficile (physiquement …&lt;/p&gt;</summary><content type="html">&lt;p&gt;J'ai descendu l'Orne en canoë, avec mon frère.&lt;/p&gt;
&lt;p&gt;C'était l'une des meilleures expériences de ma vie.&lt;/p&gt;
&lt;h2&gt;Bilan&lt;/h2&gt;
&lt;p&gt;J'avais prévu 5 jours. On a mis 7 jours (avec des journées de 8h).&lt;/p&gt;
&lt;p&gt;J'avais prévu d'y aller seul. 
Mon frère s'est joint à moi. 
Heureusement. 
Seul, cela aurait été très difficile (physiquement et mentalement).&lt;/p&gt;
&lt;p&gt;Le trajet fait 120km:
&lt;img alt="" src="_assets/canoe-parcours.png"&gt;&lt;/p&gt;
&lt;h2&gt;Jour 1 (7km)&lt;/h2&gt;
&lt;p&gt;C'est le premier jour ! &lt;/p&gt;
&lt;p&gt;On est parti de la gare de Caen à 9h.
On arrive à Argentan vers 11h. &lt;/p&gt;
&lt;p&gt;Coco a la bonne idée d'acheter un pack d'eau. 
Je pensais partir juste avec ma gourde (mauvaise idée).&lt;/p&gt;
&lt;p&gt;On gonfle les canoës. 
Il manque un bouchon pour gonfler la troisième partie du canoë. 
Fuck. 
C'est férié et on est sans voiture. 
On tente d'y aller quand même.&lt;/p&gt;
&lt;p&gt;Ça ne va pas le faire. 
On repère un décathlon à 3km.
On s'arrête pour aujourd'hui.
J'irai chercher un bouchon demain matin.&lt;/p&gt;
&lt;p&gt;Trajet réalisé le jour 1 (7km) :
&lt;img alt="" src="_assets/canoe-jour-1.png"&gt;&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j1-1.jpeg'&gt;&lt;img src='_assets/canoe/mini/j1-1.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j1-2.jpeg'&gt;&lt;img src='_assets/canoe/mini/j1-2.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h2&gt;Jour 2&lt;/h2&gt;
&lt;p&gt;Je vais à Décathlon. 2h de marche aller-retour. Ils ont une pièce en stock. Ouf. On est passé à deux doigts de saboter l'expédition.&lt;/p&gt;
&lt;p&gt;On repart à 14h.&lt;/p&gt;
&lt;p&gt;On navigue jusqu'à 19h.&lt;/p&gt;
&lt;p&gt;Je me couche inquiet.
Cela fait deux jours qu'on est parti.
On a fait 18 km sur 120.
On doit absolument être rentré dans 5 jours.
Mais regarder la carte ne me rassure pas :&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/canoe-j3.png"&gt;&lt;/p&gt;
&lt;p&gt;Trajet réalisé le jour 2 (11 km) : 
&lt;img alt="" src="_assets/canoe-jour-2.png"&gt;&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j2-1.jpeg'&gt;&lt;img src='_assets/canoe/mini/j2-1.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j2-2.jpeg'&gt;&lt;img src='_assets/canoe/mini/j2-2.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j2-3.jpeg'&gt;&lt;img src='_assets/canoe/mini/j2-3.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h2&gt;Jour 3&lt;/h2&gt;
&lt;p&gt;Beaucoup d’arbres renversés à traverser ce matin. 
On est parti de 48,73537° N, 0,19076° à 9h50 avec comme objectif d’atteindre Giel-Courteille pour déjeuner. 
Il y a eu de beaux passages. 
On s’est arrêté à 13h10, juste avant Giel. 
On a fait une heure de sieste pour repartir à 14h50. &lt;/p&gt;
&lt;p&gt;L’après-midi, nous avons traversé beaucoup de radiers. 
Cela nous a pas mal ralenti, mais nous sommes tout de même arrivé à Pont-Ecrevin à 18h55. 
5 minutes avant que ne ferme la pharmacie et la boulangerie ! &lt;/p&gt;
&lt;p&gt;J’ai pu choper des paracetamols pour soulager mon mal de tête, on s’est chopé du dessert pour le soir et on s’est ravitaillé en eau et alimentation.&lt;/p&gt;
&lt;p&gt;On s’est arrêté pour bivouaquer juste à la sortie de la ville. 
Demain, on attaque le lac de putange !&lt;/p&gt;
&lt;p&gt;On a refait un point sur le trajet restant. 
En atteignant pont d’Ouilly demain soir puis Thury harcourt samedi soir, il va être possible d’être à Caen dimanche soir.&lt;/p&gt;
&lt;p&gt;Pas si dur finalement !&lt;/p&gt;
&lt;p&gt;Cette excursion est une épreuve bien plus mentale que je ne m’y attendais.&lt;/p&gt;
&lt;p&gt;On va passer en mode sport là.&lt;/p&gt;
&lt;p&gt;Trajet réalisé le jour 3 (19 km) : 
&lt;img alt="" src="_assets/canoe-jour-3.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/j3.jpeg"&gt;&lt;/p&gt;
&lt;h2&gt;Jour 4&lt;/h2&gt;
&lt;p&gt;Il a plut fort entre 4 et 7h et il y a eu beaucoup d’orage. 
On faisait pas les malins dans nos tentes. 
La pluie fait un boucan d’enfer quand on est sous la toile. 
Mais globalement, la nuit c’est bien déroulée.&lt;/p&gt;
&lt;p&gt;Le matin on a un peu traîné car il pleuvait entre 10h et 11h. 
Le démarrage a été difficile. 
On avait mal anticipé la pluie. 
Les canoës étaient remplis d’eau. 
La réparation du siège de Coco n’a pas fonctionné. 
On est parti à 11h dans un climat chaud et humide avec une petite énergie. &lt;/p&gt;
&lt;p&gt;La sortie de pont ecrepint conduit directement à putange. 
Là on découvre une immense retenue d’eau. 
Le lit s’élargit. 
C’est impressionnant de pagayer au milieu. 
Le point frustrant est qu’on a l’impression de ne pas avancer, car les repères défilent lentement.&lt;/p&gt;
&lt;p&gt;On a croisé des tentes de pêcheurs sur la première partie. 
Puis un ski nautique et des pédalos sur la deuxième. 
Il y a des baraques incroyables aux abords du lac. &lt;/p&gt;
&lt;p&gt;À midi, l’humidité c’est levée. 
On s’est arrêté faire une pause déjeuner à 13h car le soleils tapait très fort.&lt;/p&gt;
&lt;p&gt;On a déjeuné dans un sous bois sur les bords du lac. 
Je me suis lavé dans l’Orne et ai joué ma tenue de rechange. 
Puis on a fait une sieste pour digérer et bien repartir.&lt;/p&gt;
&lt;p&gt;Rapidement, on a atteint la fin du lac et le barage. 
Et là, problème. 
Une ligne de quille estampillées « navigation interdite » , « danger » barre la route.&lt;/p&gt;
&lt;p&gt;C’est vrai au fait, comment on passe de l’autre côté ?&lt;/p&gt;
&lt;p&gt;On aperçoit un jeune homme qui pêche pas très loin. 
Corentin s’approche pour lui demander conseil. 
Je le rejoins. 
Il nous indique un chemin : remonter sur la route par un chemin escarpé et contourner le barrage, 1km. &lt;/p&gt;
&lt;p&gt;Merde. &lt;/p&gt;
&lt;p&gt;Ça va être au chariot de jouer !&lt;/p&gt;
&lt;p&gt;On monte les bagages en premier. 
Puis on redescend chercher les canoës un par un. 
Le garçon est très sympa. 
Arrivé en haut, on est cuit.&lt;/p&gt;
&lt;p&gt;Une randonneuse passe à côté de nous.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bonjour ! &lt;/li&gt;
&lt;li&gt;Bonjour !&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Elle nous demande ce qu’on fait là. 
Je lui dit qu’on fait du canoë et lui indique la direction que l’on souhaite prendre. Elle me dit :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;non par ici c’est pas possible, mais vous pouvez aller vers putange&lt;/li&gt;
&lt;li&gt;putange on en viens. On vient d’argentan là &lt;/li&gt;
&lt;li&gt;Argentan ? Vous avez passé les boucles et tout ?&lt;/li&gt;
&lt;li&gt;Oui ^^&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On a bien rigolé. 
Très sympa, elle nous a accompagné jusqu’à l’autre côté du barrage en nous aidant à porter nos sacs et nos canoë. 
Spolier : on a galéré.&lt;/p&gt;
&lt;p&gt;Le chariot fonctionne peut-être bien pour un canoë. 
Mais pas pour deux. 
Où alors on a pas compris le sanglage. 
Bref, là, ils se décrochait souvent. 
On a papoté, poussé, tiré, sanglé, descanglé, remonté. 
Pfiou. &lt;/p&gt;
&lt;p&gt;Arrivé au panorama au-dessus du barrage, on s’est rendu compte qu’il y avait un passage 100x plus simple en face…&lt;/p&gt;
&lt;p&gt;J’étais en age. 
Mes vêtements tout propres étaient déjà ruinés. 
On a sué, mais on a aussi bien rigolé avec Nelly et ses anecdotes.&lt;/p&gt;
&lt;p&gt;On est entré dans les rapides à 17h ?&lt;/p&gt;
&lt;p&gt;On a tiré jusqu’à 20h. 
Le paysage des gorges de saint aubert est magnifique. &lt;/p&gt;
&lt;p&gt;Être dans les rapides est hyper physique. 
Au début, on avait pas mal d’eau, puis moins. 
On progresse à petit rythme dans cette partie car il faut régulièrement mettre le pied hors du canoë pour se déloger d’une pierre.&lt;/p&gt;
&lt;p&gt;On s’est arrêté à 20h rincé : en âge, épuisé et trempé d’eau à force de descendre et remonter dans le canoë dans un environnement où le niveau varie de 5 cm à 1M.&lt;/p&gt;
&lt;p&gt;Il commençait à pleuvoir alors on a planté les tentes. &lt;/p&gt;
&lt;p&gt;La suite demain !&lt;/p&gt;
&lt;p&gt;Trajet réalisé le jour 4 &lt;strong&gt;(9km)&lt;/strong&gt; :
&lt;img alt="" src="_assets/canoe-jour-4.png"&gt;&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-1.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-1.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-2.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-2.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-3.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-3.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-4.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-4.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-5.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-5.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-6.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-6.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-7.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-7.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j4-8.jpeg'&gt;&lt;img src='_assets/canoe/mini/j4-8.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h2&gt;Jour 5&lt;/h2&gt;
&lt;p&gt;Levé à 8h. 
J’ai dormi en caleçon car mon short et mon t shirt sont trempés de la veille. Entre les rapides et la pluie de début de soirée, C’était le mauvais timing pour claquer mon t-shirt de rechange. &lt;/p&gt;
&lt;p&gt;Départ à 9h30 le temps de se réveiller, petit déjeuner, replier le camp, de redescendre les affaires au canoë, de vider les canoës et de les charger. &lt;/p&gt;
&lt;p&gt;Le départ est difficile. L’avancée dans les rapides est laborieuse. Je trébuche plusieurs fois sur des rochers. Je manque de renverser mon canoë. Je descends fréquemment du canoë pour me décoincer d’un rocher ou me sortir d’une eau peu profonde.&lt;/p&gt;
&lt;p&gt;On a fait un break à 11h30. J’ai mis de la crème solaire, mangé qqes noix de cajou, bu, pipi.&lt;/p&gt;
&lt;p&gt;En deuxième partie de matinée, on est sorti des rapides. Le lit s’est resserré et le cour c’est calmé. C’était un passage agréable. On a du franchir une petite dizaine d’arbres tout de même. Le plus souvent il restait un passage où se faufiler, parfois en forçant un peu. Sinon, on portait.&lt;/p&gt;
&lt;p&gt;On a croisé la route d’un troupeau de vache qui nous barrait la route au milieu de l’eau. On a mis le pieds à l’eau, on est passé tranquillement et tout s’est bien passé.&lt;/p&gt;
&lt;p&gt;Vers 13h, on est sorti des gorges pour arriver dans les méandre de de l’Orne, en amont de Saint-Philbert. À ce niveau, on retrouve de grandes étendues d’eau. Le lit et large et le paysage marécageux avec beaucoup de lentilles et de roseaux.&lt;/p&gt;
&lt;p&gt;On s’est arrêté pour déjeuner à 13h30 au pieds de saint philbert. On a mis nos vêtements à sécher sur le canoë, on est monté déjeuner à l’ombre. Il ne nous restait plus beaucoup d’eau donc on ne s’est pas lavé les mains pour l’économiser. On a mangé uniquement aux couverts : une boîte de sardines, qqes pistaches, et go.&lt;/p&gt;
&lt;p&gt;Le temps de revider les canoë et de les charger, on est reparti à 15h.&lt;/p&gt;
&lt;p&gt;Aller !&lt;/p&gt;
&lt;p&gt;Le départ était difficile pour moi. Coco filait joyeusement à travers les lentilles. J’avais l’impression de ne pas avancer. J’avais envie de pagayer tranquillement et de contempler le paysage. Ce que j’ai fini par faire.&lt;/p&gt;
&lt;p&gt;15h30 : tiens tiens, j’aperçois des bouées « zone interdite ». Ça me rappelle quelque chose. On arrive sur le barrage de saint philbert.&lt;/p&gt;
&lt;p&gt;Cela fait à peine 30 minutes qu’on est reparti. C’est un gros barrage. On se dit qu’on est pas au bout de nos peines.&lt;/p&gt;
&lt;p&gt;Ce coup si, on a compris la leçon. On descend à pieds repérer. On se met d’accord sur la stratégie. On revient porter. D’abord les sacs. Puis le premier canoë, enfin le second. On met à l’eau, on charge, et c’est reparti ! Barrage franchi en -45 minutes !&lt;/p&gt;
&lt;p&gt;Le reste de la descente est du pur plaisir. L’eau n’était pas très profondes, mais juste assez. Je ne suis sorti du canoë pour me débloquer qu’une seule fois, on a traversé plein de rapides, il y avait du courant, on est passé par des rives magnifiques, notamment autour de le pont des vers. &lt;/p&gt;
&lt;p&gt;C’était tellement agréable de filer sans obstacles et sans forcer après tous ces efforts !&lt;/p&gt;
&lt;p&gt;On est arrivé à pont d’ouilly à 18h. &lt;/p&gt;
&lt;p&gt;Là on s’est arrêté pour ravitailler. On a pris deux toboggans canons. Et on a avancé un peu après pont d’ouilly pour bivouaquer.&lt;/p&gt;
&lt;p&gt;Voilà pour aujourd’hui !&lt;/p&gt;
&lt;p&gt;Trajet réalisé le jour 5 (18km) :
&lt;img alt="" src="_assets/canoe-jour-5.png"&gt;&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-1.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-1.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-2.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-2.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-3.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-3.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-4.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-4.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-5.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-5.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-6.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-6.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href='_assets/canoe/normal/j5-7.jpeg'&gt;&lt;img src='_assets/canoe/mini/j5-7.jpeg'/&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h2&gt;Jour 6 (28km)&lt;/h2&gt;
&lt;p&gt;Finit la bamboche.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="_assets/canoe-jour-6.png"&gt;&lt;/p&gt;
&lt;h2&gt;Jour 7 (30km)&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="_assets/canoe-jour-7.png"&gt;&lt;/p&gt;</content></entry><entry><title>Créer un générateur de recommandations LinkedIn</title><link href="https://me.petitapetit.io/recommandations" rel="alternate"/><published>2023-07-01T00:00:00+02:00</published><updated>2023-07-01T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2023-07-01:/recommandations</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Aujourd'hui, je vous partage mon aventure dans le développement d'un générateur de recommandation LinkedIn. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-app.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;ça ressemble à ça à la fin ! ☝️&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Au programme de cette édition : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Introduction&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Qu’est-ce qu’une recommandation LinkedIn ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pourquoi écrire une recommandation LinkedIn ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;La difficulté&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le concept&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Exécution&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Quelques recherches en amont&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Construire le …&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Aujourd'hui, je vous partage mon aventure dans le développement d'un générateur de recommandation LinkedIn. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-app.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;ça ressemble à ça à la fin ! ☝️&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Au programme de cette édition : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Introduction&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Qu’est-ce qu’une recommandation LinkedIn ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pourquoi écrire une recommandation LinkedIn ?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;La difficulté&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Le concept&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Exécution&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Quelques recherches en amont&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Construire le produit&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Diffuser le produit&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Bilan&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Leçons&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Monétisation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Conclusion&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;C'est parti !&lt;/p&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;blockquote&gt;&lt;p&gt;Quoi !? Pourquoi ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Début 2023, je reçois le message d'un ancien collègue. Il est en plein recrutement et me demande si je peux lui écrire une recommandation LinkedIn pour le supporter dans ce processus. &lt;/p&gt;
&lt;p&gt;Je réponds : oui, pas de soucis ! &lt;/p&gt;
&lt;p&gt;J'y pense. &lt;/p&gt;
&lt;p&gt;Je prends quelques notes. &lt;/p&gt;
&lt;p&gt;Je veux que la recommandation soit top. &lt;/p&gt;
&lt;p&gt;J'hésite.&lt;/p&gt;
&lt;p&gt;Je laisse maturer.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Bref, ça traine ...&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Mais déjà, c'est quoi une recommandation LinkedIn ? &lt;/h2&gt;
&lt;p&gt;Les recommandations LinkedIn sont une version moderne des lettres de recommandation, sauf qu'elles sont affichées en public sur ton profil LinkedIn. &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-examples.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Pourquoi écrire une recommandation LinkedIn ?&lt;/h2&gt;
&lt;p&gt;Pour la personne qui reçoit la recommandation&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Une recommandation est un gage de confiance, de crédibilité et de réputation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cela fait toujours plaisir d'apprendre qu'on est apprécié dans notre travail&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cela aide à identifier ses forces et donc à mieux se connaître&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Pour la personne qui écrit, cela permet de&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;générer de la réciprocité&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;démontrer que vous êtes enclin à aider votre entourage&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;conscientiser les qualités de l'autre : certaines collaborations sont parfaites sur toute la ligne, d'autres sont plus chaotiques. Quoiqu'il en soit, cet exercice aide à réaliser ce que l'on a apprécié chez l'autre et à relativiser les difficultés que l'on a pu rencontrer ensemble.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Pour l'analogie, c'est comme laisser un avis positif sur un lieu sur Maps : cela permet de supporter la personne et de lui donner de la crédibilité. &lt;/p&gt;
&lt;h2&gt;La difficulté&lt;/h2&gt;
&lt;p&gt;Écrire une recommandation présente de nombreux intérêts. Mais, écrire une recommandation pertinente est un exercice délicat, qui peut facilement prendre beaucoup de temps.  &lt;/p&gt;
&lt;p&gt;Au cours des 10 dernières années j'en ai écris deux. &lt;/p&gt;
&lt;p&gt;C'est peu.&lt;/p&gt;
&lt;p&gt;Pourtant, Je pense qu'elle sont plutôt réussies. Les personnes pour qui je les ai écrites en étaient très contentes, et cela m'a fait plaisir qu'elles soient touchées. Cependant, chacune m'a demandé plusieurs jours de maturation et j'ai procédé à plusieurs essais avant de trouver la forme qui me convenait. À chaque fois, cela m'a demandé beaucoup d'énergie. &lt;/p&gt;
&lt;p&gt;C'est là que ChatGPT entre en jeu !&lt;/p&gt;
&lt;h2&gt;Le concept &lt;/h2&gt;
&lt;p&gt;Proposer un outil facilite la génération de recommandation LinkedIn&lt;/p&gt;
&lt;h1&gt;Exécution&lt;/h1&gt;
&lt;h2&gt;Quelques recherches en amont&lt;/h2&gt;
&lt;h3&gt;Comment écrire une bonne recommandation LinkedIn ?&lt;/h3&gt;
&lt;p&gt;En recoupant plusieurs articles sur le web et en ajoutant mon expérience personnelle, je dirais qu'une bonne recommandation LinkedIn : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;est écrite par une personne légitime : ancien collègue, clients, mentor, ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;met en avant les talents de la personne en lien avec ses aspirations professionnelles&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;est authentique : élogieuse sans tomber dans l'excès&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;précise : &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;le contexte de la relation : la durée, le lieu, la nature de la relation&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;les résultats obtenus ou les contributions apportées par la personne à l'équipe&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;contient une anecdote mettant en avant les soft skills de la personne&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;conclut sur le poste, les missions ou les besoins pour lesquels vous recommandez la personne&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Existe-t-il déjà des générateurs ? &lt;/h3&gt;
&lt;p&gt;En écumant le web, j'ai pu trouver les générateurs de recommandation LinkedIn suivants : &lt;/p&gt;
&lt;p&gt;- &lt;a href="https://generiateur.swile.co/"&gt;Gener-IA-teur de Swile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://ai.socialrecommendator.com/"&gt;Social Recommendator&lt;/a&gt; : le générateur indie&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://www.linkedjetpack.com/linkedin-recommendation-generator/"&gt;LinkedJetPack&lt;/a&gt; : alambiqué&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://easy-peasy.ai/fr/templates/linkedin-recommendation-generator"&gt;EasyPeasy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://vanling.net/LinkedIn.htm"&gt;Vanling&lt;/a&gt; : le générateur old school&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://logicballs.com/tools/linkedin-recommendation-generator"&gt;LogicBalls&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- &lt;a href="https://lazyapply.com/linkedin-recommendation-generator"&gt;LazyApply&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;J'en ai tiré plusieurs observations : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;il y a une demande pour de tels générateurs : vanling, le plus cheap de tous, a déjà généré plus de 100k recommandations&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;il est possible de faire mieux. Les produits disponibles sont souvent superficiels. Certains sont diablement simple d'utilisation. Ils permettent de générer une recommandation en 3 clic. Mais ils recueillent trop peu de context. Ils produisent ainsi des recommendations génériques qui sonnent creux et manquent d'authenticité.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ces générateurs sont tous gratuits : il est peu probable qu'un utilisateur soit enclin à payer pour utiliser un tel outil&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Construire le produit&lt;/h2&gt;
&lt;p&gt;Mon objectif est d'aider les personnes à générer une recommandation LinkedIn. &lt;/p&gt;
&lt;p&gt;La version la plus élémentaire de cet outil serait de partager le prompt que j'ai conçu. L'avantage est que cela permet de lancer très rapidement et de toucher un premier public. L'inconvénient, c'est qu'une fois le prompt partagé, il m'est difficile de suivre sa vie et de collecter du feedback utilisateur. &lt;/p&gt;
&lt;p&gt;Une approche possible serait d'utiliser des outils NoCode ou de programmation visuelle comme Retool, Noodl ou encore Wized.&lt;/p&gt;
&lt;p&gt;Ce projet étant aussi un prétexte pour que je déploie une application web, j'ai décidé d'aller directement vers un outil plus abouti et de créer un site dédié à cet unique fonctionnalité.&lt;/p&gt;
&lt;p&gt;Le service que j'ai en tête présente la forme suivante : &lt;/p&gt;
&lt;p&gt;1. Poser des questions à la personne &lt;/p&gt;
&lt;p&gt;2. Construire un prompt à partir des réponses &lt;/p&gt;
&lt;p&gt;3. Générer une recommandation avec GPT&lt;/p&gt;
&lt;p&gt;4. Présenter la recommandation à l'utilisateur&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Choisir la stack&lt;/h3&gt;
&lt;p&gt;De quoi ai-je besoin ? &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;un front-end pour collecter les informations puis afficher les résultats&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;une partie backend pour effectuer des appels à l'API d'OpenAI&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;un système de persistence des données pour analyser les usages&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;une solution d'hébergement&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Bon, quelles sont les options ? &lt;/p&gt;
&lt;p&gt;Il existe une quantité étourdissante de langages front, back, de frameworks, de solution d'hébergement et de combinaison de tout cela. Pour simplifier ma décision, je vais me concentrer sur les langages JavaScript, Python et .NET que je connais déjà bien. Encore une fois, une bonne façon de procéder ici est de commencer par la fin : où est-ce qu'on va héberger tout ça ?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;L'hébergement&lt;/h4&gt;
&lt;p&gt;Du plus bas niveau au plus haut, on en gros trouver les approches suivantes : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Configurer son propre serveur et le faire tourner dans sa chambre, back to basics ! &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Louer un serveur virtuel privé et configurer sois-même le serveur : on bascule dans le cloud computing. On s'épargne un ronfleur, mais on est encore sur quelque choses de très bas niveau. Il faut configurer Apache HTTP Server, gérer les sécurité, ...&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Infrastructure as a Service (IaaS) : Encore trop complexe pour mon besoin, je ne veux pas passer ma semaine à configurer la couche infrastructure. Je n'ai jamais testé AWS et ai eu une expérience laborieuse d'Azure. Je préfère garder ça pour une prochaine fois. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Platform as a Service (Paas) : les PaaS sont moins flexibles que les IaaS, mais permettent de se concentrer sur l'application et de démarrer rapidement, ce qui est idéal pour un minimum viable product (MVP).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Parmis les PaaS, les 4 solutions suivantes me paraissent intéressantes : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt;  : existe depuis 2011, Firebase est une solution propriétaire développée par Google. FireBase regroupe un ensemble de composants modulaires qui peuvent être facilement reliés entre eux pour construire une solution complète : base de donnée NoSQL (Cloud FireStore), service d'authentification (Authentication), création de points d'api avec des [[serverless functions - Function as a Service (FaaS)]] (Cloud Functions).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; : existe depuis 2020, Supabase est une solutions open source qui se positionne directement comme une alternative à Firebase. La société compte dans ses investisseurs des partenaires prestigieux comme le Y Combinator et Mozilla. Elle offre des fonctionnalités similaires à Firebase.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt; : existe depuis 2015. C’est la société qui est derrière le framework React Next.js. Bien qu'initialement centré sur le déploiement du front-end, Vercel offre auhourd'hui une solution quasi-complète avec des solutions de stockage (NoSQL, SQL et Blob) et des fonctions serverless. La plateforme n'offre par contre pas de solution d'authentification clé en main à ce stade.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Netlify&lt;/strong&gt; : existe depuis 2014. Couplé à FaunaDB, la plateforme offre elle aussi une solution d'hébergement complète.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Partons sur Firebase.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Langage et frameworks&lt;/h4&gt;
&lt;p&gt;Le fait de partir sur du "pur" Firebase va fortement orienter la suite. &lt;/p&gt;
&lt;p&gt;Firebase a récemment introduit des templates dédiés à certains framework front-end. Cela facilite par exemple le setup d'applications basés sur Angular ou Next.js (React). J'ai déjà une expérience positive de React. Go pour ce framework.&lt;/p&gt;
&lt;p&gt;De la même façon, Cloud Functions supporte trois langages : Javascript, Python ou TypeScript (Beta). J'ai d'abord pensé à utiliser TypeScript pour bénéficier du typage, mais la documentation pour TypeScript est bien moins détaillée. Finalement, je suis parti sur Python. &lt;/p&gt;
&lt;p&gt;Il y a plusieurs intérêts à utiliser python pour le backend :&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Cela créer une séparation claire entre le front et le back : en fonction du langage, on sait où on est !&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;L'écosystème python est juste dingue en terme de bibliothèques. Que ce soit pour faire du traitement d'image, de l'analyse de données, du machine learning, il est possible de tout faire.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Contrairement à TypeScript, il n'est pas nécessaire de transpiler python (ce qui veut dire moins de configuration à gérer)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;j'adore python&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;OK, on a notre stack ! &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;Hébergement du front-end : Firebase Hosting&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Authentification : Firebase Auth&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Framework front-end : [[Next.js]] (React/TypeScript)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Backend : Cloud Function for Firebase (Python)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Persistence des données : Cloud Firestore&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;Développer &lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Je ne vas pas entrer dans le détail pas à pas de comment créer une application avec Firebase — mais si c'est quelque chose qui t'intéresse, fais le moi savoir !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Un truc amusant à raconter ici c'est que, si, après coup, la fonction et le périmètre de chaque service me parait clair, j'étais dans le brouillard en abordant les services de Firebase. La première approche que j'ai eu a été d'avancer pas à pas, de faire avec les zones de flou et de suivre la documentation. &lt;/p&gt;
&lt;p&gt;Il y a de nombreuses façons de monter une app. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Premier jet&lt;/h4&gt;
&lt;p&gt;En première approche, je suis parti de `npm create-next-app` et ai intégré Firebase à posteriori. J'ai utilisé TypeScript pour le backend et ai setup des tests automatisés avec jest ainsi qu'un système de formattage automatique avec Prettier. &lt;/p&gt;
&lt;p&gt;Je me suis retrouvé avec une véritable usine à gaz. &lt;/p&gt;
&lt;p&gt;L'organisation imbriquée du code source était source de confusion. Avec la transpilation, je n'étais pas bien sûr de quel code était exécuté où et à quel moment. Aussi, je rencontrais des problèmes avec la version déployée que je ne parvenais pas à comprendre.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-tree-chaos.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Remise au propre et simplification&lt;/h4&gt;
&lt;p&gt;J'ai finalement remis de l'ordre en recréant l'architecture du projet de zéro. Cette fois-ci, j'ai utilisé uniquement les client firebase. Aussi, j'ai décidé de ne pas mettre en place de tests automatisés et ni de formatage automatique du code à ce stade.&lt;/p&gt;
&lt;p&gt;Pour se faire, rien de plus simple. &lt;/p&gt;
&lt;p&gt;1. Installer &lt;a href="https://www.nodejs.org/"&gt;Node.js&lt;/a&gt; en utilisant &lt;a href="https://github.com/creationix/nvm/blob/master/README.md"&gt;nvm&lt;/a&gt; (Node Version Manager)&lt;/p&gt;
&lt;p&gt;2. Installer le client Firebase à l'aide de npm (gestionnaire de paquets installé avec Node.js)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   npm install -g firebase-tools&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3. Connecter sa machine à Firebase&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   firebase login&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4. Créer un dossier pour le projet et se placer à la racine du projet&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir writing-a-linkedin-reco
cd writing-a-linkedin-reco &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5. Utiliser le client Firebase pour créer l'architecture du projet et se laisser guider &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   firebase init hosting
   firebase init functions&lt;/code&gt;&lt;/pre&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-tree-ok.jpeg"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;La question du formatage&lt;/h4&gt;
&lt;p&gt;Les outils de formatage automatique sont précieux lorsque la codebase est maintenue par de nombreuses personnes. Ils permettent d'uniformiser le style et donc de réduire la charge cognitive qu'implique de lire le code existant. Dans notre cas, je suis seul, il y a peu de code, et mon style est cohérent avec moi même. J'ai donc jugé qu'un formatter n'était pas nécessaire. &lt;/p&gt;
&lt;p&gt;De plus, pour que ce type d'outil soit vraiment à ton service (et non l'inverse ...) il est indispensable d'aller au bout de la configuration. Configurer un hook de pre-commit par exemple permet de lancer le formatage de façon automatique avant chaque commit, ce qui assure que l'outil est exécuté à intervalle régulière sans que l'on ai à y penser. Lorsque l'outil est installé mais configuré à moitié, il représente finalement davantage une charge mentale puisqu'il faut penser à le lancer manuellement. &lt;/p&gt;
&lt;h3&gt;Designer le produit&lt;/h3&gt;
&lt;p&gt;Afin de designer l'application, j'ai utilisé les techniques suivantes : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;m'inspirer du design des autres compétiteurs : ce qui marchait bien, ce qui ne me plaisait pas.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;lire le livre &lt;a href="https://www.refactoringui.com/"&gt;Refactoring UI&lt;/a&gt; et appliquer pas à pas les conseils données dans le livre.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;utiliser le générateur de &lt;a href="https://www.framer.com/"&gt;Framer&lt;/a&gt; avec comme instruction "a linkedin recommendation generator app" pour avoir une proposition de design&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;Diffuser le produit&lt;/h2&gt;
&lt;p&gt;Les pistes de diffusions identifiées : &lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;publier 50 recommandations à l'aide de l'outil et faire un poste LinkedIn à ce propos&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;proposer l'outil à mon entourage&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;commenter des posts qui listent les outils indispensable pour LinkedIn&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;faire une vidéo Youtube qui présente l'outil&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;Exact match domain&lt;/h3&gt;
&lt;p&gt;En SEO, on parle d'"exact match domain" lorsque l'adresse de site web contient les mots clés des requêtes ciblées. Un vecteur sur lequel j'ai parié est qu'en choisissant le nom de domaine `ecrire-une-recommandation-linkedin.fr`, le site ressortirait naturellement des recherches google "Comment écrire une recommandation LinkedIn".&lt;/p&gt;
&lt;p&gt;A posteriori, il s'avère cependant que "écrire une recommandation linkedin" correspond à un volume de recherche extrêmement faible : &lt;/p&gt;
&lt;div class="captioned-image-container"&gt;&lt;img src="_assets/recommandations-keyword-research.png"/&gt;&lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Recherche de mots clés surSemrush- les volumes sont faibles !&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;🥴&lt;/p&gt;
&lt;h1&gt;Bilan&lt;/h1&gt;
&lt;h2&gt;Leçons&lt;/h2&gt;
&lt;h3&gt;Mobile first ! &lt;/h3&gt;
&lt;p&gt;J'ai développé l'application sur desktop. Lorsque je demandais à mon entourage de tester pour faire des retours, il se produisait systématiquement la même chose : ils testaient sur mobile et des bugs d'affichages se présentaient. &lt;/p&gt;
&lt;p&gt;De fait, en France, &lt;a href="https://www.similarweb.com/fr/platforms/france/"&gt;65% du trafic provient des mobiles&lt;/a&gt;. Il semble donc logique d'optimiser l'affichage en premier lieu pour les mobiles. &lt;/p&gt;
&lt;p&gt;Par ailleurs, il est plus simple d'adapter un design mobile vers un design desktop que l'inverse. &lt;/p&gt;
&lt;p&gt;Donc, mobile first !&lt;/p&gt;
&lt;h3&gt;Just in time&lt;/h3&gt;
&lt;p&gt;Je réalise à quel points certains outils qui me paraissent indispensable lorsque l'on travaille en équipe ne le sont pas lorsque l'on travaille seul et sur un prototype. Ici, ma priorité est de délivrer le plus rapidement possible. Mettre en place proprement des outils de formatage automatique et de test automatisé demande un investissement que je n'ai pas trouvé nécessaire ici.&lt;/p&gt;
&lt;h3&gt;Side-project vs side-business&lt;/h3&gt;
&lt;p&gt;Je suis allé trop loin dans le développement du produit et dans le design de l'interface utilisateur pour un minimum viable product (MVP). Le fait de mélanger un objectif de création de valeur et de génération de revenus (business) avec un objectif d’apprentissage (project) me conduit à prendre des décisions qui n’ont pas de sens d’un point de vue économique. &lt;/p&gt;
&lt;p&gt;D’un point de vue technique, j’ai pu mettre en pratique et apprendre. Mais d’un point de vue business, j’ai gâché énormément de ressources car j’aurais pu tester mes hypothèses plus rapidement. &lt;/p&gt;
&lt;p&gt;Pour les prochains produits, je serai davantage focus sur la démarche économique.&lt;/p&gt;
&lt;h2&gt;Monétisation&lt;/h2&gt;
&lt;p&gt;Je ne pense pas que les gens soient prêt à payer pour accéder à ce service seul. Les coûts d'exploitation sont cependant relativement faible. S'il y a un product market fit, la piste du sponsoring me paraît la plus prometteuse. &lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Les premiers designs de prompt étaient catastrophiques, j'ai hésité un moment à laisser tomber. Mais je suis content du résultat final. &lt;/p&gt;
&lt;p&gt;Écrire une recommandation permet de réaliser ce qu'il y a de bon chez l'autre. C'est comme une lettre d'amour. Alors écrivez des recommandations de vos anciens collègues, le liens est 👉 &lt;a href="https://ecrire-une-recommandation-linkedin.fr/"&gt;ici&lt;/a&gt; 👈 !&lt;/p&gt;
&lt;p&gt;Merci de m’avoir lu jusqu’ici et à la prochaine !&lt;/p&gt;
&lt;p&gt;🙂&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Alexandre&lt;/p&gt;</content></entry><entry><title>Créer une version des Fables de La Fontaine avec Midjourney</title><link href="https://me.petitapetit.io/lafontaine" rel="alternate"/><published>2023-06-01T00:00:00+02:00</published><updated>2023-06-01T00:00:00+02:00</updated><author><name>Alexandre Petit</name></author><id>tag:me.petitapetit.io,2023-06-01:/lafontaine</id><summary type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Bienvenue dans &lt;em&gt;100 produits&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ma démarche est donner vie à des projets en quelques
  journées et de documenter leur réalisation. Plutôt que de travailler sur LE projet parfait, je vais multiplier les
  expérimentations, explorer les façons de trouver des solutions, mettre les mains dans le cambouis, trouver des
  hacks …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hello ! &lt;/p&gt;
&lt;p&gt;Bienvenue dans &lt;em&gt;100 produits&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ma démarche est donner vie à des projets en quelques
  journées et de documenter leur réalisation. Plutôt que de travailler sur LE projet parfait, je vais multiplier les
  expérimentations, explorer les façons de trouver des solutions, mettre les mains dans le cambouis, trouver des
  hacks, aller droit au but, copier des idées de business déjà existantes, lancer rapidement, itérer, améliorer, et
  répéter l’expérience jusqu'à ce qu'elle soit couronnée de succès.
&lt;/p&gt;
&lt;p&gt;Cette newsletter sera comme un journal de
  bord et je publierai une rétrospective par projet. &lt;br/&gt;&lt;br/&gt;C’est parti pour la première !
&lt;/p&gt;
&lt;h2&gt;Une version
  illustrée des fables de La Fontaine
&lt;/h2&gt;
&lt;p&gt;J’ai redécouvert les Fables de La Fontaine il y a deux ans, dans &lt;a
  href="https://podcast.ausha.co/artisan-developpeur/le-loup-et-le-chien"&gt;un épisode du podcast Artisan
  Développeur &lt;/a&gt;de Benoit Gantaume. Depuis, j’ai souhaité les relire. J’ai acheté deux éditions, mais aucune ne m’a
  vraiment plus :
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;La première est une version de poche. La mise en page est agréable, mais il n’y a pas d’images et je trouve
      que cela manque à l’expérience de lecture.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;La deuxième est une intégrale illustrée, en quatre tomes : les images sont belles, mais la mise en page n’est
      pas agréable. Aussi, toutes les fables ne se valent pas. Parmi les 243, certaines sont plus universelles que
      d’autres. Or, je ne recherche pas une encyclopédie sur La Fontaine. Juste des histoires avec des leçons de vie
      que je peux méditer dans mon quotidien. Le 20% qui apportent 80% de la valeur.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avec la sortie de ChatGPT et la version 4 de Midjourney fin 2022, on a vu de nombreuses personnes créer des
  livres en combinant ces deux technologies et les mettre en vente sur Amazon, “juste en cliquant sur un bouton” !
  Cela m’a donné envie d’expérimenter.&lt;br/&gt;&lt;br/&gt;Enfin, je cherchais un prétexte pour me documenter davantage sur
  ChatGPT et Midjourney. Les capacités esthétiques de Midjourney sont bluffantes. Et, quelque part, j’ai pensé que
  peut-être une édition actualisée des Fables, avec des images époustouflantes, donnerait peut-être envie à un public
  de 20-30 ans de redécouvrir ces textes.&lt;br/&gt;
&lt;/p&gt;
&lt;p&gt;Je me suis alors lancé dans ce projet avec en tête :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;d’automatiser un maximum le processus &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;d’aller au bout quoi qu’il arrive&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;de chercher en premier lieu un produit fini plutôt qu’un produit parfait&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;d’y consacrer 5 jours maximum&lt;br/&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Évidemment, tout ne s’est pas passé comme prévu.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Le PDF du résultat final est visible &lt;a
    href="https://www.dropbox.com/scl/fi/i9rb8ggmcalt74805dqw5/Les-Fables-de-La-Fontaine-illustr-es-avec-Midjourney-Alexandre-Petit.pdf?rlkey=6dnidl571o887rv8inexo2yub&amp;amp;dl=0"&gt;ici&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Vue d’ensemble&lt;/h3&gt;
&lt;p&gt;Traditionnellement, publier un livre c’est : &lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Écrire un manuscrit&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Trouver une maison d’édition&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Travailler avec des illustrateurs et des designers pour mettre en page l’ouvrage&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Missionner une équipe marketing pour la diffusion de l’ouvrage&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Contacter des imprimeurs et des relieurs pour la fabrication de l’ouvrage&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Conclure avec un distributeur pour acheminer le livre en librairie&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;C’est un processus qui mobilise un grand nombre d’acteurs et présente deux barrières majeurs. Premièrement, il y
  a la barrière de la sélection : peu de personnes passent le cap de l’étape 2. Ensuite, si vous signez un contrat à
  compte d’auteur, il reste une barrière financière puisque c’est à vous d’avancer les frais de conception et de
  fabrication.
&lt;/p&gt;
&lt;p&gt;Ce qui est dingue avec les solutions d’auto-édition et l’émergence de l’impression à la demande,
  c’est qu’il est possible de gérer soi-même une grande partie des étapes, de publier un ouvrage sans permission, et
  sans avoir à avancer de frais.
&lt;/p&gt;
&lt;p&gt;Dans ce projet, l’étape 1 est déjà bien prémâchée puisque je vais me baser des
  textes déjà existants. L’étape 2 se change en “choisir un service d’auto édition”. Le service d’auto-édition va
  prendre en charge les étapes 5 et 6. Il me reste donc le troisième point (la mise en page), et le quatrième point
  (la diffusion).
&lt;/p&gt;
&lt;p&gt;[Faire un schéma classique vs pour le projet avec des photos de moi avec un pouce]&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Choisir une solution d’auto-édition&lt;/h2&gt;
&lt;p&gt;Il existe des dizaines de solutions d’auto-édition : Lulu Xpress, Blurb,
  Amazon Kindle Direct Publishing (KDP), Coollibri, Ingram, The Book Edition, Librinova, Iggybook, Publishroom,
  Bookelis, …
&lt;/p&gt;
&lt;p&gt;Parmi les plus importantes : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://kdp.amazon.com/fr_FR/"&gt;Amazon KDP&lt;/a&gt; est la solution d’auto-édition d’Amazon. Elle permet
      de publier de eBooks aussi bien que des livres papier (reliés ou brochés) et est adaptée à tous types de contenu
      (romans, livres pour enfant, bandes déssinées, …). Les livres publiés sur Amazon KDP ne sont disponibles que sur
      Amazon. &lt;a href="https://kdp.amazon.com/fr_FR/help/topic/G200634280"&gt;L’espace d’aide&lt;/a&gt; est complet. Dans le
      cas des eBooks, il est nécessaire de publier le livre au format KPF.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://www.lulu.com/"&gt;Lulu Xpress&lt;/a&gt; a été fondée en 2002 par l’ancien CEO de RedHat. Comme
      Amazon, elle permet de publier de eBooks aussi bien que des livres papier. Le design du site exprime érgonomie
      et simplicité. La platforme met à disposition des templates Word et InDesign avec des presets d’export. Le
      service gère lui même l’obtention de l’ISBN. L’option “global distribution” permet de distribuer son livre
      papier sur Amazon et le rends disponible en commande dans “toutes les bonnes librairies” via le réseau du groupe
      Hachette.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sans trop hésiter, je suis parti sur Lulu.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Le processus éditorial&lt;/h2&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-process.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Visualisation schématique du processus
    éditorial
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Récupérer les textes&lt;/h3&gt;
&lt;p&gt;Pour aller plus vite, j’ai demandé à ChatGPT de lister
  les 50 fables de La Fontaine les plus populaires. Ces textes sont depuis longtemps dans le domaine public et on les
  retrouve facilement sur internet. Beaucoup sont sur Wikipédia. Souvent, je suis allé les récupérer à la main sur ce
  site &lt;a href="http://www.mesfables.com/livre-1/01-la-cigale-et-la-fourmi.html"&gt;mes-fables.com&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Amélioration
  possible de cette étape : automatiser le processus avec un script navigateur.&lt;/em&gt;
&lt;/p&gt;
&lt;h3&gt;Sélectionner&lt;/h3&gt;
&lt;p&gt;Pour
  cette étape, j’ai lu l’ensemble des textes récupéré et ai utilisé deux critères de sélection.
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Est-ce que le texte déclenche une émotion chez moi ? est-ce que son message est toujours d’actualité ?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Est-ce que c’est un “à connaitre” ? est-ce que c’est une fable dont les &lt;a
      href="https://fr.wikipedia.org/wiki/Fables_de_La_Fontaine#Vers_et_moralit%C3%A9s_les_plus_souvent_cit%C3%A9es"&gt;vers
      et la moralité sont les plus fréquemment citées&lt;/a&gt; ?
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Amélioration possible : appliquer ce processus aux 249 fables plutôt qu’à une pré-selection &lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  Choisir l’ordre d’apparition des textes
&lt;/h3&gt;
&lt;p&gt;Au fil de la lecture, j’ai tagué chaque fable avec des émojis. &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;⭐️ (incontournable)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;❤️ (j’adore)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;😂 (lol)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;😵‍💫 (pas compris)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;👍 (ok)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-ranking.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Annotations des fables dans une feuille de
    calcul
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Mon premier critère de tri a été de mettre en premier les fables ⭐️ et ❤️. &lt;/p&gt;
&lt;p&gt;Le deuxième
  a été de mettre les plus courtes en premier, pour offrir des récompenses rapides au lecteur et lui donner envie
  d’aller plus loin dans la lecture.
&lt;/p&gt;
&lt;p&gt;On peut voir cela comme un deuxième niveau d’application du &lt;a
  href="https://fr.wikipedia.org/wiki/Principe_de_Pareto"&gt;principe de Pareto&lt;/a&gt;. Le livre ambitionne de regrouper
  les 20% des fables qui apportent 80% de la “sagesse”. Les premiers 20% du livre tentent d’apporter eu aussi un
  maximum de valeur.
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Amélioration possible : avoir une évaluation plus fine que les trois cas (rien), &lt;/em&gt;👍,
  ❤️. &lt;br/&gt;
&lt;/p&gt;
&lt;h2&gt;Créer les illustrations&lt;/h2&gt;
&lt;p&gt;Une fois la solution d’auto-édition choisie, le gros du projet
  consiste à générer les illustrations.
&lt;/p&gt;
&lt;p&gt;Pour aller au plus simple, j’ai d’abord testé de donner directement le
  texte complet et en français de la fable à Midjourney, mais les résultats n’étaient pas brillants.
&lt;/p&gt;
&lt;p&gt;En
  itérant, je suis finalement arrivé au processus suivant :
&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-automation.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Vue globale du processus de génération des
    illustrations
  &lt;/p&gt;
&lt;/div&gt;
&lt;h3&gt;Traduire en anglais&lt;/h3&gt;
&lt;p&gt;GPT comprend le français mais saisi mieux l’anglais. Afin
  de l’aider, j’ai rédigé un script qui utilise l’API de &lt;a href="https://www.deepl.com/translator"&gt;DeepL&lt;/a&gt; pour
  traduire chaque fable. À ce stade que j’ai abandonné ma feuille de calcul pour basculer sur &lt;a
    href="https://airtable.com/"&gt;Airtable&lt;/a&gt; afin de simplifier l’accès aux informations dans les scripts.&lt;br/&gt;
&lt;/p&gt;
&lt;p&gt;Cette étape génère la colonne “Traduction” à partir de la colonne “Texte”.&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-table.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Aperçu de la table Airtable où les données
    relatives à chaques fables sont stockées
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Amélioration possible : mesurer la qualité des
  descriptions visuelles générées par ChatGPT avec ou sans prompt.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Décrire une scène avec
  ChatGPT
&lt;/h3&gt;
&lt;p&gt;Là, on arrive sur les parties épineuses. &lt;/p&gt;
&lt;p&gt;J’ai testé des dizaines de prompts pour demander à
  ChatGPT de décrire une fable en une image. Celui que j’ai retenu est le suivant.
&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;You are an experienced illustrator. The goal is to create a prompt for a text to image generator. I will
    give you a tale. I want to depict the most representative scene of the tale. Answer using short sentences. Use less
    than 30 words. Focus on the subjects, the environment and the major event of the tale. &lt;br/&gt;&lt;br/&gt;{la fable traduite
    en anglais}
  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Le prompt peut être analysé de la façon suivante : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;You are an experienced illustrator&lt;/em&gt; : le rôle à jouer&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;The goal is to create a prompt for a text to image generator&lt;/em&gt; : objectif&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I &lt;em&gt;want to depict the most representative scene of the tale&lt;/em&gt; : dans les premières versions, ChatGPT
      avait tendance à résumer l’histoire et décrire plusieurs scènes. Évidemment, cela n’aide pas Midjourney à créer
      des images cohérentes. Ici, je lui demande de se concentrer sur une seule scène.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Answer using short sentences. Use less than 30 words&lt;/em&gt; : Les prompts ont tendance à être longs. Or,
      lorsque la description est faite de phrases à rallonge, Midjourney part facilement en vrille sur
      l’interprétation. Je préfère lui donner peu d’informations mais qu’il colle au prompt plutôt que de lui donner
      trop d’informations et qu’il ne tienne compte que de la moitié.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Focus on the subjects, the environment and the major event of the tale&lt;/em&gt; : donner à Midjourney des
      indications sur les principaux éléments à représenter. Le décor, les personnages, l’action.
    &lt;/p&gt;
    &lt;p&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Prendre en main Midjourney et calibrer le prompt&lt;/h3&gt;
&lt;p&gt;Midjourney est un programme qui permet de générer des
  images à partir de descriptions textuelles. Il se distingue des autres logiciels du même genre tels que
  StableDiffusion et Dall-E par sa tendance naturelle à générer des images esthétiques, ce qui est bien adapté à la
  création d’un livre illustré.
&lt;/p&gt;
&lt;p&gt;En plus de la description visuelle, il est possible d’utiliser des paramètres
  pour guider la génération.
&lt;/p&gt;
&lt;p&gt;Afin de me familiariser avec Midjourney, j’ai sélectionné trois fables (le chêne
  et le roseau, le rat des villes et le rat des champs, la cigale et la fourmi) et ai testé différentes variations de
  :
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Style : &lt;em&gt;“cyanotype”, “contemporary illustration, bold colors, dynamic composition”, “&lt;a
      href="https://cdn.discordapp.com/attachments/1089155086925705336/1089806385262370906/lx_An_oak_tree_and_reed_stand_on_a_windy_riverbank_tension_buil_142c4379-0766-48a0-93ec-91ef17e42df2.png"&gt;minimalist
      neo pop style&lt;/a&gt;”, “&lt;a
        href="https://cdn.discordapp.com/attachments/1089155086925705336/1089639875566903416/lx_An_oak_tree_and_reed_stand_on_a_windy_riverbank_tension_buil_9b23be8c-601e-4fcb-8d2c-3c0691a05d04.png"&gt;moebius&lt;/a&gt;”,
      “&lt;a href="https://cdn.discordapp.com/attachments/1089155086925705336/1089641314909442058/lx_An_oak_tree_and_reed_stand_on_a_windy_riverbank_tension_buil_a690ec99-5875-4115-bdaa-13e2154a200d.png"&gt;naive
      art&lt;/a&gt;”, “&lt;a
        href="https://cdn.discordapp.com/attachments/1089155086925705336/1089797825522585630/lx_An_oak_tree_and_reed_stand_on_a_windy_riverbank_tension_buil_95e8dee5-f391-4c4d-b13c-731be9218506.png"&gt;op
      art&lt;/a&gt;”, “&lt;a
        href="https://cdn.discordapp.com/attachments/1089155086925705336/1089799181973078046/lx_An_oak_tree_and_reed_stand_on_a_windy_riverbank_tension_buil_328d7f57-69a1-49b4-bd9c-c6f57215819b.png"&gt;zentangle&lt;/a&gt;”,
      “iridescent”, “colorful geometric”, “Airbrush surrealism”, “Contrast of resilience, watercolor style with
      expressive brushstrokes”, “Y2K grunge grimy textures, chrome metallics, broken grids and amorphous shapes”,
      “Vintage computer graphics”…&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Description&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;chaos : 0, 1000&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;stylize : 0, 100, 1000&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;graine aléatoire (seed)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-tree-tests.jpeg"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Test de différents styles graphiques dans
    Midjourney
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Rapidement, plusieurs problèmes/limitations sont apparues : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Explorer au hasard et à la main un problème à cinq dimensions n’est pas efficace. Même après y avoir passé
      plusieurs heures, il reste difficile de tirer des conclusions sur “est-ce que je vais dans la bonne direction
      avec ce jeu de paramètres”
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Lorsque le prompt décrit plusieurs animaux, le moteur tends à mélanger les deux. Par exemple, le prompt
      &lt;em&gt;&lt;a href="https://cdn.discordapp.com/attachments/1089155086925705336/1090970693698592879/lx_a_dog_and_a_rabbit_in_a_landscape_playing_hapiness_colorfull_0606586f-1bb7-46be-8d26-5e9a03b54b5d.png"&gt;“a
      dog and a rabbit in a landscape playing hapiness colorfull”&lt;/a&gt; &lt;/em&gt;génère un lapin avec une langue de
      chien, et un chien avec des oreilles de lapin…
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Midjourney a du mal à représenter certains animaux. Lorsque l’on demande une pie, l’animal qu’il dessine
      présente parfois davantage les traits d’un corbeau. Lorsque l’on demande une belette, il esquisse une
      souris…
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Le moteur tends à prendre ses liberté par rapport aux instructions. Il va globalement coller au prompt, mais
      n’intègre pas toujours tous les éléments.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Le images générées tendent à présenter de nombreuses inconsistences : un troisième bras, une jambe en moins,
      …
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Amélioration possible : &lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;commencer par 1) explorer de façon libre et sur un temps délimité pour explorer les limites et les
      paramètres importants puis 2) explorer de façon méthodique&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;cette étape n’est pas nécessaire à la mise en place du walking skeleton : pour commencer, il serait tout
      à fait possible de générer des images sans préciser le style et avec les paramètres par défaut puis de revenir
      plus tard à cette étape pour l’améliorer&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;jouer avec le paramètre “quality” (qui n’influe pas sur la qualité de l’image comme je l’ai d’abord
      interprêté mais sur la “qualité du dessin”).&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Automatiser la génération des images&lt;/h3&gt;
&lt;p&gt;L’un des objectifs de ce projet est d’automatiser au maximum
  chaque étape. Générer de longues séries d’images avec Midjourney est particulièrement fastidieux puisque pour chaque
  image, cela consiste à :
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Entrer le prompt&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Générer une grille de quatre propositions (~ 1min)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Agrandir une image (10s) ou relancer une grille de propositions (retour à l’étape 2)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Enregistrer l’image&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;L’automatisation de cette étape présente ainsi deux intérêts : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Mettre à jour l’édition avec une nouvelle version de Midjourney devient simple. Il suffit de relancer le
      script de génération pour chaque fable.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Explorer de façon méthodique un grand nombre de styles, de paramètres et de descriptions&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Malheureusement, il n’existe pas d’API officielle de Midjourney, et l’automatisation va à l’encontre de
  leurs &lt;a href="https://docs.midjourney.com/docs/terms-of-service"&gt;conditions d’utilisation&lt;/a&gt;. Cependant, il existe
  tout de même plusieurs façons d’automatiser le processus.
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Utiliser une api non officielles : &lt;a href="https://www.theinsaneapp.com/2023/05/midjourney-api.html"&gt;cet
      article&lt;/a&gt; en référence plusieurs
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Créer son propre bot Discord pour servir de proxy : il est ainsi possible d’interagir avec via son bot, qui
      lui interagit alors avec le bot de Midjourney.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Automatiser la souris et le clavier avec des outils comme &lt;a
      href="https://pyautogui.readthedocs.io/en/latest/#"&gt;PyAutoGUI&lt;/a&gt; ou son navigateur avec des outils comme &lt;a
      href="https://pptr.dev/"&gt;Puppeteer&lt;/a&gt;, &lt;a href="https://www.selenium.dev/"&gt;Selenium&lt;/a&gt; ou &lt;a
      href="https://playwright.dev/python/"&gt;Playwright&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Option 1 : Utiliser une api non officielles&lt;/h4&gt;
&lt;p&gt;Les API non officielles sont payantes. Or, j’ai déjà
  un abonnement à Midjourney et ne veux pas payer en double. Aussi, étant dans une démarche de construction, j’ai
  rapidement écarté ces options et ne les ai pas creusées davantage.
&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Option 2 : Créer mon propre Bot
  Discord
&lt;/h4&gt;
&lt;p&gt;C’est l’option qui m’a d’abord parue la plus séduisante. &lt;/p&gt;
&lt;p&gt;J’ai donc exploré cette piste et ai
  été agréablement surpris de voir à quel point il est simple de créer un bot Discord. La bibliothèque python &lt;a
    href="https://github.com/Rapptz/discord.py"&gt;discord.py&lt;/a&gt; est simple à prendre en main et il est rapide de
  coder ses premières interactions.
&lt;/p&gt;
&lt;p&gt;Par contre, je n’ai pas réussi à faire communiquer mon bot avec le bot de
  Midjourney (pour être précis, je n’ai pas trouvé comment faire pour envoyer une “slash command” au bot de midjourney
  à partir de mon bot). J’ai bloqué toute une après-midi sur la question.
&lt;/p&gt;
&lt;p&gt;En allant chercher de l’aide sur le
  serveur Discord de discord.py, j’ai vu que d’autres personnes rencontraient le même problème que moi. La réponse
  était toujours là même : &lt;a
    href="https://discord.com/channels/336642139381301249/985299059441025044/threads/1075203228255395923"&gt;“les
  bots ne peuvent pas invoquer de slash commands”&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;Essayons autre chose.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Option 3 :
  L’automatisation navigateur
&lt;/h4&gt;
&lt;p&gt;Les automatisations navigateur sont répandues dans le monde du développement
  logiciel où elles servent à vérifier le bon fonctionnement de l’application du point de vue de l’utilisateur.
&lt;/p&gt;
&lt;h5&gt;Choisir une bibliothèque&lt;/h5&gt;
&lt;p&gt;En python, les deux principaux outils pour automatiser les interactions avec un
  navigateur sont &lt;a href="https://pypi.org/project/selenium/"&gt;Selenium&lt;/a&gt; et &lt;a
    href="https://playwright.dev/python/"&gt;Playwright&lt;/a&gt;.
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Selenium existe depuis 2004, est open-source et bénéficie d’une large communauté d’utilisateurs. C’est un
      standard de l’industrie, à tel point que son interface WebDriver a été ajouté au recommandations de l’organisme
      de standardisation W3C en 2018 (&lt;a href="https://www.w3.org/TR/webdriver1/"&gt;ref&lt;/a&gt;).
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Playwright lui est tout jeune. Il a été publié en 2020 par Microsoft et met en avant le fait d’être conçu
      pour le web moderne. Il supporte moins de navigateurs que Selenium, mais est plus performant.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans notre contexte, les deux outils sont parfaitement adapté. J’ai donc suivi mon envie et suis parti sur
  l’option Playwright.
&lt;/p&gt;
&lt;h5&gt;Coder l’automation&lt;/h5&gt;
&lt;p&gt;Pour répondre aux besoins du projet, j’ai mis en place deux
  automations. La première génère une planche de suggestion pour chaque fable. La deuxième, décrite plus bas, agrandit
  les images sélectionnées via la méthode “upscale” fournie par Midjourney.
&lt;/p&gt;
&lt;p&gt;Les automatisations de navigateur
  procèdent de la façon suivant :
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Cibler un élément de la page&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Interagir avec l’élément ciblé&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour accélérer cette partie, j’ai me suis directement inspiré de &lt;a
  href="https://github.com/passivebot/midjourney-automation-bot/blob/main/upscale_midjourney_downloader.py"&gt;ce
  script&lt;/a&gt; en récupérant les fonctions dont j’avais besoin, au moment où j’en avais besoin.
&lt;/p&gt;
&lt;h3&gt;Le pipeline de
  sélection
&lt;/h3&gt;
&lt;p&gt;Les outils d'IA facilitent le processus de divergence et la génération d'options. Cependant, une
  fois les propositions générées, il reste encore à choisir celle que l’on veut utiliser, l’agrandir, la passer en 300
  dpi (en vue de l’impression), puis à la placer dans le dossier assets du livre afin que l’image soit chargée dans le
  projet.
&lt;/p&gt;
&lt;p&gt;Ce processus peut rapidement devenir chronophage et désordonné. Afin de l'automatiser et de le
  fluidifier, j'ai mis en place le pipeline suivant. A chaque fois, un script permet de passer d'une étape à la
  suivante et toutes les données intermédiaires sont mémorisées.
&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-pipeline.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Prennons l’exemple de la fable “Le
  Renard et les Raisins”.
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;La planche de suggestion du job &lt;code&gt;1b197be7&lt;/code&gt; est enregistrée dans le dossier &lt;code&gt;1_Grille/Le
      Renard et les Raisins/1b197be7.png&lt;/code&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Un script python parcours de façon récursive les dossiers qui sont dans &lt;code&gt;1_Grille&lt;/code&gt;, découpe
      l’image en quatre puis enregistre chaque morceau dans le dossier &lt;code&gt;2_Splits&lt;/code&gt; en conservant le dossier
      intermédiaire qui indique le nom de la fable et en ajoutant le suffixe &lt;code&gt;U1&lt;/code&gt;, &lt;code&gt;U2&lt;/code&gt;, &lt;code&gt;U3&lt;/code&gt;
      ou &lt;code&gt;U4&lt;/code&gt; au nom de l’image pour indiquer sa position dans la grille initiale.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Dans le finder, j’utilise la preview avec la touche espace pour comparer les images puis renomme ma favorite
      en la préfixant par une étoile.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Un script python récupère toutes les images qui sont préfixées, les agrandit en entrant dans Discord la
      commande Midjourney &lt;code&gt;/show 1b197be7&lt;/code&gt; puis en cliquant sur le bouton upscale correspondant au morceau.
      Par exemple, &lt;code&gt;U1&lt;/code&gt;.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Un script python récupère les images qui ont été agrandies, les passe en 300 dpi, puis les copie dans le
      dossier ressources du livre.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Amélioration possible : &lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;upscaler et passer en 300 dpi de façon systématique permettrait de réduire le nombre d’étapes et de
      simplifier le processus. Passer une image en 300 dpi est quasiment instantané et le coût de l’upscale est
      négligeable en phase d’exploration.&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Mettre en page le livre&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Choisir un outil de mise en page&lt;/h4&gt;
&lt;p&gt;Toujours en ayant en tête
  de produire une solution la plus automatisée et “mise-à-jour-able” possible, j’ai d’abord essayer de trouver un
  outil qui permette de “générer” un livre à partir de code.
&lt;/p&gt;
&lt;p&gt;Je n’ai pas trouvé de tels outils à proprement
  parler.
&lt;/p&gt;
&lt;p&gt;Les outils &lt;em&gt;Markdown to PDF&lt;/em&gt; se rapprochent de cette idée, mais derrière, les options de mise
  en page sont assez limitées ou “hacky”.
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.latex-project.org/"&gt;LaTeX&lt;/a&gt; que j’ai utilisé
  pendant mes études réponds bien à cette envie d’automatisation. Avec LaTeX, le contenu est séparé de la mise en
  page. Il est donc possible de gérer chaque chose séparémment. Seulement, produire un joli style n’est pas aisé.
  L’outil s’adresse davantage aux chercheurs et aux scientifiques qu’aux designers et aux auteurs de livres pour
  enfants.
&lt;/p&gt;
&lt;p&gt;Il vient ensuite les deux principales solutions du marché : &lt;a
  href="https://www.adobe.com/fr/products/indesign.html"&gt;InDesign&lt;/a&gt; et &lt;a
  href="https://www.canva.com/"&gt;Canva&lt;/a&gt; : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;InDesign est un logiciel de mise en page dévelopé par Adobe et publié pour la première fois en 1999. C’est un
      standard de l’industrie de la mise en page, mais aussi un produit qui est complexe à aborder de prime abord. Son
      prix est assez élevé. De façon plus personnelle, c’est un produit que j’affectionne. Cela fait longtemps que je
      ne l’ai pas utilisé, mais j’ai beaucoup utilisé par le passé (comme pour ce portfolio réalisé en 2015).
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Canva est un logiciel de conception graphique dont la première version a été publiée en 2008. Cet outil est
      né en réaction à InDesign avec la volonté de proposer un outil sans installation et simple d’utilisation. Sa
      popularité a explosée en 2019 et c’est une solution particulièrement adapté à la création de supports visuels en
      ligne.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les deux outils me paraissent adapté au problème, mais je pense qu’InDesign offre d’avantage d’options
  d’automatisation. J’espère qu’apprendre à l’utiliser va vite revenir et Lulu propose justement des templates
  InDesign pour réaliser la maquette du livre. Alors go pour InDesign.
&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;Executer la mise en page&lt;/h4&gt;
&lt;p&gt;
  Pour mettre en page le livre, j’ai procédé de la façon suivante :
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Récupérer le &lt;a href="https://www.lulu.com/publishing-toolkit"&gt;template InDesign&lt;/a&gt; proposé par Lulu&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Mettre en page une fable représentative. En analysant les textes sélectionné, on note que les fables font en
      moyenne un peu plus de 31 lignes et que cela correspond aussi à la valeur médiane : la moitié des fables
      sélectionnées font 31 lignes ou moins. Idéalement, j’aurais aimé que pour chaque fable, le texte et l’image
      tienne dans une double page, ce qui aurait grandement facilité la mise en page.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Considérer mettre de côté tous les textes qui ne tiennent pas sur une page. Cela peut faire une édition
      agréable à lire. Mais cela implique de mettre de côté certaines de mes fables intéressantes comme Le Meunier,
      son Fils et l'Âne (83 lignes). Si je veux faire un recueil des “meilleures fables”, ce n’est pas une option.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Considérer de mettre les textes sur plusieurs colonnes. Mais étant parti sur du A5, introduire deux colonnes
      nécessite de descendre la taille de police à 6 pour que les vers tiennent sur une ligne. C’est trop petit.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Aller à la Fnac pour voir et toucher la mise en page d’autres livres. &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Revenir sur InDesign&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;s&gt;Tester différentes polices&lt;/s&gt; (pas nécessaire pour le walking skeleton)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Extraire le gabarit &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Dupliquer la double page, dupliquer, dupliquer … x60&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Coller les titres &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Coller les textes&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Importer les images&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Gérer les exceptions : le titres qui ne tiennent pas sur une ligne (coucou La Grenouille qui veut se faire
      aussi grosse que le Bœuf), les textes qui courent sur deux voir trois pages.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Ajuster les polices, les marges, la position de la numérotation, … les petits détails&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Générer la couverture directement sur Lulu&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;OK, on est bon pour une première version !&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-layout.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Le maquettage dans InDesign&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Finaliser la publication&lt;/h2&gt;
&lt;p&gt;Une fois la mise en page terminée, il ne reste plus qu’à finaliser la publication
  !
&lt;/p&gt;
&lt;p&gt;Les instructions fournies par la plateforme sont claires et je n’ai pas rencontré de problèmes particuliers
  à cet étape.
&lt;/p&gt;
&lt;p&gt;Au niveau de la fixation des prix, il tout de même intéressant de noter que dans le cas d’une
  distribution globale (Amazon etc), la marge sur chaque vente est extrêmement faible. Ici, pour un livre vendu 19€90
  sur Amazon, je touche 1€06. Dans le cas d’une vente directe ou via le shop de Lulu, la marge est par contre
  importante, autour de 9€ par vente.
&lt;/p&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-revenues.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;La fixation du prix sur Lulu&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;
  Diffuser le livre
&lt;/h2&gt;
&lt;p&gt;Pour la diffusion, j’ai prévu les actions suivantes : &lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Laisser trainer un exemplaire dans l’espace salon du Moho&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Préparer 10 posts pour LinkedIn&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;En parler dans cet article de newsletter&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Le résultat n’est pas à la hauteur de mes espérance et j’y ai consacré deux fois plus de
  temps qu’initiallement prévu, mais je suis allé au bout !
&lt;/p&gt;
&lt;p&gt;J’ai beaucoup aimé travailler en mode hack et
  essayer d'utiliser la solution la plus simple et la plus directe pour répondre aux besoins.
&lt;/p&gt;
&lt;p&gt;Techniquement, ce
  projet aura été l’occasion de balayer pas mal de technologies. Replonger dans python, tester Airtable, utiliser
  l’api DeepL, prototyper un bot Discord, découvrir Playwright, remettre les mains dans InDesign.
&lt;/p&gt;
&lt;h3&gt;Aller plus
  loin
&lt;/h3&gt;
&lt;p&gt;Pour aller plus loin, il est possible d’améliorer l’édition de la façon suivante : &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;indiquer les morales de chaque fable sous la forme de conseils activables, en une ligne. Les placer en sous
      titre ou dans un index placé en début ou en fin d’ouvrage. &lt;em&gt;Exemple pour Le Cochon, la Chèvre et le Mouton :
      Ne te torture pas l'esprit avec des problèmes qui échappent à ton influence.&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ajouter des questions à méditer. &lt;em&gt;Exemple pour Le Renard et les Raisins : Dans quelles situations de votre
      vie avez-vous déjà rationalisé ou minimisé l'importance de quelque chose que vous désiriez, simplement parce que
      vous ne pouviez pas l'obtenir ou l'atteindre facilement ?&lt;/em&gt;
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;mettre à jour l'édition dans un autre style ou avec une prochaine version de Midjourney &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;D’autre part, un autre d’angle à explorer est de réutiliser le processus mis en place pour créer d’autres
  ouvrages :
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;50 expressions populaires imagées : ceintures et bretelles, raconter des salades, …&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;un recueil de contes illustrés&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;un autre recueil de fables&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;un livre de concepts illustrés (10x, founder market fit, ...)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;une version contemporaine des “Caractères” &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;une version illustrée et expliquée des “27 émotions” &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;div class="captioned-image-container"&gt;
  &lt;img src="_assets/lafontaine-ceinture-et-bretelles.png"/&gt;
  &lt;p style="text-align: center; font-style: italic; font-size: 0.8em;"&gt;Illustration de l’expression “ceintures et
    bretelles”. On est jamais trop prudent !
  &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Difficultés rencontrées&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Rester concentré sur la mise en place du processus de bout en bout avant de chercher à améliorer les
      différentes étapes&lt;/strong&gt;. C’est dingue comme cette tentation est forte de rester des heures à améliorer une
      étape. Par exemple dans la recherche de style, j’ai eu beaucoup de mal à me dire “ok partons avec ça et je
      peaufinerais plus tard”. Si je n’avais pas dès le départ eu la ferme intention d’éviter ce type de “piège”,
      c’est vraiment quelque chose qui aurait pu empêcher le projet de se matérialiser. Au final, la qualité n’est pas
      au niveau que j’espérait, mais le projet est. Et si je veux y consacrer davantage de temps, il m’est posssible
      de l’améliorer.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Définir le public visé&lt;/strong&gt;. J’ai beaucoup hésité entre adresser l’éditions aux enfants, ou à de
      jeunes adultes, ou à des fans de mangas, ou, ou, ou. Au final, j’ai pris l’option de l’adresser à de jeunes
      adultes. Mais ne pas avoir une idée claire de “à qui s’adresse l’ouvrage” m’a fait perdre beaucoup de temps en
      questions et interrogations lors de l’étape de la mise en page et la recherche du style.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Aller au bout de l’itération quoi qu’il arrive&lt;/strong&gt;. En avançant dans les expérimentations, j’ai
      rapidement compris que les illustrations auraient difficilement l’originalité et la qualité que j’espérait et
      que la mise en page n’allait pas non plus être au niveau ambitionné. Plusieurs fois je me suis demandé s’il ne
      vallait pas mieux que je passe directement à un autre projet.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Leçon&lt;/h3&gt;
&lt;p&gt;On dit souvent que “&lt;em&gt;l’idée de vaut rien&lt;/em&gt;” et que &lt;em&gt;“c’est l’exécution qui compte”&lt;/em&gt;.
  Dans ce projet, je me suis rendu compte que, clairement, tant que tu n'as pas essayé de mettre en œuvre l’idée, tu
  n’as pas idée du nombre de problèmes et d’obstacles que tu vas rencontrer. Aussi, le nombre de décisions à prendre,
  et donc de chemins qui partent de l’idée pour arriver à une concrétisation, sont tellements nombreux, que l’on peut
  affirmer que tout se passe dans l’exécution.
&lt;/p&gt;
&lt;p&gt;À très vite pour un nouveau projet !&lt;/p&gt;</content></entry></feed>