tag:blogger.com,1999:blog-76595155555340232442024-02-25T13:13:08.171-08:00Johan MartinssonJohan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.comBlogger57125tag:blogger.com,1999:blog-7659515555534023244.post-70733689453411316702023-04-05T06:52:00.000-07:002023-04-05T06:52:04.422-07:00Some economics of refactoring<p>Refactoring in a separate project is often not the best of ideas 👎 . It might well be the most common form, but it's a terrible idea from an investment point of view. </p><p><br /></p><p>Let's look at the investment profile of various forms of refactoring. First refactoring in a separate project; We refactor for weeks or months to make the code somewhat clean again. Of course the business won't see the fruit of the refactoring until it has made us save time developing new features, after the refactoring is finished. </p><p><br /></p><p>Compared with no refactoring, the break even comes after many months or more.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj-tIjDeNATBhU8DqwZPkVvB91T-ux_jMO_bIBhvUCeBVAJZ7OsEWllEHDP2An2z9UsiezHTW24CiHK6GoXkR6GCBpL8eQngL-UsDfHbsLmdUykM0NADrpbcUCouYg-Edk0zcrLNm_QZSWv_F9zmZSuKlz8wuwjQJj574vXlfrnNvZfoGKdZNzt2t-O" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="957" data-original-width="1044" height="586" src="https://blogger.googleusercontent.com/img/a/AVvXsEj-tIjDeNATBhU8DqwZPkVvB91T-ux_jMO_bIBhvUCeBVAJZ7OsEWllEHDP2An2z9UsiezHTW24CiHK6GoXkR6GCBpL8eQngL-UsDfHbsLmdUykM0NADrpbcUCouYg-Edk0zcrLNm_QZSWv_F9zmZSuKlz8wuwjQJj574vXlfrnNvZfoGKdZNzt2t-O=w640-h586" width="640" /></a></div><br /><br /><p></p><p>Another common type of refactoring is End-of-story refactoring. The value of this refactoring is again reaped when we touch the code again, a few weeks or months in the future. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEguSLlmZNvM8RWfNiNgLql5gWEo9Ci_nrE__v8MCCrBZG3oeRKzJ-vUttSTW45bcJnoJRiibyABet_fqBWwl11czX0hWakGHp0gbZXOqzDx0tXwB5v2OXSGbCpyU0hOvGCAqPyO0adQNJEpKc7vIgACPY0_uF1hC5zki01SV4pTrWptbzQsnnvpqo1e" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="850" data-original-width="1176" height="462" src="https://blogger.googleusercontent.com/img/a/AVvXsEguSLlmZNvM8RWfNiNgLql5gWEo9Ci_nrE__v8MCCrBZG3oeRKzJ-vUttSTW45bcJnoJRiibyABet_fqBWwl11czX0hWakGHp0gbZXOqzDx0tXwB5v2OXSGbCpyU0hOvGCAqPyO0adQNJEpKc7vIgACPY0_uF1hC5zki01SV4pTrWptbzQsnnvpqo1e=w640-h462" width="640" /></a></div><br /><br /><p></p><p>While some basic cleaning is called for - encoding "end-of-story" knowledge into the design - any attempt to make the code "extendable" or modular is risky business. It's not KISS/YAGNI and ROI is uncertain. Break even comes after weeks or months.</p><p><br /></p><p>Now let's look at Preparatory Refactoring, as suggested in 3P ( Protect-Prepare-Produce) https://lnkd.in/eiewkYBe</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj5_su0q44eKpZ3jNt3dNSkGG7ufO84irLW5vDU7bpgsHxL1Gb1DlC5o_QLw6up2ntnPNn2mjvRMJ3EzJqZazF7qC83i0P5IuQW6BdlHAq7axiKgxwYq0YGpabfqOsElw-gvhyRLEpGmT7c37hNSASkxYHJmCmtUaIybaLLViKS_MOqWB-9Pgt5z7hl" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="931" data-original-width="1073" height="555" src="https://blogger.googleusercontent.com/img/a/AVvXsEj5_su0q44eKpZ3jNt3dNSkGG7ufO84irLW5vDU7bpgsHxL1Gb1DlC5o_QLw6up2ntnPNn2mjvRMJ3EzJqZazF7qC83i0P5IuQW6BdlHAq7axiKgxwYq0YGpabfqOsElw-gvhyRLEpGmT7c37hNSASkxYHJmCmtUaIybaLLViKS_MOqWB-9Pgt5z7hl=w640-h555" width="640" /></a></div><br /><br /><p></p><p>The return of investment comes directly after the refactoring. It's an investment opportunity we cannot miss! We're almost certain to at least break even, and in the shortest period of time</p><p><br /></p><p>3P (<a href="https://martinsson-johan.blogspot.com/2022/11/breaking-out-of-legacy-with-3p.html">Protect-Prepare-Produce</a>) acknowledges this and gives us a way of optimizing the ROI while working on stories. Let's see more of 3P and preparatory refactoring, our code will be better off! and perhaps most importantly business people will start to understand and appreciate refactoring 😘 </p><div><br /></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-33922589336804832682023-04-02T03:53:00.004-07:002023-04-02T04:01:50.648-07:00Quick and Dirty tests, FTW<p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;">If we consider solely the refactoring phase: Then what do we care about in tests. What quality do the tests need to be useful to us?</span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;">Imagine we could change the tests later, what do we actually care about during the hours/days we're performing some larger refactoring</span><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;"> </span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;">It turns out we can do quite a few compromises, a lot of the vital qualities of a tests don't matter over a few days, running on a laptop with no other persons reading the tests</span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_g5YEXzoZLigY4-jaXhzGgo3xgPGCVWFbDMyKvc73Vs3xtvN5znhm0Lq9gCYSEZyXhwgnPzaL5bhnn0Px663FESt8dowHFCUe0AseW_FXEhBGFXoWnoqD1zk5bK1N16eHXKQ9zIxJwjzQIxWoukMy4a5KxJpaqehnLpmsmQmQG-f4PBaG73dGdwbg/s2352/quick%20and%20dirty%20tests.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1442" data-original-width="2352" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_g5YEXzoZLigY4-jaXhzGgo3xgPGCVWFbDMyKvc73Vs3xtvN5znhm0Lq9gCYSEZyXhwgnPzaL5bhnn0Px663FESt8dowHFCUe0AseW_FXEhBGFXoWnoqD1zk5bK1N16eHXKQ9zIxJwjzQIxWoukMy4a5KxJpaqehnLpmsmQmQG-f4PBaG73dGdwbg/w640-h392/quick%20and%20dirty%20tests.png" width="640" /></a></div><p></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;"><br /></span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif" style="background-color: white; caret-color: rgb(15, 20, 25); color: #0f1419; font-size: 17px; white-space: pre-wrap;">Now why would one want to write unmaintainable tests only to replace them later?
Well for one it's a lot faster, secondly writing great tests for legacy code is often close to impossible.
Given that we'll modify/replace anyway, the question is "What compromises can we make?"</span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif" style="color: #0f1419;"><span style="background-color: white;"><span style="font-size: 17px; white-space: pre-wrap;">This type of quick-and-dirty tests is precisely the kind of tests were likely to do in a 3P (<a href="https://martinsson-johan.blogspot.com/2022/11/breaking-out-of-legacy-with-3p.html" target="_blank">Protect-Prepare-Produce</a>) approach. The above post will also somewhat lay out when/how/what to do with those quick and dirty tests later.</span></span></span></p><p><span face="TwitterChirp, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif" style="color: #0f1419;"><span style="background-color: white;"><span style="font-size: 17px; white-space: pre-wrap;">What about you, are you doing these compromises. Any other compromises?</span></span></span></p><p><br /></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-63056984997641933822022-11-07T10:24:00.006-08:002022-11-07T10:27:16.413-08:00Breaking out of legacy with 3P<p style="text-align: left;">Protect, Prepare, Produce - 3P. A recipe to break out of legacy</p><p>If you prefer you can read this <a href="https://martinsson-johan.blogspot.com/2022/11/3p-pour-sortir-du-legacy.html">in French</a> </p><p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjetU3WyHJPb2YnsljSZDAZQVaGwmbTHaXDzllWyMpiHE2m0-xT-ItyFFgkJ8_ZzUN0d4RcgZdmU83vXTCHE0v2xKU_4Z4LXlpq7g2mjzLiAIA1k-S-3I_zPaMwXEYmlsJNBKmDP0udA2c8o_iOHo0KIOwl8DDtEiJQxZofNVZIzy0KrPh7rLsx_pkh" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="3840" data-original-width="5760" height="426" src="https://blogger.googleusercontent.com/img/a/AVvXsEjetU3WyHJPb2YnsljSZDAZQVaGwmbTHaXDzllWyMpiHE2m0-xT-ItyFFgkJ8_ZzUN0d4RcgZdmU83vXTCHE0v2xKU_4Z4LXlpq7g2mjzLiAIA1k-S-3I_zPaMwXEYmlsJNBKmDP0udA2c8o_iOHo0KIOwl8DDtEiJQxZofNVZIzy0KrPh7rLsx_pkh=w640-h426" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Photo by Ugne Vasyliute on Unsplash</td></tr></tbody></table><br /><p></p><p>Maybe you've already had experiences like</p><p></p><ul style="text-align: left;"><li>A refactoring project over several months was been cancelled</li><li>Tests that don't really help, perhaps even slowing you down</li><li>Refactorings that don't turn out that good </li><li>Code that continuously degrades, despite the stories dedicated to refactoring and testing that you do regularly.</li></ul><p></p><p>If this is the case you would probably be interested in knowing why these efforts are not bringing the expected benefit and why the 3Ps would help you.</p><p>Let's first consider the classic approaches of either doing refactoring and testing in dedicated stories, or doing most of the refactoring and testing at the end of the story. These approaches seem attractive at first glance, but they have a big ROI problem. Indeed, they lengthen the time between investment (refactoring) and return on investment (a new feature, facilitated by said refactoring). </p><p>Precisely, <b>when we refactor </b>in a separate project or story, or even at the end of a story: <b>who reaps the value, and when</b>? </p><p>It's the next person who touches the code, hopefully, sometime later. And that's provided that the refactoring actually turns out to facilitate the future work that we don't know beforehand... Basically the ROI will be for someone else, maybe. And this in a potentially distant future. Not ideal, but we can notice that the ROI is closer if we do it at the end of the story than if we do it in a separate project.</p><p>Same question for tests. <b>Who reaps the value of tests written after the fact? And when will that be?</b></p><p>The answer is essentially the same. It's the next person who touches the code, hopefully, sometime in the future. Provided that the tests are still relevant with regard to the new need, which we didn't know about when we wrote the tests.</p><p>In short, we can see that there is a time that can be more or less long between the investment and the benefit. The shorter the delay, the better. But that's not all. In my experience, we don't always do the ideal refactoring and we don't always do the ideal tests. One of the reasons is that we don't know the future very well. Could we minimise these "mistakes"?</p><p>So we should find a way to get feedback on the quality of our tests and our refactoring as quickly as possible, and to minimise the uncertainty about the future. If we did this, <b>we'd have refactoring and tests that would bring much more for the same amount of time invested</b>. Given that our investments pays off better, we're likely to increase the investments, possibly by a lot!</p><h2 style="text-align: left;">3P</h2><p>This is where the idea of the 3Ps comes in, where we not only bring the investment closer to the benefit, but also we are the foremost beneficiaries of the tests just wrote and the refactoring we just did! This has the added benefit that any ill conceived tests and refactoring are immediately brought to the attention of just the right person to fix the issue. </p><p>The idea is simple, for each story break down the work into </p><p></p><ol style="text-align: left;"><li>Protect</li><li>Prepare</li><li>Produce</li></ol><p></p><h3 style="text-align: left;">Protect</h3><h4 style="text-align: left;">Setting up the missing tests</h4><p>First we have to protect the changes with tests. To do this, we need to identify the code that needs to be touched in order to add any missing tests (for the upcoming refactoring). We don't worry about covering anything but the code that will be changed, that would be a distraction</p><p>The code that will be affected <b>must be perfectly covered with tests</b>. Any less and we wouldn't dare to thoroughly refactor in the next phase. However, <b>we can completely ignore the maintainability</b> of the tests, we're only concerned with protecting the refactoring phase. So this phase is way shorter than what we might expect.</p><p>We need tests that will not break during refactoring, so low-level tests are not always the best. Typically, duplicating and adapting some high level tests, even end-to-end tests, works quite well. Quite often the code is not testable at all, but with high level tests anything is testable.</p><h3 style="text-align: left;">Prepare</h3><h4 style="text-align: left;">Refactor in depth to make the code fit for the new feature. Address testability</h4><p>We make sure that the new functionality can be inserted in the code in an elegant way. </p><p>We also make the code easily testable. Maybe we wrote end-to-end tests in the protect phase. This is the time to refactor to move those tests to a level where they're fast, predictable and easy to understand. Basically improve maintainability of the tests without sacrificing much of confidence and refactorability.</p><h3 style="text-align: left;">Produce</h3><h4 style="text-align: left;">Code the new functionality in TDD</h4><p>Since the code is clean and testable, this phase can be done using TDD, regardless of the code at the start.</p><h2 style="text-align: left;">Conclusion</h2><p>With the 3Ps we write the tests, to do ourselves the refactoring behind. We minimize the time between refactoring and introducing the new feature. We know the functionality before we start. We can work in TDD to benefit from the new tests ourselves. How could the ROI be better? And if it is, wouldn't we do more of it, a lot more?</p><h4 style="text-align: left;">End notes</h4><p>I'm not saying to avoid refactoring at the end of the story, just that if that's all we're doing, we're missing a lot. </p><p>I'm aware don't go into much detail about how the 3Ps work in practice. I'll try to make it clearer in a subsequent post.</p><p>Translated (mostly) with www.DeepL.com/Translator. <a href="https://martinsson-johan.blogspot.com/2022/11/3p-pour-sortir-du-legacy.html">Originally posted in French</a></p><p><br /></p><p><br /></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-45205765259222190582022-11-02T07:11:00.002-07:002022-11-02T07:11:38.973-07:003P pour sortir du Legacy<p>Protéger, Préparer, Produire - 3P. Une machine pour sortir du legacy.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC5QB_FUfCKyYwu8xW9Rvi6rd_M46q_oZCe0cUZzXm2X0rsMsCD7vlN-THu2yhH-C5MyRX2ih-95mF0eo8tajCTow-DHI6A0WzWn8oJEJ0i6c5ziyGbYXjGh0st1Rk5npxqnWVb_JeakMGQsmzrT_m6pEVG4gjryIQdYMh8falgiXaKZMnAmxLssHV/s5760/ugne-vasyliute-Dag9cv89jb4-unsplash.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Photo by Ugne Vasyliute on Unsplash" border="0" data-original-height="3840" data-original-width="5760" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC5QB_FUfCKyYwu8xW9Rvi6rd_M46q_oZCe0cUZzXm2X0rsMsCD7vlN-THu2yhH-C5MyRX2ih-95mF0eo8tajCTow-DHI6A0WzWn8oJEJ0i6c5ziyGbYXjGh0st1Rk5npxqnWVb_JeakMGQsmzrT_m6pEVG4gjryIQdYMh8falgiXaKZMnAmxLssHV/w640-h426/ugne-vasyliute-Dag9cv89jb4-unsplash.jpg" title="Photo by Ugne Vasyliute on Unsplash" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Photo by Ugne Vasyliute on Unsplash</td></tr></tbody></table><p>Peut-être que avez-vous déjà vécu des expériences du style</p><p></p><ul style="text-align: left;"><li>Un projet de refactoring sur plusieurs mois a été annulé</li><li>Des tests qui n'aident pas vraiment le dev, voire le ralentit</li><li>Des refactorings qui finalement se montrent pas terribles </li><li>Le code qui se dégrade continuellement, malgré des stories dédiées au refactoring et aux tests que vous faites régulièrement.</li></ul><p></p><p>Si c'est bien le cas vous seriez probablement intéressés par savoir pourquoi ces efforts n'apportent pas le bénéfice escompté et pourquoi les 3P vous aiderait.</p><p>Considérons d'abord les approches classiques de faire du refactoring ou tests dans des stories dédiés, ou le simple fait de faire la majorité du refactoring et les tests en fin de story. Certes ces approches semblent attrayantes à première vue, mais elles ont un gros problème de ROI. En effet, elles rallongent le temps entre investissement (le refactoring) et retour sur investissement (une nouvelle fonctionnalité, facilité par ledit refactoring). </p><p>Justement, <b>quand on fait du refactoring</b> dans un projet ou story à part, ou même en fin de story : <b>qui récolte la valeur, et ce sera quand</b>? </p><p>C'est la prochaine personne qui touche le code, d'ici qq temps, avec un peu de chance. Et ce à condition que le refactoring facilitera bien le futur travail qu'on ne connaît pas... En gros le ROI sera pour quelqu'un d'autre, peut-être. Et ce dans un futur potentiellement lointain. Pas idéal, mais on peut remarquer que le ROI est plus proche si on le fait en fin de story que si on le fait dans un projet à part.</p><p>Même question pour les tests. <b>Qui récolte la valeur des tests écrits après coup? Et ce sera quand</b>?</p><p>La réponse est essentiellement la même. C'est la prochaine personne qui touche le code, d'ici qq temps, avec un peu de chance. A condition que les tests seront encore pertinents face à la nouvelle demande, qu'on ne connaissait pas lors de l'écriture des tests.</p><p>Bref on voit bien qu'il y un temps qui peut être plus ou moins long entre l'investissement et la récolte du bénéfice. Plus ce temps est court plus on va y trouver un intérêt. Mais ce n'est pas tout. Dans mon expérience, on ne fait pas toujours le refactoring idéal et pas toujours les tests idéaux. Entre autres parce qu'on connaît pas bien le futur.</p><p>On aurait donc intérêt à trouver une façon de récolter le plus rapidement le feedback sur la qualité de nos tests et notre refactoring, ainsi que de minimiser l'incertitude par rapport au futur. Si jamais on le faisait, <b>on aurait alors des actions de refactoring et de tests qui apporteraient nettement plus pour un même temps investi</b>. Dans ces conditions on aura tendance à en faire plus, voire beaucoup plus.</p><h2 style="text-align: left;">3P</h2><p>C'est là que se trouve l'intérêt des les 3P, où nous rapprochons non seulement l'investissement du bénéfice, mais en plus c'est nous même qui sommes les premiers bénéficiaires des tests que nous venons d'écrire et du refactoring que nous venons d'opérer! </p><p>L'idée est simple, pour chaque story décomposer le travail en </p><p></p><ol style="text-align: left;"><li>Protéger</li><li>Préparer</li><li>Produire</li></ol><p></p><h3 style="text-align: left;">Protéger</h3><h4 style="text-align: left;">Mise en place des tests manquants</h4><p>D'abord il faut protéger les modifications avec des tests. Pour cela il faut identifier le code qu'on doit toucher pour compléter avec des tests manquants. Attention il s'agit uniquement des tests pour le code qui sera touché, tester plus serait une distraction</p><p>Le code qui sera touché doit être parfaitement couvert avec des tests bétons. Sans quoi on n'osera pas faire de refactoring digne de ce nom.</p><p>On fait plutôt des tests haut niveau dans cette phase afin qu'elles ne cassent pas lors du refactoring. Pour gagner beaucoup de temps, on ignore complètement l'aspect maintenabilité dans cette phase (voir la suite)</p><h3 style="text-align: left;">Préparer</h3><h4 style="text-align: left;">Refactoring en profondeur pour rendre plus facile la nouvelle fonctionnalité</h4><p>On fait en sorte que la nouvelle fonctionnalité puisse s'insérer de façon élégante dans le code. </p><p>On rend également le code facilement testable. Peut-être qu'on a écrit des tests end-to-end dans la phase protéger. C'est le moment de refactorer pour rendre possible de déplacer ces tests à un niveau où ils seront plus rapides et où il sera plus facile de maintenir les tests.</p><h3 style="text-align: left;">Produire</h3><h4 style="text-align: left;">Coder la nouvelle fonctionnalité en TDD</h4><p>Puisque le code est propre et testable, cette phase peut se faire en TDD, quel que soit le code au départ.</p><h2 style="text-align: left;">Conclusion</h2><p>Avec les 3P nous écrivons les tests, pour faire nous même le refactoring derrière. Nous pouvons travailler en TDD pour bénéficier nous même des nouveaux tests. Nous minimisons le temps entre refactoring et introduction de la nouvelle fonctionnalité. Nous connaissons la fonctionnalité avant de commencer. Comment le ROI pourrait-il être plus idéal?</p><p>Et toi lecteur, tu fais comment?</p><h4 style="text-align: left;">Notes de fin</h4><p>Je ne dis pas qu'il faut éviter le refactoring en fin de story, simplement c'est pas la part principale. </p><p>Je ne donne pas beaucoup de détails sur comment s'articule les 3P concrètement. Je vais tâcher à rendre ça plus claire dans un autre post.</p><p><br /></p><p><br /></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0France46.227638 2.21374915.049421842554978 -32.942501 77.40585415744502 37.369999tag:blogger.com,1999:blog-7659515555534023244.post-54489532364847797602022-10-06T15:43:00.018-07:002022-10-10T02:41:46.714-07:00Surprising Facts about the Dev Practices that drive Business Performance<p><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;">I'm currently re-reading the book </span><em style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; font-synthesis: style;"><a href="https://www.goodreads.com/en/book/show/35747076-accelerate">Accelerate, the science behind devops</a></em><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;"> </span><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;">where a list of technical practices are found to contribute to continuous delivery, which in turn contribute to organisational performance - meaning roughly that they increase revenue, market share etc.</span></p><div><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">This post is meant both as a reminder to myself and as a way to condense the essence of the findings. I particularly put all the findings that were surprising either to the authors or to me.</p><h1 style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: calc(1.2777777777777777em); line-height: calc(1.3043478260869565); margin-left: 0px; margin-right: 0px; margin: calc(2.608695652173913em) 0px; text-align: left;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjSPgqAuiB2nuys1s4Y02RCLjgHXN2gq2k4i5Vpa1y9DsG3ZUlWYoLWS_YjEkYloTKRjV1CD1UiapKeeaz-3tiPWbX7smxnDi1g2_5Gy0f2rN13bi0OhJ7opqw2cHBs35fZsSfHZN6jXiCFysxWJH2Q7owzce_qnW5EYh5PzSm-GaCwXVEuAbOlrqSS" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="2380" data-original-width="5184" height="294" src="https://blogger.googleusercontent.com/img/a/AVvXsEjSPgqAuiB2nuys1s4Y02RCLjgHXN2gq2k4i5Vpa1y9DsG3ZUlWYoLWS_YjEkYloTKRjV1CD1UiapKeeaz-3tiPWbX7smxnDi1g2_5Gy0f2rN13bi0OhJ7opqw2cHBs35fZsSfHZN6jXiCFysxWJH2Q7owzce_qnW5EYh5PzSm-GaCwXVEuAbOlrqSS=w640-h294" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Photo by Isaac Wendland on Unsplash</td></tr></tbody></table><br /></h1><h2 style="text-align: left;">Practices</h2><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">The following technical practices were found to be significant in predicting high <em style="box-sizing: border-box; font-synthesis: style;">Software Delivery Performance</em> that in turn predicts <em style="box-sizing: border-box; font-synthesis: style;">Organisational Performance</em>.</p><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Version control</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">All teams have version control for the code. However having additionally</p><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;"><ol style="text-align: left;"><li>system configuration<div class="separator" style="clear: both; text-align: center;"><br /></div></li><li>build and deploy scripts </li></ol></span><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;">under source control is correlated with high performance</span><span face="IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif" style="caret-color: rgb(26, 26, 26); color: #1a1a1a; font-size: 16px;"><br /></span><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Test automation</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">Having automated tests are correlated with high performance. but <span style="box-sizing: border-box; font-weight: 700;">only</span> if they're reliable (not flaky). Otherwise having automated test is <span style="box-sizing: border-box; font-weight: 700;">not</span> correlated to high performance.</p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">Having tests primarily written and maintained by a QA team or an outsourced party is <span style="box-sizing: border-box; font-weight: 700;">not</span> correlated to high performance. </p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">I.e better have tests written and maintained by developers</p><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Trunk based development</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">Teams that did well practiced Trunk Based Development of some sort. The best performing teams had fewer than three active branches at any time and branches were merged at least daily.</p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">Using feature branching or git-flow was negatively correlated to performance. However the authors acknowledge that if you merge your branches at least daily the difference may be negligible. They also acknowledge that git-flow is a good fit for open source community projects.</p><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Architecture</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">High perf is possible with all kinds of systems, provided that the systems, and the teams, are loosely coupled. That is high performing teams were found in legacy systems as well as green field projects.</p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">Highly correlated with high performance are the ability to </p><ol style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px; padding-left: 1.5rem;"><li style="box-sizing: border-box;">Do most of the testing without integration with other platforms.</li><li style="box-sizing: border-box;">Deploy without synchronisation with other teams</li></ol><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">They also found the surprising fact that for high performing teams the number of deploys <span style="box-sizing: border-box; font-weight: 700;">per</span> developer increased with more devs. The inverse was true for poor performers. This stresses the importance of loosely coupled architecture.</p><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Choice of tools</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">Being able to choose the best tools for the job contributes to performance and hence is found to outweigh the value from rationalisation by mandating tools within an organisation.</p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">They mention a few exceptions to this rule.</p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">They suggest Architecture as a service, where system teams work as service providers to other teams, as a good way to strike a balance. The feature teams ultimately retain free choice of what is best for them.</p><h2 style="text-align: left;"><br /></h2><h3 style="text-align: left;">Infosec</h3><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: 0px auto;">Teams that integrate infosec in the design and development, as opposed by auditing after the fact, spend 50% less time fixing security problems. I.e infosec teams should be involved in design and assist in demos regularly. </p><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">Info sec teams, just as architectes, can provide tools that are easy-to-consume, pre-approved libraries, packages, toolchains to make it easy for teams to develop secure applications.</p><h2 style="text-align: left;"><br /></h2><h2 style="text-align: left;">Conclusion</h2><p style="box-sizing: border-box; caret-color: rgb(26, 26, 26); color: #1a1a1a; font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin-bottom: 0px; margin-left: auto; margin-right: auto; margin: calc(1.6666666666666665em) auto 0px;">There are a lot more in the book than a list of technical practices, it's well worth a read. I hope you found this condensed resume either useful in itself or an inspiration to read the book :)</p></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-48570182782989291902021-11-21T03:19:00.000-08:002021-11-21T03:19:34.277-08:00TDD avec tests intégrés<p style="text-align: left;"><span style="font-family: arial;">Ce que nous attendons des tests<b> dans une CI</b> est qu'ils soient parfaitement reproductibles, extrêmement rapides, faciles à tourner n'importe où.</span></p><p><span style="font-family: arial;">Ce que nous attendons des tests lorsque nous développons du nouveau code <b>en TDD</b> est que les tests soient représentatifs de la réalité, que la différence avec la production soit faible, que l'on puisse démarrer en TDD au plus vite (pour écrire le minimum de tests après-coup) et qu'on puisse appliquer le TDD sur une partie importante du code.</span></p><p><span style="font-family: arial;">Ce sont des besoins très différents, qui de plus nous importe à des phases séparées! Pourtant nous nous efforçons de répondre aux deux besoins avec les mêmes tests en même temps. </span><span style="font-family: arial;">Mais qui a eu une idée pareille? </span></p><p><span style="font-family: arial;">Que se passerait-il <b>si nous commencions les stories avec des tests intégrés et si on isolait uniquement "au besoin"</b>? Si à la fin de la story on s'assurait d'avoir les tests maintenables et adaptés à tourner dans une CI personne ne saurait qu'on fait des tests </span><i style="font-family: arial;">sales</i><span style="font-family: arial;"> entre temps. Peut-être qu'on en tirerait une grande valeur?</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2QVU4dFoibDmD-yXcw039xmhHAxrvv9EML8NMl6yBlVqM87QahM-W7Lpruy8HoVMeYXt846q_RULEQyWkGGJ8o-cXXTZpdtwTsQ0wtLi5LEA96p5NKPhQ_vSdFTxtpx7n_4t8ITdJbX0/s2414/Image+PNG-7E5D0449E991-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1110" data-original-width="2414" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2QVU4dFoibDmD-yXcw039xmhHAxrvv9EML8NMl6yBlVqM87QahM-W7Lpruy8HoVMeYXt846q_RULEQyWkGGJ8o-cXXTZpdtwTsQ0wtLi5LEA96p5NKPhQ_vSdFTxtpx7n_4t8ITdJbX0/w640-h296/Image+PNG-7E5D0449E991-1.png" width="640" /></a></div><p></p><h3 style="text-align: left;"><span style="font-family: arial;">L'approche classique</span></h3><p><span style="font-family: arial;">Typiquement pour répondre aux besoins de maintenabilité des tests nous nous isolons des dépendances externes tels que apis externes, bases de données etc. Ce qui implique que nous introduisons une couche d'isolation de ces dépendances, en simulant ces dépendances avec des mocks ou des simulateurs fait main. Du côté framework qui nous appelle, on va plutôt ignorer le framework dans nos tests en appelant directement notre code.</span></p><p><span style="font-family: arial;">Imaginons la situation où nous devons écrire une fonction lambda ou cloudfunction pour modifier une valeur persisté en bdd. Nous devons décrire la fonction, ses paramètres, les ressources auxquelles elle a droit, faire de la validation des droits utilisateur, extraire des données de la requête, récupérer une valeur en bdd, récupérer une autre valeur dans une api externe, sauver une nouvelle valeur en bdd et qq part dans tout ça faire un petit calcul. A quel moment faisons nous du TDD avec tests unitaires? </span></p><p><span style="font-family: arial;">Probablement assez tardivement, une fois que nous avons réussi à mettre en place tout le reste et ce uniquement sur la partie calcul - le moins risqué de tout. On comprend bien que l'apport du TDD dans l'ensemble de la story semble dérisoire! Certes au bout d'un moment la complexité grandit et les tests unitaires changent tout, mais le problème est qu'il faut attendre un certain temps et pendant ce temps les tests et le TDD semblent apporter peu, donc ne sont pas faits.</span></p><p><span style="font-family: arial;"><b>La bonne volonté de faire des tests de bonne qualité</b>, notamment en termes d'isolation, <b>nous contraint de ne les utiliser que pour une petite partie du problème</b>, après avoir presque terminé la tâche. Pas étonnant qu'on commence pas avec les tests!</span></p><p><span style="font-family: arial;">La bonne question à se poser serait plutôt; <b>Comment puis-je faire pour que les tests me donnent un feedback de qualité le plus tôt possible?</b> Quand je dis "feedback de qualité" je pense à rapide et proche de la réalité sur le code en construction.</span></p><p><span style="font-family: arial;">Par exemple, il est tout à fait possible de lever temporairement les contraintes de besoins tel que la répétabilité, la rapidité et tout autre aspect de la maintenabilité. Des tests qui prennent 1 seconde à tourner, voire plus, ne poseront pas de problème pendant qq heures. Des tests qui ne tournent pas sur la machine de mes collègues ne poseront pas de problème tant que je ne les mets pas dans le pipeline. Même des tests que je suis le seul à comprendre (avec mon pair) suffisent amplement tant que personne d'autre ne travaille dessus. Je pourrais alors faire du TDD avec des tests très haut niveau en totale intégration avec toute dépendance externe....</span></p><h3 style="text-align: left;"><span style="font-family: arial;">TDD en totale intégration avec toute dépendance</span></h3><p><span style="font-family: arial;">Cette approche est le mieux illustré par le workflow d'une story lorsqu'il n'existe aucun code existant. Mon workflow dans ce cas est de commencer avec un test très haut niveau, au niveau de l'api, sans rien mocker, ni même des prestataires externes. En pur TDD je vais faire passer ce test en faisant du code sale, dans cet exemple en faisant tout le code dans mon contrôleur. Mon code ne sera pas très gros, car il n'aura pas de branches, pas de validation, pas de configurabilité, ce sera pour plus tard grâce à d'autres tests (justement TDD), ainsi j'aurai fait passer le test assez rapidement. Je passe ensuite à un deuxième test aussi haut niveau, je le fais passer rapidement, et ainsi de suite.</span></p><p><span style="font-family: arial;">Au bout d'un moment, typiquement au bout de 3-5 tests la logique commence à se cristalliser. Le comportement d'un service aura été découvert, les interactions avec la BDD stabilisés. Il y aura aussi de la logique pure de transformation de données et validation. C'est un bon moment pour concevoir une interface mockable pour s'isoler de la dépendance externe, concevoir l'interface d'une classe repository, une classe de service et peut-être des value objects. Ces extractions rendent possible l'isolation des dépendances et je peux ainsi continuer avec d'une part des tests haut niveau mais cette fois-ci sans dépendances, soit des tests haut niveau de l'hexagone, et d'autre part des tests bas niveau de mes adapteurs (le code qui invoke des services externes) si j'ai besoin de les explorer davantage.</span></p><p><span style="font-family: arial;">C'est à ce moment du workflow que les tests commencent à devenir maintenables. C'est donc qu'à partir d'un certain moment que je fais mes choix de design interne et ça c'est clé! Certes on peut faire ces choix au début, mais cela implique de prendre des décisions avec moins d'informations, donc plus souvent sous-optimales. Si on doit revenir sur un choix de design cela coûtera plus cher si des tests sont déjà écrits contre ces choix! Cerise sur le gâteau, ces tests ne pourront pas protéger le refactoring. Mon choix par défaut est donc de commencer avec des tests intégrés afin de retarder la prise de décisions, avoir le maximum de feedback proche de la réalité et assurer la refactorabiltié de l'ensemble le cas échéant. </span></p><p><span style="font-family: arial;">Mais là où je trouve l'isolation "d'office" néfaste c'est pour les débutants en TDD, soit presque la totalité des devs… <b>Les inciter à résoudre les problèmes de maintenabilité des tests avant même de commencer à les écrire leur rend la tâche plus dure</b>. Le tout pour un bénéfice discutable, ou inexistant, voire même négatif quand cela, comme souvent, résulte dans des tests pas écrits du tout! Pour apprendre à travailler avec des tests il faut déjà en faire. </span></p><h3 style="text-align: left;"><span style="font-family: arial;">Conclusion</span></h3><p><span style="font-family: arial;">Ce n'est pas parce qu'on veut des tests maintenables qu'on dois s'infliger cette obligation dès le 1er test.</span></p><p><span style="font-family: arial;">En particulier il peut y avoir un certain nombre d'avantages à faire autrement :</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: arial;">Dans la phase TDD (construction) les tests totalement intégrés peuvent nous apporter plus que des tests isolés.</span></li><ul><li><span style="font-family: arial;">En terme de fidélité avec la réalité</span></li><li><span style="font-family: arial;">En terme de couverture</span></li></ul><li><span style="font-family: arial;">Il est plus facile de commencer en TDD avec des tests intégrés.</span></li><li><span style="font-family: arial;">Plus les tests sont haut niveau, plus ils permettent des refactorings</span></li></ul><p></p><p><span style="font-family: arial;">Pour être clair je ne transige pas sur les qualités des tests à la fin de la story. Simplement j'utilise souvent des tests intégrés au début et pendant une story. Je pense aussi que c'est plus facile d'en tirer un bénéfice dans la phase de construction, ce qui inciterait plus de gens à essayer le TDD sur du vrai code.</span></p><p><span style="font-family: arial;">Surtout, n'hésite pas à essayer.</span></p><h3 style="text-align: left;"><span style="font-family: arial;">Notes de fin<br /></span></h3><p><span style="font-family: arial;">En pratique je fais des variantes dans le workflow ci-dessus, plus classiques comme approche. Par exemple, pour intégrer un service externe on peut très bien directement explorer l'api avec des tests bas niveau. Un autre exemple c'est lorsque le code d'intégration avec une dépendance externe existe déjà j'utilise directement son simulateur, au lieu d'intégrer avec le service externe. </span></p><p><span style="font-family: arial;">Encore un autre exemple est lorsque la story ne concerne pas les dépendances alors évidemment on utilise pas de test intégré.</span></p><div><br /></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-81517395799043521642021-10-04T15:33:00.000-07:002021-10-04T15:33:38.167-07:00Configure XDebug for code coverage in PhpStorm<p style="text-align: left;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">A frequent problem when running TDD or legacy testing workshops is to get code coverage for PHP to work within PhpStorm. In fact XDebug is needed and it can be a bit tricky to install on some configurations. This post shows how to make it work any time, by running it within a docker container.</p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">PhpStorm has the ability to run the php executable, for testing or for debugging using a docker container. While that's easy and well documented we also need a docker image with xdebug installed and we also need to configure the code coverage to NOT try to code cover the whole "vendor" directory which would be slow to say the least. So let's dive into how it's done</p><h2 style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: calc(1.1666666666666665em); line-height: calc(1.4285714285714284); margin-bottom: calc(1.6666666666666665em); margin-top: calc(2.8571428571428568em); text-align: center;">Configure a php cli to run in docker</h2><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">Open settings and click on the "three dots"</p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7WBoG7DYrxak3GtmFw0URwk4W3Q15eEMhUsqzb2Z95HLyWYcYj7WfnenD5NE0vF8a_YWq-xDJFlGfkGfeGuCKLrVGl64QSAOCZsckXXeYae42RwSgyKFbzjgu0iUezrDMPVBC8SWtiu4/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="438" data-original-width="1986" height="72" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7WBoG7DYrxak3GtmFw0URwk4W3Q15eEMhUsqzb2Z95HLyWYcYj7WfnenD5NE0vF8a_YWq-xDJFlGfkGfeGuCKLrVGl64QSAOCZsckXXeYae42RwSgyKFbzjgu0iUezrDMPVBC8SWtiu4/" width="320" /></a></div><br /><br /><br /><br /><br /><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">Click "+"</p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCQe51Ozb8SDmWIH_pCYArRJ0dGtBEHnLipqzhWexOHS1NT17X4UrKVfRoLjCAurn2bzdVovNLMBHL442xcmOC0QnrPDVLayPwFJJMEpHyMleu6pHz3WKp1etY1HkB2cZAQndBvtVKmmw/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="412" data-original-width="1128" height="118" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCQe51Ozb8SDmWIH_pCYArRJ0dGtBEHnLipqzhWexOHS1NT17X4UrKVfRoLjCAurn2bzdVovNLMBHL442xcmOC0QnrPDVLayPwFJJMEpHyMleu6pHz3WKp1etY1HkB2cZAQndBvtVKmmw/" width="320" /></a></div><p></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">Choose "From docker, Vagrant, ..."</p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidE4V6icCX-IyIJLG-uJHuyF-7nCfAGOEUV9H0IxRt_rul_m4CICu1kCRr9GKauLZFcOMQ2-WoA4d267HBzEnEMnLVDH7QRwAbPS-I-pCS1vksesagUvWQ_FmhJ0BFEKVg1ntI511faBU/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="154" data-original-width="708" height="71" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidE4V6icCX-IyIJLG-uJHuyF-7nCfAGOEUV9H0IxRt_rul_m4CICu1kCRr9GKauLZFcOMQ2-WoA4d267HBzEnEMnLVDH7QRwAbPS-I-pCS1vksesagUvWQ_FmhJ0BFEKVg1ntI511faBU/" width="320" /></a></div><br /><p></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br />Enter the full image name. </p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5F7moaWdv1Qe2_tJflyXFJIS4WR75H4IBuGbxwBXLuI2Qt9bGbxGMP-cx88881Dgbki5aFS8HofZMC7o6eFlu6Xj9kPFCB0xlSM9ThASKucboXLeJZOEPbAksTxM5JhdqN8lEFBb0F0o/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="474" data-original-width="1486" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5F7moaWdv1Qe2_tJflyXFJIS4WR75H4IBuGbxwBXLuI2Qt9bGbxGMP-cx88881Dgbki5aFS8HofZMC7o6eFlu6Xj9kPFCB0xlSM9ThASKucboXLeJZOEPbAksTxM5JhdqN8lEFBb0F0o/" width="320" /></a></div><br /><br /><p></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">At this stage your tests should run, the debugger should work. You might have to configure the "Test Framework" in the settings, and in particular the autoloader to use. For instance </p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8feGRkX1oiNkh1QVVZ7CcSsja0i6Yiv6FNZWM4UcQ3gCY0KwApztCHOKAa0_6E3aqIHb04tLILp_U2ttCu10kxfkTXM8rZ-L9FZ3l2S-uczN2IRvEYOPOPkbdr7rw8slYhO8OZrVZ7u0/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="526" data-original-width="1466" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8feGRkX1oiNkh1QVVZ7CcSsja0i6Yiv6FNZWM4UcQ3gCY0KwApztCHOKAa0_6E3aqIHb04tLILp_U2ttCu10kxfkTXM8rZ-L9FZ3l2S-uczN2IRvEYOPOPkbdr7rw8slYhO8OZrVZ7u0/" width="320" /></a></div><br /><br /><p></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;"><br /></p><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">Note for PHP > 7.3 : PhpStorm <a href="https://github.com/JetBrains/phpstorm-docker-images">produced images</a> up until PHP7.3. But if you need 7.4 or above you'll have to make your own. For instance here's the whole Dockerfile you need for 7.4</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9.8pt;"><span style="color: #cc7832;">FROM </span>php:<span style="color: #6897bb;">7.4</span><br /><span style="color: #cc7832;">RUN </span><span style="color: #c57633;">pecl </span>install xdebug-3.1.0<span style="color: #6897bb;"><br /></span><span style="color: #cc7832;">RUN </span><span style="color: #c57633;">docker-php-ext-enable </span>xdebug</pre><h2 style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: calc(1.1666666666666665em); line-height: calc(1.4285714285714284); margin-bottom: calc(1.6666666666666665em); margin-top: calc(2.8571428571428568em); text-align: center;">Configure the code coverage to exclude vendor</h2><p style="box-sizing: border-box; caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px; margin: calc(1.6666666666666665em) auto 0px;">It boils down to use the option of "whitelist" or the more recent "coverage" to tell phpunit which source code to instrument for code coverage.</p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;">While it should work to pass options to phpunit to ignore vendor, it seems that PhpStorm makes this impossible. I've never been able to configure this without an xml file. </span><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;">On the other hand with the xml file it's easy.</span></p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br /></span></p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkN1mjd7DZILjLcAaXkjRkn89Phb5whdGwJT8evSB6X_CF77WZkGakyYsS4O36BPWXbEElvak_Rr65-5NOi6gLPVaWYvf0DBtSnsFJN5F2VYMUNrFtQtoTkFvAiAsp5GiwQl2oO7njHXc/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="520" data-original-width="1960" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkN1mjd7DZILjLcAaXkjRkn89Phb5whdGwJT8evSB6X_CF77WZkGakyYsS4O36BPWXbEElvak_Rr65-5NOi6gLPVaWYvf0DBtSnsFJN5F2VYMUNrFtQtoTkFvAiAsp5GiwQl2oO7njHXc/" width="320" /></a></div><br /><br /><p></p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"> </span></p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br /></span></p><p><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br />Add a section "filter" or "coverage" with your source code directory.</span></p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8ScMITN7xbRyzCRg3m3Uf5ZHUYAKv2QArBa0aTaNa8DxbPQhf7F7SdbcbB-Uc5jv50lHdqHEj0egDcix0OyuWSxiJzhKw6Oe1kywtSgcI-pGtbw8HZZylLupd0ci6YdUztRauIXsOxxQ/" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" data-original-height="588" data-original-width="1058" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8ScMITN7xbRyzCRg3m3Uf5ZHUYAKv2QArBa0aTaNa8DxbPQhf7F7SdbcbB-Uc5jv50lHdqHEj0egDcix0OyuWSxiJzhKw6Oe1kywtSgcI-pGtbw8HZZylLupd0ci6YdUztRauIXsOxxQ/" width="320" /></a></div><p></p><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;">Now you're ready to run the tests with code coverage</span></div><div><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br /></span></div><div><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br /></span></div><div><span style="caret-color: rgb(204, 204, 204); font-family: IBMPlexSans, iAWriterEmphasis, iAWriterDuo5, -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><br /></span></div><div><br /></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-64582261821880930182021-04-13T01:57:00.000-07:002021-04-13T01:57:00.958-07:00On premature optimisation<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVaS2wWuLz719wCi3JN7_2NM2eckfCd7XqlcFkIoSLtDEWoE26FC5spuA46U8dPlfVNrT60P6Vu7qEAiZ1cAiVhTDv_DkI_xKg04_dHN0eGdewVE9nHlwLixDFKCND8SvvoY1mlylHf40/s459/the-hare-and-the-tortoise.jpg" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="459" data-original-width="326" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVaS2wWuLz719wCi3JN7_2NM2eckfCd7XqlcFkIoSLtDEWoE26FC5spuA46U8dPlfVNrT60P6Vu7qEAiZ1cAiVhTDv_DkI_xKg04_dHN0eGdewVE9nHlwLixDFKCND8SvvoY1mlylHf40/s320/the-hare-and-the-tortoise.jpg" /></a></div><br /><p><br /></p><p>Dev: this code is more optimal, i.e. better</p><p>Senior dev: most performance problems appear where we don't expect them, let's wait and measure</p><p>Senior dev: Besides most code optimizes one thing, it can be CPU-time, but also readability (i.e. developer time), but rarely more one thing. If we have to choose, which is the best optimisation in this case?</p><p><br /></p><p>Dev: we need to optimise before it is too late</p><p>Senior dev: most perf problems I've had to deal with, were very simple to solve. Just limit the number of round-trips to the db, if not possible parallelize, if not possible maintain a read-model. Let's wait and measure.</p><p><br /></p><p>Dev: but some problems require a completely different paradigm</p><p>Senior dev: True, and most often the business never scales that much. Let's wait (building a working system), validating that we actually have that problem! And read up on the <a href="https://blog.acolyer.org/2019/03/06/keeping-calm-when-distributed-consistency-is-easy/">CALM theorem</a>, <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">CRDT</a>'s*, CQRS etc</p><p><br /></p><p>PS: Both devs are me. Just separated by two decades of experience </p><p>* CRDT is a basically a data-structure that allows parallel writes</p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com1tag:blogger.com,1999:blog-7659515555534023244.post-58251874681723863192021-01-05T14:13:00.000-08:002021-01-05T14:13:32.025-08:00Hexagonal architecture vs Functional core / Imperative shell<p>There's been some comparison about theese two architectural styles recently. To me they address totally very different concerns and are by no means mutually exclusive. Let's review that visually.</p><p>Let's take the example of some user calling a rest-service with some request for modification. A typical application will have the workflow of </p><p></p><ul style="text-align: left;"><li>Validation </li><li>Load state from db </li><li>Compute new state</li><li>Save state to db</li></ul><h3 style="text-align: left;">Hexagonal architecture</h3><div>I assume you have basic knowledge about this architecture. If not you might want to have a look <a href="https://alistair.cockburn.us/hexagonal-architecture/" target="_blank">at this</a>. In this paradigm we'd distribute the steps of the given use case in the following way.</div><div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimr_P9PiFQ0LEFNKgDbUmNPxoCqf3NHQOam90Zlf_pBBCjenQ_vFDjViJsz7mhs1Uf70vi0-jjUbnJSg53qny5UnXfEsxX4l0t8lakZ25RKGcSJfV_OO87OVkC2bIewQEsYfa6l5ChpL0/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1784" data-original-width="2048" height="557" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimr_P9PiFQ0LEFNKgDbUmNPxoCqf3NHQOam90Zlf_pBBCjenQ_vFDjViJsz7mhs1Uf70vi0-jjUbnJSg53qny5UnXfEsxX4l0t8lakZ25RKGcSJfV_OO87OVkC2bIewQEsYfa6l5ChpL0/w640-h557/hexagonal+architecture+.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Inside the "hexagon" everything except what talks to the outside world</td></tr></tbody></table><br /><br /></div><div class="separator" style="clear: both; text-align: left;">We have separated the blue parts from the yellow because those are the parts that talk to the outside world. The boundaries that we create by separating the repository from the rest are useful for testability (and a few other things that I won't go into in this post). We can test the whole logic by simply mocking the repository. So here the emphasis is on creating boundaries from the outside world. All the rest is considered inside the hexagon.</div><div><br /></div><h3 style="text-align: left;">Functional core / imperative shell</h3><div>In this paradigm we focus on what is pure and impure. A pure function is a function that needs nothing else than values as inputs and that does nothing else than returning a result. No side effects, no loading data from the file-system etc. A pure function <b>always </b>returns the same values given the same inputs. The properties of a pure function make them very nice to work with. For one thing they can be tested without any mocks, they don't surprise us, results can be cached(!) and so on. Because they have these nice properties we want to separate them from the rest, and possibly maximize the scope of them.</div><div><br /></div><div>Let's look at the same application from the point of view of what is functional core and what is the imperative shell. </div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidyG2XLf2aKqIMRshj8a43akfXps2_Xa1Fx1lM9yL1oSv_OkyJMGtOiuHwNW9CHtaZBavyHJ9kXu-h-H_umxXHq18999GGHrPFOcVjqq44YdnLRnXSHM4Bstnmb3laVoW1M8IyllCayQE/s2048/functional+core+imperative+shell.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1753" data-original-width="2048" height="548" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidyG2XLf2aKqIMRshj8a43akfXps2_Xa1Fx1lM9yL1oSv_OkyJMGtOiuHwNW9CHtaZBavyHJ9kXu-h-H_umxXHq18999GGHrPFOcVjqq44YdnLRnXSHM4Bstnmb3laVoW1M8IyllCayQE/w640-h548/functional+core+imperative+shell.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The adapters and all the coordination logic is in the imperative shell. This shell delegates as much as possible to the functional core through calls to pure functions.</td></tr></tbody></table><div><br /></div><div><br /></div><div></div><div>There is no change! The only difference is how we classify things. Admittedly this is because I already separated the pure parts from the rest. Note that the diagram is simplified in the sense that the validate and compute stages do not actually call the next stages. Both are pure functions that just return some value. </div><div><br /></div><div>Although in this case the architecture is both hexagonal and functional core / imperative shell, it is seldom the case. Often what we do in traditional applications is a lot of validation and computational logic in the middle of our service classes, just next to where we load and save state. Even worse we load state and save state in several steps with computational logic intermingled so there is very little to extract. </div><div><br /></div><h3 style="text-align: left;">Functional islands</h3><div>What is called functional core is actually more like functional islands. It only makes sense if we can make them big enough so that at-least it makes sense to test them in isolation. That is, we tend to mould the architecture to increase the size of the functional islands. To get the most out of functional core we will tend to load everything beforehand and save everything afterwards, limiting the round-trips to the DB while risking loading data that is not used. One of the architectures that gives us the greatest opportunity for functional islands is EventSourcing, <a href="https://martinsson-johan.blogspot.com/2020/12/coder-et-tester-mieux-avec-cqrs-et.html" target="_blank">a though I detail here</a>.</div><div><br /></div><div>Personally I tend to extract pure functions a lot. It's easy to edge towards this in legacy apps and I find it a better trade-off for new apps because it makes testing a breeze, tests are blazing fast, they are easier to read without mocks and it is easier to write them in a manner that minimizes test maintenance. I tend not to do it when the islands are too small.</div><div><br /></div><div>For more on this paradigm, here's a <a href="https://github.com/kbilsted/Functional-core-imperative-shell/blob/master/README.md">good collection of links</a>. </div><div><br /></div><h3 style="text-align: left;">Conclusion</h3><div>The two patterns are not mutually exclusive, in fact a lot of the goodness that both strive for is common, like testability and separation of concerns. As a result we get changeability.</div><p></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-87383790267190914472021-01-05T01:53:00.000-08:002021-01-05T01:53:56.800-08:00 Comment éviter un piège avec les systèmes "EventSourced"<p><br /></p><p>Dans ce type de système apparaît un problématique pour laquelle nous sommes mal armées car elle n'existe pas dans des systèmes traditionnels. Certes il n'est pas compliqué de l'éviter si on le connaît, mais il est possible de le découvrir tard et ainsi de payer cher la correction.</p><p>TLDR; l'état perceptible d'un système est un cache. Quand on vide ce cache pour le re-calculer, il va être calculé avec la dernière version du code et donc le résultat pourrait être différent. Pour s'en prémunir il y a <i><b>une invariante à respecter</b></i></p><h3 style="text-align: left;">Quelques bases pour comprendre</h3><p>Pour comprendre la problématique, revoyons d'abord les bases de l'approche EventSourcing. Visuellement, ca va être simple. Si jamais mon résumé en 10 lignes ne suffit pas, il a y a <a href="https://www.eventstore.com/blog/what-is-event-sourcing">ceci</a>.</p><p>Quand une <b>commande</b> arrive par le biais d'un utilisateur ou d'une autre application, elle se fait transformer en un <b>event</b> (ou plusieurs) qui est persisté. Ces évènements sont utilisés pour calculer l'<b>état (state)</b> actuel du système. La fonction <b>decide</b> transforme la commande en <b>event</b> et la fonction evolve transforme un event en état qui est incrémenté avec chaque nouvel évènement. Certes les deux fonctions ont aussi besoin de l'état actuel pour ce calcul, mais j'ai omis ce détail dans le dessin. </p><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEBf7uvosrBJsxfZKe7fu3sdZzcKgPOxwGrwgy8m2vJa8C22ARYWE-FdK3C8dWwm3Dbl22gdG_2297bYtLOp0cTcMoguGN3-8glkHbQkKwaTNHd1QPlb1qyuHU4okpeyclkIFru3WcXlo/s2048/piege+es+1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1535" data-original-width="2048" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEBf7uvosrBJsxfZKe7fu3sdZzcKgPOxwGrwgy8m2vJa8C22ARYWE-FdK3C8dWwm3Dbl22gdG_2297bYtLOp0cTcMoguGN3-8glkHbQkKwaTNHd1QPlb1qyuHU4okpeyclkIFru3WcXlo/w640-h480/piege+es+1.png" width="640" /></a></div><br /><p></p><p><br /></p><p>On pourrait faire sans l'état mais pour des raisons de performance on fait un (ou plusieurs) états. A n'importe quel moment cet état peut être réinitialisé, c'est pas grave on sait tout recalculer à partir des évents. </p><p>C'est ce recalcul qui présente un piège de temporalité.</p><h3 style="text-align: left;">Le piège</h3><p>De temps en temps on livre de nouvelles versions de l'application. En particulier la fonction evolve, comme ceci</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfQbLkgk3tOJE4wh-QqGSEoZMPZUPVVkWLXruN5EMgCSaqW90yT-cwZZfvJydunEiyeoHHex_Gzu5br1Ku4TH8EneYU0s8XJGc8e0jNVb5LieKv-1e7M6njksDfSnzMDdfnRhwaYg4YAE/s2048/piege+es+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1535" data-original-width="2048" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfQbLkgk3tOJE4wh-QqGSEoZMPZUPVVkWLXruN5EMgCSaqW90yT-cwZZfvJydunEiyeoHHex_Gzu5br1Ku4TH8EneYU0s8XJGc8e0jNVb5LieKv-1e7M6njksDfSnzMDdfnRhwaYg4YAE/w640-h480/piege+es+2.png" width="640" /></a></div><br /><p><br /></p><p>Ici les deux premières itérations d'état ont été calculées avec la première version d'<b>evolve</b> et les deux derniers avec <b>evolve'</b>. Dans le cas d'un recalcul c'est différent car tous les évènements sont processés avec <b>evolve'</b>. Si la dernière version de evolve traite un évent différemment alors il y a une différence, on n'a plus s4 comme état final mais s4'.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1_og6WNo2q5z7EeQhlaituXbyQRNNBtznmhPtgF5UElqBFJzBXKdNXMKZ0N2XmCfnWTNLvqARnFAJjEyNM01ye8y-iRTpNw9nIsf-59ZerbcFV5DQ9cJg1g7UJfJTEGRXQI0dfaYKpY8/s2048/piege+es+3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1535" data-original-width="2048" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1_og6WNo2q5z7EeQhlaituXbyQRNNBtznmhPtgF5UElqBFJzBXKdNXMKZ0N2XmCfnWTNLvqARnFAJjEyNM01ye8y-iRTpNw9nIsf-59ZerbcFV5DQ9cJg1g7UJfJTEGRXQI0dfaYKpY8/w640-h480/piege+es+3.png" width="640" /></a></div><br /><p><br /></p><p>Mais quel est le problème? C'est pas ce qu'on veut? Eh ben pas toujours. Cela pourrait être un bug. Imaginons qu'on calcule la taxe sur des évènements d'achat et qu'à un moment donné la taxe a changé. On veut que ce calcul donne le même résultat qu'il soit fait maintenant ou au fil de l'eau. <b>L'utilisateur ne doit pas être impacté par <i>quand</i> on a calculé l'état. </b>Imaginons l'utilisateur qui a travaillé dur pour attendre un niveau sur StackOverflow. Il serait bien content s'il perd des points suite à une livraison.</p><h3 style="text-align: left;">L'invariant à respecter</h3><p></p><blockquote><i>Une nouvelle version de la fonction evolve doit toujours donner le même résultat pour un événement donné</i>.</blockquote><p></p><p>Si on choisit de le respecter, les états, les caches du système, sont toujours cohérents et n'ont pas besoin d'être recalculés. Par contre dans le cas d'un bug dans la fonction evolve on aurait un état incorrect, alors il est probable qu'on souhaite recalculer l'état pour avoir un résultat différent justement. Dans les deux cas c'est un choix intentionnel, le pire serait de ne pas avoir les idées claires sur ce mécanisme et de se retrouver surpris devant des changements d'état d'apparence aléatoires.</p><p>Mais comment fait-on pour résoudre ce dilemme de ne pas pouvoir changer le comportement actuel, tout en introduisant de nouvelles fonctionnalités ou des changements de règles métier? </p><h3 style="text-align: left;">Les solutions</h3><p>La seule axe de solution, à ma connaissance, se trouve dans la fonction <b>decide</b> qui décide quel événement créer suite à la commande. Elle peut changer, une même commande qui arrive maintenant n'est pas obligé de produire le même événement. Pourquoi donc? Simplement parce que la fonction <b>decide s'exécute toujours au moment de l'arrivée de la commande</b>, jamais de façon rétroactive. Cette fonction décide comment interpréter la commande en créant un événement, qui est persisté. A partir de là je connais trois approches, créer un nouveau type d'évènement, créer un nouvelle version ou se baser sur la date.</p><h4 style="text-align: left;">Un nouveau type d'événement</h4><p>Si on change decide pour émettre une nouvelle version de l'événement alors evolve peut le traiter en cas "special". Puisque ce évènement n'existait pas avant alors on a pas cassé l'invariant. Par exemple pour un achat à 5% de TVA au lieu de 20%, au lieu d'émettre Purchased on peut émettre PurchasedVAT5-5. Toute la nouvelle logique se trouve dans le traitement de ce nouvel événement. On a pas touché à l'ancien car pour un évènement Purchase on applique toujours l'ancien taux.</p><p>Evolve reste ouvert à l'extension de nouveaux types, mais fermé à la modification sur les types existantes</p><h4 style="text-align: left;">Une nouvelle version d'un event existant</h4><p style="text-align: left;">Si on a plus besoin de l'événement d'avant. Genre il n'existe plus de TVA à 20% alors on peut utiliser l'ancienne type. Le fait d'annoter l'événement avec une version +1 permet d'appliquer un traitement différent et donc préserver l'invariant. </p><h4 style="text-align: left;">Se baser sur la date</h4><p>Une autre possibilité est de se baser sur la date. L'invariant nous interdit de changer l'algo pour un même évènement, mais finalement les évènements récentes ont une date différente. Donc il suffit de préserver le fonctionnement pour les évènements antérieures à une certaine date.</p><p>Utiliser une date semble intéressant car c'est légèrement plus facile et c'est peut-être justifié dans certains cas. L'inconvénient est que dans cette approche le code n'exprime pas bien le concept métier qui a changé. La règle métier sera enfouie dans un simple if quelque part. Alors qu'en introduisant un nouveau type cela devient plus visible dans la structure du code, même dans la base de données. Avec un nouveau type, le code communique mieux le métier.</p><h3 style="text-align: left;">Conclusion</h3><p>Pour un changement de fonctionnalité, ajouter un nouvel type/variante d'évènement en changeant <b>decide</b>. Étendre la fonction <b>evolve</b> pour le traiter, sans changer l'existant.</p><p>Une façon de résumer est de dire que </p><p></p><blockquote><i>Tant qu'une nouvelle version de <b>evolve</b> est une extension de la version précédente alors on n'a pas besoin de recalculer les caches du système </i></blockquote><p></p><p>accessoirement on est aussi certain de ne pas surprendre l'utilisateur - il verra toujours un état cohérent avec le passée.</p><p>Bref, c'est un piège qui peut causer bien des maux de tête, mais il est facile de s'en prémunir, dès lors que l'on sait s'en méfier.</p>Merci à Jérémie Chassaing de m'avoir expliqué le problème, et à Arnaud Bailly pour son aide. Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-35001581054638035542020-12-14T14:32:00.001-08:002020-12-15T02:22:54.000-08:00Coder et tester "mieux" avec CQRS et EventSourcing<p>Le titre fait le plein de buzzwords, mais c'est pas pour cela que je l'ai choisi. Non j'ai la conviction et l'expérience que l'architecture basé sur <a href="https://martinfowler.com/bliki/CQRS.html">CQRS</a> - le principe de séparation de commande et de query et le EventSourcing - le fait de calculer l'état de l'application basé sur les évènements passés, facilite les tests automatiques tout comme il facilite l'expression du domaine métier dans le code - le DDD. Je vais essayer d'expliquer pourquoi de manière <b>visuelle</b>. Mais tout d'abord une petite explication sur ce que je veux dire par "mieux" dans le titre de ce billet. Ici mieux veut simplement dire "plus métier", c'est pas toujours "mieux" mais "mieux" fait mieux dans un titre ;) </p><p>Bon revenons aux choses sérieuses, en commençant par un diagramme basique de CQRS implémenté de façon EventSourcing</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaZYFsbw8DKqnMLN6LTzdHgNvVf44OvOeY0pMa543VJdJr5GuzKaeHuajDemD_U75Q4epGRTwh9g-TJ7TpOFxzM86_OHFWNc65Lggow_dQuQoHtOj4Y7fDHPfrpx62C8PCIZxlk6nhyphenhyphen8s/s1712/cqrs+et+eventsourcing.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1712" data-original-width="1584" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaZYFsbw8DKqnMLN6LTzdHgNvVf44OvOeY0pMa543VJdJr5GuzKaeHuajDemD_U75Q4epGRTwh9g-TJ7TpOFxzM86_OHFWNc65Lggow_dQuQoHtOj4Y7fDHPfrpx62C8PCIZxlk6nhyphenhyphen8s/w592-h640/cqrs+et+eventsourcing.png" width="592" /></a></div><br /><h4 style="text-align: left;">CQRS et EventSourcing</h4><p>Lorsqu'une commande arrive, elle se fait valider par le CommandHandler qui la transforme en 0 à n évènements. Par exemple une demande de réservation hotel, peut se solder par une réservation ou non. Les évènements, s'il y en a, se font ajouter à la log des events et servent aussi à entretenir des "projections". Les projections servent avant-tout à répondre rapidement à une question (query), car avec ces projections pré-calculées pas besoin de parser l'ensemble des évènements pour chaque question. A noter tout de même qu'on peut faire sans projection pour les cas qui ne nécessitent que peu de performance.</p><h4 style="text-align: left;">Code orienté métier </h4><p>Mais qu'est-ce qu'il y ait de si fantastique pour les tests et en quoi cela permet de mieux exprimer le domaine? Pour y répondre regardons de plus près les 3 éléments principaux :</p><p></p><ol style="text-align: left;"><li>CommandHandler</li><li>ProjectionHandler</li><li>QueryHandler</li></ol><div><br /></div><div>Le CommandHandler consiste à recevoir la commande, une <b>intention utilisateur</b>, la valider par rapport aux évènements passés et la traduire en quelques évènements <b>du domaine</b>. Bon déjà l'entrée et la sortie sont purement des données du domaine, compréhensibles par n'importe quelle personne du domaine métier.</div><div><br /></div><div>Le ProjectionHandler reçoit des évènements et par rapport à l'état actuel de la projection calcule un nouveau état de projection. Les <b>projections sont</b> souvent <b>des états pré-machés pour l'UI</b> ou pour une autre application, donc proche du besoin métier. </div><div><br /></div><div>Finalement le QueryHandler répond à une requête GET de façon spécialisé (non CRUD) soit en passant une projection directement soit en parsant des évènements <b>métier</b>. Parfois un peu des deux. </div><div><br /></div><div>On voit bien que la logique est très tourné métier et que les interactions avec les bases de données sont faibles. Donc le code est à la fois tourné métier mais aussi peu couplé à la BDD.</div><h4 style="text-align: left;">Fonctions pures</h4><div>Mais ce n'est pas tout. Ce que moi je fais énormément dans le code un peu partout, quelque soit l'architecture en place, est d'<b>isoler des parties de pure logique</b>. En gros avoir des fonctions qui ne font que prendre des valeurs en entrée et qui retournent des valeurs en sortie, rien d'autre. Dans le cas ci-dessus ça donnerait quelque chose comme (la logique pure en jaune)</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7XDnSVWI_bnbNeBK8DkK8TBU5rV0I6qkPX1K59b0I9ry6IZZ1yBh1n7OBj-GgjEjQQv2N5bh7w90oqbZLGHHacneIbpguMoKmdCIjq2uJch6Z8THmXnejLg4mUMIDcShJ8qTMpdKYsVk/s2048/cqrs+et+eventsourcing+avec+logique+pure.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1501" height="926" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7XDnSVWI_bnbNeBK8DkK8TBU5rV0I6qkPX1K59b0I9ry6IZZ1yBh1n7OBj-GgjEjQQv2N5bh7w90oqbZLGHHacneIbpguMoKmdCIjq2uJch6Z8THmXnejLg4mUMIDcShJ8qTMpdKYsVk/w679-h926/cqrs+et+eventsourcing+avec+logique+pure.png" width="679" /></a></div><br /><div><br /></div><p></p><p><br /></p><p><br /></p><p><br /></p><p>Tel des satellites la logique est extraite et il ne reste que le travail de coordonneer appel à la BDD et appel à la logique pure. <b>Presque l'ensemble de la logique métier se trouve alors dans des fonctions pures</b>, toutes bien orientée métier. </p><h4 style="text-align: left;">Les tests</h4><p>Mais alors en quoi cela aide pour les tests? Ben c'est le doux rêve de tout écrivain de test d'avoir des fonctions pures. <b>Pas de dépendances hard-codés, pas de dépendances à injecter, pas de mocks à initialiser. En gros cela donne des tests faciles à écrire et surtout faciles à lire</b>. De plus ces tests ne sont pas trop sensibles à des refactorings, car plus le code est orienté métier et haut niveau plus son <b>API</b> est <b>stable</b>.</p><p>Attention ici je ne décris que les avantages de cette architecture, il y a bien-sur des inconvénients, mais ce sera le sujet d'un autre billet. Peut-être :)</p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-84767242793898041732020-12-06T15:06:00.000-08:002020-12-06T15:06:09.980-08:00Faut-il commenter son code?<div>Parfois on croise des développeurs qui disent qu'il faut toujours commenter son code. Toutes les fonctions. Parfois on croise des personnes qui ne veulent pas de commentaire du tout. Nul part! </div><div><br /></div><div>D'autres personnes ont un avis moins tranché, elles ont déjà vécu des moment où elles auraient pu payer très fort pour avoir un peu de doc, tout comme des fois où la doc il ne fallait surtout pas s'y fier! </div><div><div><br /></div><div>Prenons un peu de hauteur pour voir plus clair. La documentation a ou peut avoir une valeur, mais elle a aussi un coût.</div><div><br /></div><h3 style="text-align: left;">La valeur</h3><div>Les commentaires sont comme toute documentation, elles peuvent apporter de la valeur, parfois beaucoup. Je pense notamment à des documentations d'api publique de style swagger ou la documentation sur les libraries open source. Je pense aussi à des bouts de code bien tordus pour une raison, là une bonne documentation n'a pas de prix.</div><div><br /></div><div>Il y a des contre exemples (plus nombreux) comme par exemple ce code qui n'apporte rien de plus que la signature même de la fonction</div><div><br /></div><div><div> <span style="color: #38761d;"> /**</span></div><div><span style="color: #38761d;"> * @param persons list of persons</span></div><div><span style="color: #38761d;"> * @return number max age of group</span></div><div><span style="color: #38761d;"> */</span></div><div> <span style="color: #0b5394;">function</span> maxAgeOfGroup(persons: <span style="color: #0b5394;">Person</span>[]): <span style="color: #0b5394;">number</span> { </div><div> ... </div><div> }</div><div> </div></div><div><br /></div><div>La valeur de la documentation est en effet très variable. Probablement il s'agit d'une courbe suivant la <a href="https://fr.wikipedia.org/wiki/Loi_de_puissance">loi de puissance</a>. Si c'est bien le cas, alors <b>il y a un faible nombre de commentaires qui apportent une majorité de la valeur</b>.</div><div><br /></div><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrjmhVpmv1O0JMZiJdeMC9v5Pq2NpwYHPP_iNAMk1lvszi1Qzg3fWq2Q2aJYEJH9p3MT3NiEf1p3Rby0GJUfuIps6sES813Uohp3U7yd89OiC6ohRhMvpCegZYjCEl3YKZitIVOiRHetQ/s2048/value+of+code+comments.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1535" data-original-width="2048" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrjmhVpmv1O0JMZiJdeMC9v5Pq2NpwYHPP_iNAMk1lvszi1Qzg3fWq2Q2aJYEJH9p3MT3NiEf1p3Rby0GJUfuIps6sES813Uohp3U7yd89OiC6ohRhMvpCegZYjCEl3YKZitIVOiRHetQ/w640-h480/value+of+code+comments.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">La valeur des commentaires est très variable<br /></td></tr></tbody></table></div><div><br /></div><div><br /></div><h3 style="text-align: left;">Le coût</h3><div>Ce qu'il est facile d'oublier est que toute commentaire vient avec un coût de maintenance. Quand on modifie le code il faut parfois modifier le commentaire. Quand la maintenance n'est pas faite cela se solde potentiellement par un bug, car on s'est fié à un commentaire qui mentait. Plus souvent les développeurs arrêtent tout simplement de le regarder. On arrête les frais. Mais même si on ignore les commentaires et on les laisse là sans les maintenir ils finissent quand même pour demander un coût. Les commentaires prennent de la place sur nos écrans au dépens des lignes des code. Dans un code commenté on voit en effet moins de lignes. Moins de lignes veut parfois dire une vue d'ensemble réduite.</div><div><br /></div></div><div><b>Donc un commentaire qui n'a que peu de valeur, finit par coûter au lieu d'apporter.</b></div><div><br /></div><div>Il faut donc écrire des commentaires avec parcimonie, là où on pense qu'elle sera lue, utile et maintenue.</div><div><br /></div><h3 style="text-align: left;">Quand alors?</h3><div>Je ne sais pas réellement, je ne peux partager que la conclusion que je prend personnellement. Outre la documentation des apis "publiques" qui a une valeur évidente j'essaie de mettre un maximum d'effort à rendre mon code explicite et écrit pour le lecteur. Mais je n'arrive pas toujours bien, alors dans ce cas je documente. </div><div><br /></div><div><b>Je documente quand j'échoue à rendre le code suffisamment explicite</b>.</div><div><br /></div><div>D'ailleurs je conseille vivement le livre <a href="https://www.goodreads.com/en/book/show/34927405-living-documentation">Living Documentation</a> par Cyrille Martraire pour la clarté qu'elle apporte sur ce terrain.</div><div><br /></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-83557283208172814702020-11-05T13:39:00.002-08:002020-11-05T13:39:45.341-08:00Continuous Delivery : What about a database change?<p><br /></p><p><br /></p><p><span style="font-size: medium;">Yesterday I ran a Continuous Deployment workshop in production, namely Continuous Deployment of a<br /> database change. Sounds advanced? </span></p><p><span style="font-size: medium;">Actually it's easy, just counter intuitive...</span><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-VHXSbXSc69thCrR3rNXz_5-BbgdtKhYY8DHGvM69rau1jHG-s4oxp-UIqs7aFYiVg-0-1l0n1XmGcj6aBbljFQ37OuRBjewvbXNkEx9IsPoUao4R9z0GdPPDW0VgY1I0cyYDFyzKv_o/s700/noun_continuous+delivery_484041.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-size: medium;"><img border="0" data-original-height="700" data-original-width="700" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-VHXSbXSc69thCrR3rNXz_5-BbgdtKhYY8DHGvM69rau1jHG-s4oxp-UIqs7aFYiVg-0-1l0n1XmGcj6aBbljFQ37OuRBjewvbXNkEx9IsPoUao4R9z0GdPPDW0VgY1I0cyYDFyzKv_o/s320/noun_continuous+delivery_484041.png" /></span></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><br /></td></tr></tbody></table></p><p><span style="font-size: medium;">It can be really scary to roll out this big feature because it includes a database change. But by splitting it into 3 to 5 chunks it's a lot less scary and can be done with sometimes shorter downtime. Here are the chunks:</span></p><p><span style="font-size: medium;"><br /></span></p><p><span style="font-size: medium;">1st deploy: Add the column/table to the database</span></p><p><span style="font-size: medium;">2nd deploy: fill the new column with values. Check</span></p><p><span style="font-size: medium;">3rd deploy: Rerun migration script while app is down. New code writing to the new column (but not reading). </span></p><p><span style="font-size: medium;">4th deploy: New code or feature flip, to only use the new column both for reads and writes</span></p><p><span style="font-size: medium;">5th deploy: remove old column and old code. (Possibly days later)</span></p><div><br /></div>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-1494028113199603572020-10-04T16:06:00.000-07:002020-10-04T16:06:06.428-07:00Less error handling noice in GO: CombineErrors<h1 style="text-align: left;"><br /></h1>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">TDLR; A nice trick to greatly reduce error handling in Go code.</span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-family: "Helvetica Neue"; font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: "Helvetica Neue"; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD8_YPAFX6G2OBCZP6RbZc8I0C1bFVSDQF_ghXaDWdFPV2rmLBbMNibmaD0OLbucYCujpx5wqaJ8P6zi9UK34DYaulwgnHgeHW9TL1muM3IfbzUsygrbQWyzCPfkadTNh0SbN0yNlyF3I/s1576/Golang.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="890" data-original-width="1576" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD8_YPAFX6G2OBCZP6RbZc8I0C1bFVSDQF_ghXaDWdFPV2rmLBbMNibmaD0OLbucYCujpx5wqaJ8P6zi9UK34DYaulwgnHgeHW9TL1muM3IfbzUsygrbQWyzCPfkadTNh0SbN0yNlyF3I/s320/Golang.png" width="320" /></a></span></div><span style="font-family: "Helvetica Neue"; font-size: medium;"><br />Error handling in go can easily get in the way of readability in Go. Big time! Reducing the verbosity of error handling and in particular the amount of if-statements that invade our application code has been occupying my thoughts for a few months now. </span><span style="font-family: Helvetica Neue; font-size: medium;">I’m appalled that it’s so difficult to find good tricks on this. The go blog has very little information on this. Let's face it I risk GOing nuts!</span><p></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">Some time ago I realised that some errors don’t have to be handled immediately. Postponing error handling opens up a few possibilities. The first and foremost is CombiningErrors. But let’s first look at the problem. Here’s a bit of typical code for obtaining data from somewhere.</span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<span style="font-size: medium;"><script src="https://gist.github.com/martinsson/8df118bbecdd2afe482886aaf8c854a7.js"></script>
</span><p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">Now that’s a short example but imagine this constructor taking a few more arguments.</span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">In this case we’re producing 3 errors. What if we could just combine those three into a single error, that will be nil if all sub-errors are nil, then we could return only one function. Something like </span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<span style="font-size: medium;"><script src="https://gist.github.com/martinsson/fc43ff769a2b7856aac6b103f635fab0.js"></script>
</span><p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">This combining function seems like generic code so there’s probably a library out there doing the job. Indeed there is, for instance <a href="https://godoc.org/go.uber.org/multierr"><span style="color: #dca10d;">https://godoc.org/go.uber.org/multierr</span></a> and <a href="https://godoc.org/github.com/hashicorp/go-multierror"><span style="color: #dca10d;">https://godoc.org/github.com/hashicorp/go-multierror</span></a> </span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;"><span style="font-family: Helvetica Neue;">Now there’s just one small improvement to do here, one that I actually learnt </span></span><span style="font-family: "Helvetica Neue"; font-size: large;">from </span><a href="https://blog.golang.org/error-handling-and-go" style="font-family: "Helvetica Neue";">one of the posts</a><span style="font-family: "Helvetica Neue"; font-size: large;"> at the go blog</span><span style="font-family: Helvetica Neue; font-size: medium;">. There’s no need to check for a combined error with an extra if-statement. We can just return the globalErr, it'll be nil if all sub-errors are nil, and the calling function will test for errors either way.</span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<span style="font-size: medium;"><script src="https://gist.github.com/martinsson/3e3252d0f453ea7d4ee96df5bbd5df99.js"></script>
</span><p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">Much better :)</span></p>
<p style="font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;"><span style="font-family: Helvetica Neue;">Now this is a trick that won't apply in every case. Actually when does it apply? Well I'd argue that this can be used whenever there is no damage done by pursuing execution after a first error. Which in my team's code is quite often. Nice :)</span></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px; min-height: 14px;"><span style="font-size: medium;"><br /></span></p>
<p style="font-family: "Helvetica Neue"; font-stretch: normal; line-height: normal; margin: 0px;"><span style="font-size: medium;">Next time let’s look at another trick where we’ll transform a sequence of error generating statements into a list of functions. Let’s see how that helps.</span></p>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-16270120522789851922020-06-17T17:17:00.000-07:002020-06-17T17:17:18.312-07:00Recorded refactoring of the Untangled Conditionals kataAfter playing around with the Untangled Conditionals kata I thought it'd be fun to publish a video of solving this kata. I share the thought process of how I approach the refactoring of this logic.<br />
<br />
It's always an immense pleasure to remove conditional logic in code, I hope you'll find it fun too!<br />
<br />
The big question is how simple can it get? Guess you'll have to watch it ;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/GHVQqqsSESk/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/GHVQqqsSESk?feature=player_embedded" width="480"></iframe></div>
<br />
Also check out the kata <a href="https://github.com/tomphp/untangled-conditionals-kata">untangled-conditionals-kata</a>. It has the author's, Tom Oram, video of solving it in a different manner in a different language. It's great stuff.<br />
<br />
<br />
<br />
<br />Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-21943730631350316412020-02-12T14:15:00.000-08:002020-02-12T14:16:46.525-08:00Formation en ligne : proposer une pédagogie adaptée<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 0pt;">
<span style="font-family: arial; font-size: 11pt; white-space: pre-wrap;">Depuis novembre 2019, j’anime une formation en ligne de 4 semaines sur la dette technique. Habitué des formations en présentiel, j’ai adopté une nouvelle posture de formateur “facilitateur” dont le rôle est primordial pour guider les apprenants à distance. L’enjeu est de favoriser les interactions, d’éviter l’isolement et le décrochage que l’on observe souvent avec ce type d’apprentissage en ligne. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jusqu’à présent, je pensais que les cours en ligne se limitaient aux MOOCs. Selon moi Internet était l’occasion pour le formateur d’atteindre un très grande nombre de participants avec un investissement de temps de travail raisonnable. Certes les MOOCs sont une réelle opportunité de s’instruire à un moindre coût mais l’apprenant reste relativement “passif” derrière son écran. À l’époque, je n’avais pas encore compris en quoi sous une autre forme l’elearning pouvait rendre l’expérience d’apprentissage encore plus intense même pour un petit groupe.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Cela fait environ 7 ans que j’ai lu “Training from the back of the room”. Cet ouvrage a changé la façon dont j’anime mes formations. À travers ce livre, j’ai compris pourquoi je n’aimais pas les cours magistraux et pourquoi j’avais raison de ne pas les aimer. En effet ce mode d’enseignement est mal adapté à la façon dont, nous les humains, apprenons. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ce livre rend accessible la recherche sur la pédagogie et surtout les avancées depuis le siècle dernier. On y découvre que nous apprenons surtout par nos </span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">actions et interactions</span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">. Il vaut mieux avoir du matériel à disposition, un groupe d’apprenants avec qui échanger, un formateur dont le rôle est de guider et de répondre à nos questions, ni plus ni moins. Ensuite il est indispensable de passer rapidement à la pratique pour bien ancrer les notions apprises pendant le cours. Le formateur est là pour créer cet environnement et il est aussi là pour nous expliquer tout ce qui ne s’invente pas, que nous pourrions difficilement découvrir par nous-mêmes.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">J’ai donc optimisé mes formations en présentiel afin qu’elles exploitent au maximum le temps de </span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">pratique</span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> et qu’elles privilégient </span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">l’échange</span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, soit entre formateur et stagiaires, soit entre stagiaires. Il y a des démos pour ce qui ne s’invente pas, mais une fois passés ces moments obligatoires, chacun a la possibilité d’avancer à peu près à son rythme. J’ai rendu les stagiaires autonomes au maximum, à tel point que je les accompagne tout en les laissant évoluer librement, puisque les exercices et le matériel “se portent” tout seuls. </span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: center;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="border: none; display: inline-block; height: 240px; overflow: hidden; width: 320px;"><img height="240" src="https://lh6.googleusercontent.com/hmywouHR09mYoH5L_dytA4TLY-08J0pgTcLMIBg0nErZ2wnFIx2nyuLZyN10eHqTg-qu3b4upDDIOozVT59mR5qOlV0YF0gim_wZe2pJzDa5-o-NCePjrzHuqcB4OV82TzAG9FQK" style="margin-left: 0px; margin-top: 0px;" width="320" /></span></span></div>
<b id="docs-internal-guid-9b3b44d4-7fff-5c52-71c3-471f4b9a5c55" style="-webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; caret-color: rgb(0, 0, 0); color: black; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-decoration: none; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;"><br /></b>
<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En présentiel, les démos sont les mêmes pour tout le monde, au même rythme. Aussi certains échanges dans le groupe n’intéressent pas tout le monde. Et passé le jour de fin de formation, toutes les explications ne sont plus disponibles.</span></div>
<h2 dir="ltr" style="line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">L’humain et l’interaction personnalisée au coeur de la formation en ligne</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ma vision en faisant une formation en ligne était d’améliorer l’utilisation du temps pour les stagiaires. Lorsqu’on regarde une vidéo, on peut le faire dans les transports, à la vitesse qu’on souhaite. Si on veut la revoir c’est possible, n’importe quand, n’importe où. On peut choisir de ne pas regarder certains sujets qu’on connaît déjà ou qui ne nous concernent pas. Quant aux exercices, on peut prendre le temps que l’on veut pour les terminer. C’est en quelque sorte une consommation “à la carte”, à son rythme.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Dans ma formation en ligne sur la dette technique, j’ai choisi d’ajouter une bonne dose d’interaction avec le groupe à travers le pair-programming à distance. Cet exercice fonctionne très bien avec <a href="https://visualstudio.microsoft.com/services/live-share/">LiveShare</a> ou <a href="https://floobits.com/">Floobits</a>. En complément, les apprenants échangent entre eux et avec moi chaque semaine lors des lives, un groupe slack permet aussi des échanges individualisés ou en groupe sur le code. On y retrouve alors les bénéfices du présentiel, avec les avantages de l’indépendance offert par le format en ligne, tout simplement.</span></div>
<span style="font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">D’ailleurs les retours de la première promo sont très positifs. Si vous voulez découvrir les témoignages des participants ou en savoir plus sur le programme de la formation, je vous invite à aller sur le </span><a href="https://www.supertilt.fr/courses/sortir-de-la-dette-technique-dette-moyenne/" style="text-decoration: none;"><span style="color: #1155cc; font-family: "arial"; font-size: 11pt; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">site de SuperTilt</span></a><span style="font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">. Si vous souhaitez vous former, profitez-en le premier module est gratuit. Après cette première semaine de découverte, vous aurez le choix de poursuivre l’aventure avec nous en rejoignant le groupe des apprenants. </span>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com1tag:blogger.com,1999:blog-7659515555534023244.post-37864242740041458612020-01-28T23:34:00.001-08:002020-01-28T23:34:26.967-08:00Quand faut-il refactorer pour éviter le legacy?<iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/Dj30V50q-Hw" width="560"></iframe>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Quand est-ce que nous devons refactorer le code? Pour ne pas contracter trop de legacy il faut refactorer, on le sait, mais quand? </span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Est-ce qu'il faut faire des stories de refactoring? Est-ce que ce sont des stories "utilisateur"? Ou faut-il le faire plutôt au fil de l'eau? Pour moi la réponse est "majoritairement au fil de l'eau", pendant les stories. Dans cette video on voit pourquoi cette approche est la plus intéressant à mon sens et s'il faut le faire avant, pendant ou après la story. </span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">On verra aussi en quoi ceci peut être contre nature au début car c'est pas dans nos habitudes. </span><br />
<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Toi aussi tu aimes le code propre et le legacy t'embête, voire le fais chier :), viens à mon</span><span style="font-size: large;"> <a href="https://www.supertilt.fr/webinar-la-dette-technique/">webinar sur la dette technique, ce vendredi 31/1</a> et tu pourras poser les questions qui te brulent en direct!</span><br />
<span style="font-size: large;"> </span>
<span style="font-size: large;">T peux aussi jeter un oeuil sur la formation <b>interactive</b> en ligne <a href="https://www.supertilt.fr/courses/sortir-de-la-dette-technique-dette-moyenne/">Clean code, sortir de la dette technique</a>, il y a des chances que ça te plaira autant que les personnes qui l'ont déjà suivi ;o)</span>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-34797072982829814882020-01-06T08:59:00.002-08:002020-01-06T08:59:54.424-08:00Stop feeding the big classes - Class division<span style="font-size: large;">I typically ask people to show me the one or the a few classes that hinder them from writing unit tests. Almost consistently (!) this is a class of 1000 lines or even a few thousand lines of code and this is <b>where most of the development work takes place</b>. Of course this is very difficult to test and even though sometimes these classes are tested with unit tests, they are very very hard to maintain. </span><br />
<span style="font-size: large;"><br /></span>
<h2>
<span style="font-size: x-large;">I wouldn't even try</span></h2>
<span style="font-size: large;">I do admit that it is very hard to write unit tests for these classes. I would find it very difficult to write good unit tests for them too. In fact I wouldn't even try to test this<b> </b>with unit tests. Now what I would do is I would extract the behaviour that I want to modify into other classes or more likely to a completely new class. If I move out a method of say 10 to 50 lines of code then that's very easy to test and things that are easy to test are easy to change by adding behaviour to it. In doing this, besides making things testable, we improve things by splitting things into smaller pieces. <b>Smaller pieces are more easy to understand to test and to assemble into new behaviour i.e. reuse these classes elsewhere</b>. </span><br />
<span style="font-size: large;"><br /></span>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9Rs9mmoSmYc3_VbwO213erg6TFKJNFqAqqF-QqdwZkGxsOxC8ifbrvaY5PQCkbVbO6aMIo2aYWn-2qhhrh72eCg_WWKjYaBR4FlQAYpMCnFHyxSUlVK_n0jyHleQNANN2wg_Zd-OkobA/s1600/10203178726_3e302ea9c3_c.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="font-size: large;"><img border="0" data-original-height="265" data-original-width="800" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9Rs9mmoSmYc3_VbwO213erg6TFKJNFqAqqF-QqdwZkGxsOxC8ifbrvaY5PQCkbVbO6aMIo2aYWn-2qhhrh72eCg_WWKjYaBR4FlQAYpMCnFHyxSUlVK_n0jyHleQNANN2wg_Zd-OkobA/s640/10203178726_3e302ea9c3_c.jpg" width="640" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;">flickr.com/photos/102642344@N02/10203178726/</span></td></tr>
</tbody></table>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Given that we do split the big classes into pieces as we do features then it doesn't take many stories to make our codebase a lot more maintainable and all the way we been able to work with lower level tests. All we needed to ensure is that the things we split apart are <b>meaningful pieces that are functionally coherent</b>. This is probably the most difficult part - extracting something meaningful. It requires being able to design code and if we don't have much practice doing it won't be very good. On the other hand if we don't start practicing this then all code we create will just be a bigger and bigger mess. I mean<b> </b>there's a reason (almost) <b>all living things grow by cell division, not by cell growth.</b></span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">The actual act of extracting something is really simple. Very often it is as simple as</span><br />
<br />
<ol>
<li><span style="font-size: large;">Extract Method (automated refactoring)</span></li>
<li><span style="font-size: large;">Create class and add it as a member of the existing class</span></li>
<li><span style="font-size: large;">Move Method (automated refactoring)</span></li>
</ol>
<br />
<span style="font-size: large;"><br /></span>
<h2>
<span style="font-size: x-large;">The bottom line</span></h2>
<span style="font-size: large;">The next time you touch a big class then split something out, find something meaningful you can move out and test and then change that piece. Done! The modification is safe and it's done rather quickly. We started out with a testability issue, but really it's a design issue regardless of the need to test.</span>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com2tag:blogger.com,1999:blog-7659515555534023244.post-74299175559089826242019-09-05T03:01:00.000-07:002019-09-05T03:01:10.265-07:00 Tester c'est douter, la qualité coûte cher!<br />
Qui n'a pas entendu le dicton <i><b>Tester c'est douter</b></i>. Certes ce dicton reste peu crédible. Bien que c'est une façon s'excuser de ne pas faire des tests sur le ton de la rigolade, personne croit vraiment que cela soit une bonne approche. Par contre <b><i>La qualité coûte cher</i></b> est déjà plus crédible dans l'oreille des gens. Pourtant cela est tout aussi faux.<br />
<br />
Faux? Ah bon, vraiment? La qualité ne coûterait pas cher?<br />
Non. Je dirais que c'est la non-qualité qui coûte cher. Et ce même à court terme! Visiblement, au regard des projets que je vois passer, tout le monde ne partage pas mon avis et pense que cela vient en partie d'un regard différent que l'on porte sur nos projets;<br />
<br />
<h4>
D'abord dev puis test</h4>
Si on aperçoit le coût de dev séparément de sa validation, si on divise l'écriture du code et l'écriture des tests une fois le code fini, on va voir un coût de dev. Puis un coût d'écriture des tests. Puis le coût de validation manuelle. Dans ce monde la qualité a un coût très mesurable, c'est déjà l'ensemble de l'écriture des tests. C'est aussi tout éventuel refactoring énoncé lors du dev. Dans ce système on cherchera naturellement à réduire les coûts qui n'ont pas un intérêt évident. Soit en caricaturant faire le dev vite fait mal fait, sauter les tests au profit d'une autre story. Certes on garde les tests manuels car sinon il y aurait trop de bugs en prod.<br />
<br />
<h4>
Test au fil de l'eau (~TDD)</h4>
Si au contraire on rend visible le <b>temps passé entre début de dev et le tampon de validation de la part du PO et des testeurs</b>, voire jusqu'au stabilisation en prod pour les plus modernes des chaines de livraison continue. Alors une méthode de dev qui supprime les retours des testeurs et PO deviendra beaucoup plus intéressante. En effet le fait de spécifier à travers des exemples deviendra naturelle pour éviter les erreurs d'interprétation, ces exemples deviennent au fur et au mesure du dev des tests automatisés et la validation manuelle après-dev se réduira au stricte minimum. Il n'y a plus d'erreurs de compréhension à détecter à la fin, car elles sont toutes relevées en amont du moindre ligne de code.<br />
<br />
Dans le premier mode de travail la qualité, en termes d'écriture de tests après dev, a un coût difficile à rentabiliser. Dans le deuxième cas les tests sont naturelles et rentabilisés par conception de mode de travail.<br />
<br />
<b>C'est notre regard sur le système qui dictera le façon que nous l'optimiserons. Si nous voulons un changement de fonctionnement il faut trouver un nouveau regard</b>.<br />
<br />Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-80129730699873359412019-05-03T04:28:00.003-07:002019-05-03T04:32:29.250-07:00Mob-programming, l'histoire d'une équipe<span style="font-size: 10pt;"><span style="color: #1a1a1a; font-family: "arial";"><span style="caret-color: rgb(26, 26, 26); font-weight: normal; white-space: pre-wrap;">J'ai eu la chance d'interviewer une équipe qui fait du mob-programming pour le travail quotidien. </span></span><span style="color: #1a1a1a; font-family: "arial"; font-weight: normal; white-space: pre-wrap;">Personnellement j'en ai fait souvent lors des coding-dojos, mais jamais pendant une longue durée dans un projet, c'était donc pour moi une excellente opportunité de bénéficier de leur expérience! Comme j'ai adoré ce petit échange d'une heure avec cette équipe très épanoui et fier de leur travail, je vous partage </span><span style="color: #1a1a1a; font-family: "arial"; font-weight: normal; white-space: pre-wrap;">cher lecteur</span><span style="color: #1a1a1a; font-family: "arial"; font-weight: normal; white-space: pre-wrap;"> une version condensé en espérant que vous trouverez cela aussi utile que moi.</span></span><br />
<span style="color: #1a1a1a; font-family: "arial"; font-size: x-small; font-weight: normal; white-space: pre-wrap;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcy2hH6CquB646_WhaEi9p8izcKDHfLIaXv72kLqHU5UxXTob1t6Y83sf7faWA8aZLFhMNP8_84tmwQcSblskLHap10btNT2skLBfV3w3rWXyyv-V1rbFyApuarBjKIkM_Ni5-gR5hKpk/s1600/PART_1556789481484_IMG_09461.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcy2hH6CquB646_WhaEi9p8izcKDHfLIaXv72kLqHU5UxXTob1t6Y83sf7faWA8aZLFhMNP8_84tmwQcSblskLHap10btNT2skLBfV3w3rWXyyv-V1rbFyApuarBjKIkM_Ni5-gR5hKpk/s640/PART_1556789481484_IMG_09461.jpg" width="640" /></a></div>
<div>
<span style="color: #1a1a1a; font-family: "arial"; font-size: x-small; font-weight: normal; white-space: pre-wrap;"><br /></span></div>
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Comment cela a-t-il commencé?</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Au début il y a avait qu'un développeur, et avec l'arrivée de deux autres on voulait essayer le mob rien qu'en mode apprentissage. On pouvait ainsi continuer à livrer tout en enseignant l'environnement technique et fonctionnel. "C'est bien plus agréable que de lire des docs" Remarque l'un des développeurs!</span><br />
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">C'était très progressif, mais au bout de quoi, peut-être 3 semaines les nouveaux étaient opérationnels et participaient à pied d'égalité. Cela fait environ 9 mois maintenant et depuis nous avons intégré une nouvelle personne. La nouvelle personne a aussi beaucoup apprécié. </span><br />
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Cette nouvelle personne connaissant un peu moins la stack et les méthodes de développement (souvent du TDD) il a beaucoup apprécié le mob et notamment le fait de forcer le passage au clavier, pendant 7-10 minutes à la fois. Quand on est obligé comme ça on est obligé de s'accrocher et on progresse très très vite.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Il a été naturel de continuer le mob même après cette phase de montée en compétences</span></div>
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Justement, je suis curieux, faites-vous du mob tout le temps?</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Non, on travaille aussi souvent en pair-programming, et même tout seul parfois. C'est assez naturel de switcher entre l'un et l'autre.</span></div>
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Qu'est-ce qui fait que vous choisissez le mob ou le pair ou le solo.</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Le mob c'est lorsqu'il y a un peu de complexité, quand on sent qu'il y aura pas mal de discussion. Si quelque chose est très clair, qu'il n'y a pas trop de questions à se poser on va le faire seul ou à deux. Le choix se fait assez naturellement.</span></div>
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Quels sont d'après vous les bénéfices du mob?</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Le code est bien meilleur en mob, on va tout de suite faire une remarque qui va être pris en compte sans perte de temps. Souvent c'est des choses qu'on aura même pas pris la peine de remonter lors d'un code-review.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Parfois on s'est retrouvé à même annuler une story suite à une discussion survenue dans le mob. C'était pas si facile de gérer ceci dans le code et c'était discutable d'un point de vue métier. Si quelqu'un l'avait fait seul on peur qu'il aurait juste foncé sans se poser ce genre de question.</span></div>
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><< Et là le PO a très envie d'intervenir :) >></span></h2>
<span style="color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-weight: 400; white-space: pre-wrap;">- Oui le truc le plus génial c'est que maintenant le dev plutôt js se retrouve à faire un peu d'embarqué (seul) et du coup je n'ai plus à planifier un petit peu de ceci et un peu de cela pour que tout le monde soit occupé dans l'équipe. Je peux me soucier de ce qu'il faut fonctionnellement!</span><span style="color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-weight: 400; white-space: pre-wrap;"><br /></span><br />
<span style="color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-weight: 400; white-space: pre-wrap;">- Effectivement ça doit faciliter les absences aussi, quelque part ça crée automatiquement une équipe pluri-disciplinaire</span><span style="color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-weight: 400; white-space: pre-wrap;"><br /></span><br />
<span style="color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-weight: 400; white-space: pre-wrap;">- Exactement!</span><br />
<h2 dir="ltr" style="line-height: 1.7142857142857142; margin-bottom: 4pt; margin-top: 18pt; text-align: left;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 17pt; font-style: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Est-ce qu'il y a eu des tensions à gérer?</span></h2>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Oui, d'un point de vue matériel une fois qu'on était lancés, on s'est rendu compte qu'il fallait acheter un grand écran de qualité et on a fait des modifications à un bureau pour qu'il n'y ait pas de gêne pour s'approcher, genre des box à tiroirs.</span><br />
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sinon dans l'équipe il y a beaucoup de bienveillance donc il n'y a jamais de problème de ce point de vue là. Par contre c'est assez intense et après une journée de mob on sent qu'on a travaillé! On n'a pas de pauses programmés sauf un peu le matin et en début d'après-midi et ça marche bien comme ça. Si quelqu'un n'est pas à fond tout le temps ce n'est pas très grave non plus.</span></div>
<span id="docs-internal-guid-b34afcc0-7fff-8dd2-8558-305e79315750"></span><br class="Apple-interchange-newline" />
<br />
---<br />
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: #1a1a1a; font-family: "arial"; font-size: 10pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Merci beaucoup à <a href="https://twitter.com/la_urre">Damien</a>, Romain, Sylvain, Benjamin et <a href="https://twitter.com/vincent_loustau">Vincent</a> d'avoir partagé toute cette expérience. J'ai pas mal pratiqué dans des dojos, mais je n'ai pas réussi à mettre en place le mob à ce point dans un projet et ce genre de retour m'aide. Que vous continuez à prendre autant de plaisir dans le travail!</span></div>
<span id="docs-internal-guid-de45f738-7fff-7f91-b93f-4599640b1155"><br /></span>Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com1tag:blogger.com,1999:blog-7659515555534023244.post-26412678503434425092019-04-02T16:26:00.000-07:002019-04-02T16:26:31.765-07:00In cognitive psychology is refactoring a cost or a benefit?<br />
Did you ever get frustrated when someone refactored your code? Or did you refactor someones code just to find out that the original developer wasn't all too happy about it? This certainly happened to me a few times. Here's my understanding of what's happening from the point of view of cognitive psychology.<br />
<br />
In cognitive psychology there's the term Long-Term Memory (LTM), which is roughly what we usually call just <i>memory</i>. What's interesting with our <b>LTM is</b> that it's <b>optimised for storing large amounts of data, but not for changing existing data</b>.<br />
<br />
When we refactor some code we're forcing the developers knowing the code to rewrite their long-term memory. Remember that LTM is made to store a lot but not to be updated. That's a cost we're imposing on the original author. Sure the intention is to make future features cheaper to implement, but that's still a certain cost vs a potential benefit, later.<br />
<br />
Given this we can better understand the frustration that a developer might feel when his/her code is thoroughly refactored. The bigger the change the higher the cost for the developers who know the code. In particular if the only motivation for changing the code is to make it cleaner or better, then that frustration might well be justified. After all better for whom? Certainly not for the original developer!<br />
<br />
On the other hand whenever we have to evolve the code we're forced to rewrite the code and the corresponding LTM anyway, and if the code is not very well decoupled then it is quite unclear what part of our mental model of the code has to be rewritten. Should our brain just hang on to the original image or replace it or a part of it? The resulting LTM rewrite is probably imprecise, or requires more effort. The more complex the code, the harder it is to remodel and if it contains accidental complexity, we pay accidental remodelling cost. In short, with bad code that keeps changing, it's expensive to maintain a good mental model of the code in our long term memory.<br />
<br />
If instead it was implemented respecting the principles of <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">Single Responsibility Principle</a> (small cohesive parts) and <a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle">Open-Closed Principle</a> (for new features code is added, instead of changed) then for any change in functionality we'd only have to swap one well identified part of the code. It becomes clear to our brain which part has to be rewritten in LTM, hence the cost of a rewrite is low. By respecting the principles of SRP and OCP we minimise the cost of maintenance in our LTM.<br />
<br />
<h3>
Conclusion and disclaimer</h3>
<br />
Cognitive psychology tells us there's an additional cost of refactoring in the form of (cache) invalidation of our mental model of the code. Of course this doesn't mean we should avoid refactoring as it also tells us that we need well factored code in order to minimise the cost as new features get added. By understanding the forces at play we can better choose when and what to refactor.<br />
<br />
I'm nowadays very wary of refactoring code that doesn't need new behaviour. It hasn't always been so, indeed for several years I happily refactored code just because I could. Learning the basics of cognitive psychology has thought me to concentrate refactoring efforts to whenever I need to change the code anyway.<br />
<br />
As a last word, there are valid reasons for refactoring code without a need for change. For instance for readability: naming things, extracting methods. Another good example is removing accidental complexity.<br />
<br />
Would you like to hear more about this? Like how our LTM is feed through Chunking and Short Term Memory and how to optimise for that?Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com2tag:blogger.com,1999:blog-7659515555534023244.post-61652731000504975202019-01-03T07:16:00.002-08:002019-12-10T16:30:27.149-08:00Testability trick - Replace static initialiser with lazy initialiserA simple 4-step refactoring to make code more testable.<br />
<br />
Static initialisers are a major hurdle for testing. If you have a static initialiser that for instance performs a request to the database then you cannot access that class in a unit test. Let alone mock it. In fact only importing this class will execute the query. So even if you're trying to test some other class, you cannot, since the offending class is contaminating every import of it with non testability. You want to make sure to remove the static initialiser so that other classes can be tested in isolation.<br />
<br />
<script src="https://gist.github.com/martinsson/e59d8cb1ec9070453429071b5ef11d57.js"></script>
<br />
The solution is to replace the static initialiser with a lazy initialiser. This will allow you to load the class for mocking. You'll probably have to also replace a static method by an instance method in order to avoid calling the real method and so properly test classes that are calling the offending class, but first things first.<br />
<br />
Assuming that your static initialiser is initialising a static field of your class:<br />
<ol>
<li><b>Encapsulate the access to the field with a simple getter</b></li>
<li><b>Use the *Extract Method* refactoring to extract all the code from the static initialiser</b></li>
<li><b>In the getter initialise the field *if it is null* by calling the extracted method </b></li>
<li><b>Delete the static initialiser</b></li>
</ol>
Yep it's that simple! :o)<br />
<br />
Your next step will likely be to replace the public methods using this getter by instance methods so that those can be tested (<a href="https://gist.github.com/martinsson/786068e4ca243ffdc48be2a2a47eb99b">example</a>).<br />
<br />
Let's have a look at an example of the code before refactoring<br />
<br />
<script src="https://gist.github.com/martinsson/c1371d6f2b872b5aeba71f98f48bfb77.js"></script>
And now here's the refactored code.<br />
<br />
<script src="https://gist.github.com/martinsson/2cad7ef64132755e254b8daf615cc5ef.js"></script>
<br />
Having "Replaced static initialiser with lazy initialiser" we're almost there. We can safely import this class, but we can't yet mock the static method. In order to make it mockable we can simply add an instance method as a proxy to the static method, then use the <a href="https://gist.github.com/martinsson/786068e4ca243ffdc48be2a2a47eb99b">instance method instead of the static method</a> .<br />
<br />
<br />Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com1tag:blogger.com,1999:blog-7659515555534023244.post-90838166992569737662018-12-11T08:30:00.002-08:002018-12-11T08:30:25.821-08:00Continous delivery predicts organisational performanceThis spring Nicole Forsgren, Jez Humble and Gene Kim published a vulgarisation of their four-year research endeavour into the impacts of Continuous delivery on the organisation. The book is called <a href="https://itrevolution.com/book/accelerate/">Accelerate - the IT revolution</a>. Starting with intuition based on years of first hand experience they took on the task of formalising their assumptions to be able to test using research based on surveys. It turns out that many of the assumptions were correct but there were a few surprises.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbjwVtrAGoxRCnD-PwV_w9Gj3FqybGOoTlMc3SeE-Z37YTqRVHwdk6c3aV-e1BatK6rA02GXVqFW3d0nNvEKNZB3sSW-_xiTJi0hjj6ezGtivOkzS9SNlRJjP_zH_OeeOayY5wDlfGjJ8/s1600/continuous+delivery.png" imageanchor="1"><img border="0" height="307" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbjwVtrAGoxRCnD-PwV_w9Gj3FqybGOoTlMc3SeE-Z37YTqRVHwdk6c3aV-e1BatK6rA02GXVqFW3d0nNvEKNZB3sSW-_xiTJi0hjj6ezGtivOkzS9SNlRJjP_zH_OeeOayY5wDlfGjJ8/s640/continuous+delivery.png" width="640" /></a><br />
<br />
<h3>
Continuous delivery predicts organisational performance</h3>
The baseline is that <b>Continuous delivery gives</b> software delivery performance, which in turn is a predictor for <b>organisational performance</b>. This is a key finding. It gives us confidence that we're very likely to get substantial bottom-line benefits if we invest into improving continuous delivery.<br />
<br />
There are a few other benefits established by the research team.<br />
Continuous delivery also reduces the amount of <b>rework</b> that has to be done, in fact high performers spend as much as 22% more time on New work when compared to low performers. Interestingly they also spend less time in meetings.<br />
<br />
Continuous delivery also has a good impact on <b>identity</b>, meaning that people identify with their company. It also gives us <b>less burnout</b> and <b>less deployment pain</b>. Not surprisingly it also has a great impact on <b>job satisfaction</b><br />
<br />
Continuous delivery also puts pressure on the organisation to become more healthy with regard to how information flow between people and departments. They use the concept of <a href="https://continuousdelivery.com/implementing/culture/">Westrum organisational culture</a>. In this sense it acts as a regulator towards the enterprise gangrene of power-struggle between managers.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0BWRLHBq8PiYSRbreeIN2L2wUalNHf2pwu5b4s2DYdLzji5f_aXrcad61WFSPtinDFFBeKL6juuneTmE0DLAcsmqgyUhbz8oWSTKYewM_1eD6pOtkl9PkumVgjCVIQC70XU3fZ-SzDnI/s1600/Continuous+delivery+tree.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0BWRLHBq8PiYSRbreeIN2L2wUalNHf2pwu5b4s2DYdLzji5f_aXrcad61WFSPtinDFFBeKL6juuneTmE0DLAcsmqgyUhbz8oWSTKYewM_1eD6pOtkl9PkumVgjCVIQC70XU3fZ-SzDnI/s640/Continuous+delivery+tree.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fruit you can expect when you nurture the roots of the continuous delivery tree</td></tr>
</tbody></table>
<br />
But let's have a look at the key finding, and what they mean by Continuous delivery, Software delivery performance and Organisational performance.<br />
<br />
<h3>
Continuous delivery</h3>
This is the practice of teams independently delivering into production safely, quickly and sustainably. The key principles here are<br />
<ul>
<li>Quality inside</li>
<li>Small batches</li>
<li>Computers do repetitive stuff, people solve problems</li>
<li>Continuous improvement</li>
<li>Everyone responsible (for the global outcome)</li>
</ul>
<br />
The concrete practices that have been found to have a strong impact on the level of continuous delivery are<br />
<br />
<ul>
<li>Version Control</li>
<ul>
<li>Including configuration data and deployment tooling</li>
</ul>
<li>Deployment automation</li>
<li>Continuous integration</li>
<li>Trunk-Based development</li>
<ul>
<li>Branches are very, very short-lived</li>
</ul>
<li>Test automation</li>
<ul>
<li>Including that the majority of tests can easily be run on a local dev machine</li>
</ul>
<li>Test data management</li>
<li>Shift left on security</li>
<ul>
<li>security is built in as features are developed, as supposed to later.</li>
</ul>
<li>Loosely coupled architecture</li>
<li>Empowered teams</li>
<ul>
<li>Teams make most decisions without outside approval</li>
</ul>
<li>Monitoring</li>
<li>Proactive notification</li>
</ul>
<div>
<br /></div>
<h3>
Software delivery performance</h3>
This means that we go quickly from idea to production which is good for minimising time to market. That we deploy frequently, which is good for developing just enough and have less features that are rarely or never used. That whenever we do introduce a bug or some down time it is quickly restored and finally how often we deliver bugs.<br />
<br />
<ul>
<li>Lead time</li>
<li>Deployment frequency</li>
<li>Mean time to restore</li>
<li>Change fail percentage</li>
</ul>
<br />
<h3>
Organisational performance</h3>
This the basic investors KPI, how profitable is the company, how productive is it and how big a market share does it have. Basically what we need to motivate top management.<br />
<br />
<h3>
Research methods</h3>
I still have some questions as to how they distinguish between correlation and causation. While they do explain this in the book, my knowledge in statistics and research methods does not allow me to judge their explanation<br />
<br />
<h3>
Conclusion</h3>
<b>Scientific findings establish a predictive relationship from continuous delivery to organisational performance</b>. Going towards continuous delivery you can expect to get organisational performance. In particular any "Continuous delivery" transformation could use this as an overarching goal, as it is both likely to succeed and <b>focuses on outcomes, rather than practices</b>. You also get happier employees which in turn will fuel the organisational performance.<br />
<br />
<br />
<br />
<br />Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-34657630666219116482018-10-09T04:48:00.000-07:002018-10-09T04:48:38.491-07:00<h3 class="graf graf--h3" name="20bc">
Obtaining time for training and going to conferences</h3>
<div class="graf graf--p" name="dd32">
I know some employees that’d like to train more, to go to conferences, present at conferences and train during working hours. Few employers allow that to the extent that the employee would want to, some don’t even feel entitled to order books on behalf of their company. This generates frustration, and to me inefficiency at work, because developers need to continuously learn a lot and learn with a wider perspective than only the working environment can ever offer.</div>
<br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="en">
In an ideal world, companies would take responsibility for continuously training every employee.<br /><br />Welcome to the real world.<br /><br />If you want to improve, you’ve got to take responsibility for your future and put in the work.</div>
— John Ⓐ De Goes (@jdegoes) <a href="https://twitter.com/jdegoes/status/1047532557892349952?ref_src=twsrc%5Etfw">October 3, 2018</a></blockquote>
<script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>
<br />
<div class="graf graf--p" name="58a8">
I’ve seen this tension and frustration in just about every company, from high profile consulting agencies where training is encouraged and expected, still one or two individuals would prefer to spend even more time training and preparing conference presentations, to some low profile consulting agencies where one boss explained to me: </div>
<blockquote class="tr_bq">
<i>I have a problem with letting people train a lot. The company makes an investment through training fees and non billed time. The return on investment is negative, because the more they learn the greater the salary they can get elsewhere, but I can’t pay them much more because developer fees are limited. No matter the skill level I can’t charge above a certain level, so <b>the more I invest the sooner they’ll leave</b></i></blockquote>
<div class="graf graf--p" name="15e5">
Now that’s sad, really really sad, but what would you do if this was your company?</div>
<div class="graf graf--p" name="8709">
Interestingly company owners never have a problem with their personal balance, neither do freelance developers. Why? It’s not because they’re special people in any sense. It’s because they are in a self regulating system. The more they train the less they "<i>produce"</i> in the short term, training is an "<i>investment</i>" in the future and they both reap the benefits and pay the bill. The system will balance without authoritarian decision and consequent frustration.</div>
<br />
<br />
<figure class="graf graf--figure" name="1a37"><img class="graf-image" data-height="474" data-image-id="1*VfapMhU8Os4HphmwuXeqKw.jpeg" data-width="800" height="379" src="https://cdn-images-1.medium.com/max/1600/1*VfapMhU8Os4HphmwuXeqKw.jpeg" width="640" /><figcaption class="imageCaption"><a class="markup--anchor markup--figure-anchor" data-href="https://pxhere.com/en/photo/1435901" href="https://pxhere.com/en/photo/1435901" rel="nofollow noopener" target="_blank">https://pxhere.com/en/photo/1435901</a></figcaption></figure><br />
<div class="graf graf--p" name="51f8">
Now I have a bold idea both for business owners and for employees negotiating a new job. It came to me several years ago when a client wanted to hire me as an employee instead of as a contractor. The job was interesting so I asked myself what do I value as a freelance, what exactly do I like about it? It became very clear to me that I liked to have the freedom to decide. In particular what conferences I’d go to, what books I’d buy, when I needed to be away from work to train. So I stated <b>my conditions: 25 days a year of training and going to and/or speaking at conferences with a budget of max 10k€</b> on training courses and travel expenses. After that we could negotiate salary, making it very easy for the future employer to calculate the total cost, salary, expenses, non-project time etc and compare it to the benefits of my activities. Naturally they had never encountered a demand like it before, nevertheless it was easily accepted. While I never ended up working with them for other reasons, I was confident that I wouldn’t have felt feel frustrated had I ended up working there.</div>
<div class="graf graf--p" name="71a4">
<br />
As you can see I value time for training a lot, but not everyone does to that extent and that’s perfectly OK. We’re not all the same, some people prefer day to day work because lets face it it can be very interesting, and some value maximising salary. The point here is to give the opportunity to everyone to choose how much they’re investing in the future and how much we’re cashing out now.</div>
<div class="graf graf--p" name="049f">
Using this clear deal of clearly reserving the expected time and budget for knowledge development, people can even use companies like the low profile consulting agency as trampolines for their career, accepting lower salary in return for greater investment, a productive partnership.</div>
<div class="graf graf--p" name="c9bc">
In short when an employee goes to training or prepare a presentation the company benefits a lot from it, but so does the employee. <b>When benefits are mutual so should cost be, otherwise heavy investment is not sustainable</b>. So following this idea of shared costs, almost anyone can find a good balance with almost any company.<br />
<br /></div>
<div class="graf graf--p" name="ad8b">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://cdn-images-1.medium.com/max/1600/1*8RELO052VYaDJnyCf1NTWg.jpeg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" class="graf-image" data-height="1285" data-image-id="1*8RELO052VYaDJnyCf1NTWg.jpeg" data-width="830" height="400" src="https://cdn-images-1.medium.com/max/1600/1*8RELO052VYaDJnyCf1NTWg.jpeg" width="257" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">www.publicdomainpictures.net/view-image.php?image=136005</td></tr>
</tbody></table>
Possibly there’s even more value to it. Some people haven’t quite internalised that we have to learn more than we can learn by daily work. And I think it can help them, as well as the organization as a whole, to ask people during the hiring process</div>
<blockquote class="graf graf--blockquote" name="e06d">
<i>How much training etc would suit you? We will provide a minimum, but we can also help you if you’d like more.</i></blockquote>
<div class="graf graf--p" name="5f1a">
I suspect such a question could help people think about it and take responsibility for their future development.</div>
<br />
<figure class="graf graf--figure" name="8766"></figure><br />
<div class="graf graf--p" name="911c">
What do you think? Have you tried something similar? If you know of people and companies who does this or would like to try it yourself, I’d love to hear about it!</div>
Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0tag:blogger.com,1999:blog-7659515555534023244.post-48661417425421956112018-07-04T01:45:00.003-07:002018-07-05T01:28:46.859-07:00Type Driven Development pattern: Small types<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFkvVRCDHR8kzQjLSVF3APz8v4KX7Ly2BTxLJykhiCiTJ-T4XAQ4wCqDZgT_vubDKCbijHhHVkW0xuZwudXElwPLj2nRbPTcAuyi0GaEo7wX07u8csExD3txTA3LC_xHXGTy3rXxlW_p0/s1600/lego.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFkvVRCDHR8kzQjLSVF3APz8v4KX7Ly2BTxLJykhiCiTJ-T4XAQ4wCqDZgT_vubDKCbijHhHVkW0xuZwudXElwPLj2nRbPTcAuyi0GaEo7wX07u8csExD3txTA3LC_xHXGTy3rXxlW_p0/s640/lego.jpg" width="640" /></a></div>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">TLDR; Small types in type driven development facilitates composability much like small objects do in OO. By small types I mean few constructors and little data in each.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">One of my first experiences in <a href="https://www.idris-lang.org/">Idris</a> was to try to implement a FizzBuzz where the types would be so strong that they'd ensure the code could only be correctly implemented.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><a href="https://en.wikipedia.org/wiki/Fizz_buzz">FizzBuzz</a> is a game where the input is a number between 1 and 100. The output is a string, "Fizz" if the number is divisible by 3. "Buzz" if the number is divisible by 5. "FizzBuzz" if it is both divisible by 3 and 5 and just the number otherwise. eg counting from 1</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;"><span class="Apple-tab-span" style="white-space: pre;"> </span>1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, ...</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">My first implemention enforces the correctness by taking as arguments <i>proofs</i> that the given number is either divisible by 3 or a <i>proof</i> that it is not divisible. That way can call the <i>constructor</i> Fizz if and only if you can provide proof of division by 3 and a proof of non division by 5.</span><br />
<span style="font-size: large;"><br /></span>
<script src="https://gist.github.com/martinsson/2ad793d63dbc825e87b0423d6876bfc2.js"></script><span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">So just by defining a type FizzBuzz and the signatures of the constructors of this type we have no possibility of making a mistake in the implementation. Nice! Here's are the types and signature of methods.</span><br />
<span style="font-size: large;"><br /></span>
<script src="https://gist.github.com/martinsson/1aa9184ecf451445d6bfc69634bfdd65.js"></script><span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">IF you're curious about the implementation <a href="https://github.com/martinsson/DDD-and-Dependent-Types/blob/master/fizzbuzz_johan.idr">it's here</a></span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Besides being a major challenge, for me, in mastering dependent types and understanding that types can act as proofs, it actually worked out quite OK. Certainly it does not allow for incorrect code, so it was a successful experience. Sure it is a bit of verbose and contains a bit of pseudo-duplication.</span><br />
<span style="font-size: large;"><br /></span>
<b><span style="font-size: large;">But not extensible</span></b><br />
<span style="font-size: large;">Now there's an extension to FizzBuzz, namely Bang. Bang is the word for every multiple of 7, for instance given the number 21 we should return FizzBang. Now without Bang there are 4 possible results, Fizz, Buzz, FizzBuzz and the number. Now here's where things become interesting because with Bang there are 8, so the problem of verbosity and pseudo-duplication gets a lot worse. Clearly the first solution isn't very extensible</span><br />
<span style="font-size: large;"><br /></span>
<b><span style="font-size: large;">A better solution</span></b><br />
<span style="font-size: large;">A different approach is to decompose the type FizzBuzz into smaller orthogonal types. Lets consider the type Fizz (either a IsFizz or a Not IsFizz), the type Buzz, then FizzBuzz can be modeled as a tuple (pair) of Fizz an Buzz (<a href="https://github.com/martinsson/DDD-and-Dependent-Types/blob/master/fizzbuzz-composable.idr">the full code</a>).</span><br />
<span class="Apple-tab-span" style="font-size: large; white-space: pre;"> </span><br />
<script src="https://gist.github.com/martinsson/db2be0d5803cfecd2e2e99e400c9cff4.js"></script><span style="font-size: large;"><br /></span>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">If we add the functionality of Bang then all we have to do is create a new type Bang, and a new composed type a 3-tuple of Fizz, Buzz and Bang. We don't have to break the existing type FizzBuzz nor the former function, we could possibly even keep the former method, a good example of the open-closed principle. Open-closed-principle with dependently typed code!</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Possibly we'd fear that by making the types less <i>closed</i> we'd be able to code an incorrect solution to fizzbuzz. But that's still not possible.</span><br />
<span style="font-size: large;"><br /></span>
<b><span style="font-size: large;">Learnings</span></b><br />
<span style="font-size: large;">Keeping types small in the sense that they <b>contain little data</b> (few arguments) and have <b>few constructors</b>, favours reuse through composition. It might seem obvious in hindsight, but I discovered this much in the same way that I discovered the use of small objects in OO.</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Some people say that strongly typed languages like Idris doesn't allow for extension, that they'd be <i>closed</i> by design. What I learned from this experience is that while that certainly applies to <b>a</b> type and functions on existing types, we can still compose types in new ways. Given of course that our types are small enough.</span><br />
<div>
<br /></div>
Johan Martinssonhttp://www.blogger.com/profile/06338139663667581308noreply@blogger.com0