<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nicolas Carlo</title>
    <description>The latest articles on DEV Community by Nicolas Carlo (@nicoespeon).</description>
    <link>https://dev.to/nicoespeon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F128004%2F2f4f315a-401a-43ec-8fb2-222ec3f79cad.png</url>
      <title>DEV Community: Nicolas Carlo</title>
      <link>https://dev.to/nicoespeon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nicoespeon"/>
    <language>en</language>
    <item>
      <title>Can AI help me write tests on legacy code?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Thu, 09 Feb 2023 14:26:49 +0000</pubDate>
      <link>https://dev.to/nicoespeon/can-ai-help-me-write-tests-on-legacy-code-396o</link>
      <guid>https://dev.to/nicoespeon/can-ai-help-me-write-tests-on-legacy-code-396o</guid>
      <description>&lt;p&gt;AI has become a hot topic in the past few months. Tools like &lt;a href="https://chat.openai.com/chat"&gt;ChatGPT&lt;/a&gt; have been released for the world to play with, and this has raised a lot of interesting questions about how it will change the way we work.&lt;/p&gt;

&lt;p&gt;My interest is piqued when it comes to &lt;strong&gt;legacy code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A lot of us are working on existing, poorly documented, untested software that we have to change. Evolving this code without breaking things is a daily challenge. Would AI does that job better? I would actually be &lt;em&gt;excited&lt;/em&gt; if tools could do some of the grunt work, so I could spend more time understanding the Problem that is being solved instead of spending so long on the implementation details of the Solution that I need to change.&lt;/p&gt;

&lt;p&gt;However, I’m &lt;em&gt;suspicious&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/blog/ai-test-generation-for-legacy-code/"&gt;I've already wrote about this topic&lt;/a&gt; and my experience wasn't great then. The generated tests weren't really helpful. I was mostly wasting time.&lt;/p&gt;

&lt;p&gt;AI makes mistakes. Yet, it sounds confident. If it doesn’t know, it may just make things up. If I don’t know what the code is doing, how can I hope to detect the lies?&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT vs. Rubberduck
&lt;/h2&gt;

&lt;p&gt;That being said, I’ve also played with ChatGPT. I gave it slices of code and asked it to simplify it, refactor it with patterns, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GUULJFpR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r194dpd6nxpjft8b4c42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GUULJFpR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r194dpd6nxpjft8b4c42.png" alt="Asking ChatGPT to refactor some code with guard clauses" width="800" height="923"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works… but not always. I’ve found it creates friction during my development process, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes (often?) ChatGPT is not available&lt;/li&gt;
&lt;li&gt;Sometimes, it stops generating code in the middle. You can reply “Continue” to resume the generation, but that’s still annoying (and you better know the trick)&lt;/li&gt;
&lt;li&gt;Sometimes, &lt;a href="https://www.reddit.com/r/ChatGPT/comments/zhraup/i_wrote_some_code_that_made_the_chatgpt_response/"&gt;the returned code formatting breaks down&lt;/a&gt;. It happened to me a few times already and that’s annoying.&lt;/li&gt;
&lt;li&gt;It requires me to switch context: get out of my editor to use my browser. It’s not a &lt;em&gt;big&lt;/em&gt; deal (browsing answers is probably the 2nd most common development activity after reading code), but it would be nice to have that from my editor…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then, I saw &lt;a href="https://twitter.com/lgrammel/status/1620734494071480324?s=20&amp;amp;t=b8SYniIweOisuXehosTarw"&gt;Lars Grammel’s tweet&lt;/a&gt; about his new project: &lt;a href="https://marketplace.visualstudio.com/items?itemName=Rubberduck.rubberduck-vscode"&gt;Rubberduck&lt;/a&gt;, an AI-powered coding assistant for VS Code.&lt;/p&gt;

&lt;p&gt;It says it can help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat with OpenAI&lt;/li&gt;
&lt;li&gt;Diagnose Errors&lt;/li&gt;
&lt;li&gt;Explain Code&lt;/li&gt;
&lt;li&gt;Generate Tests (!)&lt;/li&gt;
&lt;li&gt;Edit Code (!!)&lt;/li&gt;
&lt;li&gt;Find Bugs (!!!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right from VS Code.&lt;/p&gt;

&lt;p&gt;Interestingly, &lt;a href="https://github.com/rubberduck-ai/rubberduck-vscode"&gt;its source code is open&lt;/a&gt;! Thus, &lt;strong&gt;I've started to contribute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But here, I'll be using Rubberduck from my end-user point of view. So let’s try and find out what it can really do…&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lift Pass Pricing refactoring kata
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/martinsson/Refactoring-Kata-Lift-Pass-Pricing"&gt;This is a coding exercise&lt;/a&gt; that captures real-life software complexity (HTTP requests, database calls, external data…), without adding too much noise. I really like to use it to practice tools and techniques for refactoring legacy code.&lt;/p&gt;

&lt;p&gt;For testing Rubberduck, I will tackle the TypeScript version.&lt;/p&gt;

&lt;p&gt;The very first step is to run a tool like Prettier to standardize the shape of the code. No more noise in follow-up changes: &lt;a href="https://github.com/nicoespeon/Refactoring-Kata-Lift-Pass-Pricing/tree/rbd-1-prettier"&gt;rbd-1-prettier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The end goal of the exercise is to change the API so the endpoint that returns lift pass prices can return the price for several passes instead of just one.&lt;/p&gt;

&lt;p&gt;But the code is hard to re-use, so we would like to refactor it a little bit first.&lt;/p&gt;

&lt;p&gt;But how do we know the code is still working as expected after we refactored it? In general, we want to write tests first.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t82LmMvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuule784qlhk6454k5ck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t82LmMvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuule784qlhk6454k5ck.png" alt="Our game plan" width="600" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let’s see if the duck can help us write the missing tests faster… or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, make the tests run
&lt;/h2&gt;

&lt;p&gt;First of all, we need to write some tests before we touch the code. For this kata, I usually go with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/supertest-session-as-promised"&gt;&lt;code&gt;supertest-as-promised&lt;/code&gt;&lt;/a&gt; to run HTTP queries against the top-level application&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/testcontainers"&gt;&lt;code&gt;testcontainers&lt;/code&gt;&lt;/a&gt; to set up the database and get it to run in the tests (it’s based on Docker)&lt;/li&gt;
&lt;li&gt;A script to fill the database with tests data (these are already provided in the kata)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But let’s ask Rubberduck!&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask Rubberduck to suggest how to test this code
&lt;/h3&gt;

&lt;p&gt;To do so I need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the code I want to test&lt;/li&gt;
&lt;li&gt;Use VS Code palette to trigger the command “Rubberduck: Start Chat 💬”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This started a new chat and I could type in my question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xBMTRArj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u87l4pv4ca8vfjmj69oi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xBMTRArj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u87l4pv4ca8vfjmj69oi.png" alt="Question to ChatGPT (transcribed below)" width="880" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;How can I write tests for this piece of code?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🤖 Writing tests for this code can be a bit tricky, as there are a lot of conditions to consider. To make sure that all the conditions are tested, you'll need to create a test for each condition. For example, you could create a test to check that the cost is 0 when the age is less than 6, and another test to check that the cost is reduced when the age is between 6 and 15. You could also create tests to check that the cost is reduced on holidays and on Mondays.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, this is not very helpful. It’s just generic guidance…&lt;/p&gt;

&lt;p&gt;I followed up with more details about what’s blocking me and got some advice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;This is an Express controller. There is also a database involved. Which TypeScript libraries can I use to test it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🤖 You can use the &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt; testing framework with the &lt;a href="https://www.chaijs.com/"&gt;Chai&lt;/a&gt; assertion library to write tests for your Express controller. You can also use the &lt;a href="https://sinonjs.org/"&gt;Sinon&lt;/a&gt; library to mock the database calls. This will allow you to test the controller without actually making a call to the database. You can also use the &lt;a href="https://github.com/visionmedia/supertest"&gt;SuperTest&lt;/a&gt; library to make HTTP requests to the controller and test the response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s more interesting!&lt;/p&gt;

&lt;p&gt;✅ It’s good that it suggested SuperTest for testing the controller. If I wasn’t aware of this, it would have put me on the right track!&lt;/p&gt;

&lt;p&gt;🚫 I think going for Sinon to mock the database calls is a mistake at this point. I will refactor the implementation details and I have no control over the &lt;code&gt;connection&lt;/code&gt; API. Mocking it will make refactorings harder. I think a better approach here is to take a step back, consider this a black box, and set up an actual database.&lt;/p&gt;

&lt;p&gt;I tried to ask follow-up questions. Rubberduck gave me an interesting option where we could use Sequelize in tests to setup the database:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;I don't want to mock the database calls. Is there a library to set up the database and get it to run in tests?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🤖 If you don't want to mock the database calls, you could use a library like &lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt; to set up the database and run it in tests. Sequelize is an ORM (Object-Relational Mapping) library that allows you to define models and perform database operations. You can use it to create a test database and run tests against it. For example, you could create a test database with the following code:&lt;/p&gt;


&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sequelize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;sequelize&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Database is synced&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error creating database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Once the database is set up, you can use Sequelize to perform database operations in your tests. For example, you could create a user in the test database with the following code:&lt;/p&gt;


&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;testuser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error creating user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Do you have any other questions about setting up the database and running tests?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this would work, indeed.&lt;/p&gt;

&lt;p&gt;Note however that I couldn’t get the AI to guide me towards &lt;code&gt;testcontainers&lt;/code&gt;, despite mentioning “Docker in tests”. Without AI, I would usually &lt;a href="https://www.npmjs.com/search?q=docker%20tests"&gt;search npmjs with the relevant keywords&lt;/a&gt;, which will expose me to it.&lt;/p&gt;

&lt;p&gt;To me, this means &lt;strong&gt;you should not just blindly listen to AI&lt;/strong&gt;—it would have been naive to think otherwise. But &lt;strong&gt;combining its suggestions with your own experience and other sources can get you further, faster&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If I didn’t know how to get started here, Rubberduck suggestions to use Sequelize would certainly have helped.&lt;/p&gt;

&lt;p&gt;But let’s go with &lt;code&gt;testcontainers&lt;/code&gt; anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Rubberduck to write the code for me
&lt;/h3&gt;

&lt;p&gt;There is an existing test. To make it run, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a MariaDB up and running&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn start&lt;/code&gt; to get the server running&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn test&lt;/code&gt; to run the tests against the server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It may work to get started, but that won’t go far. If anything, this test can’t easily run on CI. The idea would be to start a Docker container where the whole thing can run reliably for each test run. That’s what &lt;code&gt;testcontainers&lt;/code&gt; can do!&lt;/p&gt;

&lt;p&gt;Let’s ask Rubberduck to update the tests for us, and see if it can save us some time writing the boilerplate code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the whole existing test code that I want to edit&lt;/li&gt;
&lt;li&gt;Use VS Code palette to trigger the command “Rubberduck: Edit Code 💬”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K97Wa5bI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwcv5716slhevcqf5xdj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K97Wa5bI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwcv5716slhevcqf5xdj.gif" alt="Question to ChatGPT (transcribed below)" width="880" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, I type in my request, trying to give some specificities:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;Use testcontainers to set up a new GenericContainer before all tests. It should use mariadb version 10.4 and expose port 3306. After all tests ran, it should stop.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After ~10s it generates a diff next to my code. I can inspect it and decide to “Apply” by clicking on a button at the bottom. I must admit that’s a way better flow than having to copy-paste code on ChatGPT. It feels &lt;strong&gt;integrated&lt;/strong&gt; within my development process 😄&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O2UVVeS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ni1oh28khgnmq7r3zbio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O2UVVeS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ni1oh28khgnmq7r3zbio.png" alt="Screenshot of interaction with OpenAI from VS Code" width="880" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the suggested diff, it’s not bad. There are some good things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The imports are correct&lt;/li&gt;
&lt;li&gt;It did declare the &lt;code&gt;container&lt;/code&gt; variable along with the others and initiated it&lt;/li&gt;
&lt;li&gt;It exposed the proper ports and started the container correctly&lt;/li&gt;
&lt;li&gt;It did stop the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also some errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The syntax to use MariaDB 10.4 is &lt;code&gt;GenericContainer("mariadb:10.4")&lt;/code&gt;, not passing two arguments. Hopefully, TypeScript will catch that easily.&lt;/li&gt;
&lt;li&gt;I wanted the container to be started before ALL tests and stopped after ALL tests. Doing it between each test is unnecessary and costly. AI didn’t listen to my instructions here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, I have 2 options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tell Rubberduck about the changes I want, so it can refine the suggestion.&lt;/li&gt;
&lt;li&gt;Apply the suggestion and do the manual tweaks myself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On real code, I would go for option #2 since it would be faster—the AI still takes a few seconds to generate the code and I &lt;em&gt;feel&lt;/em&gt; I can do the change faster myself because I won’t wait. It also comforts me with the idea of using AI to assist me, but still own the changes and finalizes what needs to be done.&lt;/p&gt;

&lt;p&gt;For the exercise though, I will refine the suggestion to see what it can do:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;The container should be created before ALL tests. It should be stopped after ALL tests.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BrzOIJ3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rs4eq9n1k4qvnkc9bl3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BrzOIJ3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rs4eq9n1k4qvnkc9bl3t.png" alt="Updated code" width="880" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that worked!&lt;/p&gt;

&lt;p&gt;It’s very interesting how you can follow up on the chat to get a refined suggestion. It means you don’t have to get the first input right on the first try. The main limiting factor today is the ~10s it takes to generate the code again, which makes the feedback loop too long for just developing like that—I can &lt;strong&gt;just&lt;/strong&gt; make the changes myself.&lt;/p&gt;

&lt;p&gt;Happy with these changes, I clicked “Apply” to get the code in. I make a few manual tweaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I forgot to ask Rubberduck to fix the syntax to call &lt;code&gt;GenericContainer("mariadb:10.4")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I’m explicitly typing &lt;code&gt;container&lt;/code&gt; when it’s declared to get type-safety wherever it’s used&lt;/li&gt;
&lt;li&gt;I configure the DB environment variable and initialization script when starting the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I guess I could have refined these points with Rubberduck, but I mostly realized them &lt;strong&gt;after&lt;/strong&gt; I got the code in. That’s fine. Rubberduck got me the headstart I was looking for… TypeScript is getting me through the finish line 👍&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the tests
&lt;/h3&gt;

&lt;p&gt;Now it’s time to verify if all of that is working!&lt;/p&gt;

&lt;p&gt;I don’t have a MariaDB up and running. Nor is the app running. I only have Docker started on my machine. I hit &lt;code&gt;yarn test&lt;/code&gt; and wait…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_RuVEnsp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdqobwrpnneue40m58tn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_RuVEnsp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdqobwrpnneue40m58tn.png" alt="Tests failed" width="880" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Failure!&lt;/p&gt;

&lt;p&gt;Apparently, something is wrong with the exposed ports. And indeed, in this specific case, AI’s suggested syntax won’t be enough: &lt;code&gt;withExposedPorts(3306)&lt;/code&gt;. The problem is that the port may not be mapped to the 3306 port on the host.&lt;/p&gt;

&lt;p&gt;A quick search in &lt;a href="https://github.com/testcontainers/testcontainers-node"&gt;testcontainers docs&lt;/a&gt; tells me that there is another syntax to bind the ports: &lt;code&gt;withExposedPorts({ container: 3306, host: 3306 })&lt;/code&gt;. Another way would be to get the mapped port and pass it to the source code, but I don’t want to change the source code now. Let’s go with the first option.&lt;/p&gt;

&lt;p&gt;I change the code and run &lt;code&gt;yarn test&lt;/code&gt; again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm3G1Q_U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/01lt0ympcgf5s23m73a9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm3G1Q_U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/01lt0ympcgf5s23m73a9.png" alt="Tests passed" width="880" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success!&lt;/p&gt;

&lt;p&gt;I think that illustrates something important: the code suggested by AI has a lot of unknowns and hypotheses. I need to combine it with other tools (static types, reading the API docs, my own knowledge, some sort of tests…) to validate them.&lt;/p&gt;

&lt;p&gt;I think it’s important to find a way to &lt;em&gt;verify&lt;/em&gt; the suggested code soon after it was merged in.&lt;/p&gt;

&lt;p&gt;That being said, the suggested syntax was fine and it helped my lookup for the relevant docs. If I wasn’t familiar with &lt;code&gt;testcontainers&lt;/code&gt; API, that would have saved me quite some time!&lt;/p&gt;

&lt;p&gt;Commit, push, and we are here: &lt;a href="https://github.com/nicoespeon/Refactoring-Kata-Lift-Pass-Pricing/tree/rbd-2-testcontainers"&gt;rbd-2-testcontainers&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Then, write the high-level tests
&lt;/h2&gt;

&lt;p&gt;This is often a difficult part.&lt;/p&gt;

&lt;p&gt;What do you test? How do you get started? There is so much going on… I generally recommend using the test coverage to identify parts of the code that are not tested yet. &lt;a href="https://understandlegacycode.com/blog/3-steps-to-add-tests-on-existing-code-when-you-have-short-deadlines/"&gt;I’ve already detailed this process,&lt;/a&gt; the main idea is to vary the inputs to capture the existing behavior&lt;/p&gt;

&lt;p&gt;For this exercise, we have 3 parameters: &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;, and &lt;code&gt;date&lt;/code&gt;. Spoiler, there are at least 11 scenarios to cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;type="1jour" =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;type="night" =&amp;gt; then cost is 0&lt;/li&gt;
&lt;li&gt;type="night" &amp;amp; age &amp;lt; 15 =&amp;gt; then cost is night cost&lt;/li&gt;
&lt;li&gt;type="night" &amp;amp; age &amp;gt; 64 =&amp;gt; then cost is 40% of night cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age &amp;lt; 6 =&amp;gt; then cost is 0&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age &amp;lt; 15 =&amp;gt; then cost is 70% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age &amp;gt; 64 =&amp;gt; then cost is 75% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 &amp;amp; any date =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 &amp;amp; date is Monday =&amp;gt; then cost is 65% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 &amp;amp; date is Monday and Holiday =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s see if Rubberduck can speed that up!&lt;/p&gt;

&lt;p&gt;The first test is already given, let’s just rename it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- it("does something", async () =&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+ it("returns day cost when type is '1jour'", async () =&amp;gt; {
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s ask Rubberduck to generate some tests for us, and see if it can suggest useful scenarios quickly from the source code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the code I want to test&lt;/li&gt;
&lt;li&gt;Use VS Code palette to trigger the command “Rubberduck: Generate Tests 💬”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QZBerQA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij1sp5vcrjuy6aibc1hk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QZBerQA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij1sp5vcrjuy6aibc1hk.png" alt="Triggering the command in VS Code" width="880" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After waiting ~1min it generates a new unsaved file with the test code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D8qx-Dqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3auk48dty3z42b9f9a2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D8qx-Dqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3auk48dty3z42b9f9a2c.png" alt="Generated code" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The style doesn’t match the rest of the tests. It tried to write unit tests from scratch. But I can copy-paste the generated scenarios and adapt them. I feel this will be faster than re-generating new tests.&lt;/p&gt;

&lt;p&gt;After copying the test cases, I select them and trigger another “Rubberduck: Edit Code 💬” action.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👨‍💻 &lt;em&gt;Use supertest to request the app and don't mock the response. Use chai library for the expect API.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After ~10s I get a diff that looks good. I click apply and I got a code that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Original test&lt;/span&gt;
&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;returns day cost when type is '1jour'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/prices?type=1jour&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Tests generated from Rubberduck&lt;/span&gt;
&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return cost 0 when age is less than 6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/prices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return cost 0 when age is undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/prices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But running the tests will fail. With a closer look, I realize there is indeed an error: the &lt;code&gt;req&lt;/code&gt; object should actually be a &lt;code&gt;query&lt;/code&gt; and not have a nested &lt;code&gt;query&lt;/code&gt; attribute! I could use Rubberduck to fix all tests… but I decide to use VS Code multi-cursor instead since I can do it in a few seconds:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9GVXxLAW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gzssby91iok2y0peprok.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9GVXxLAW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gzssby91iok2y0peprok.gif" alt="Using VS Code multi-cursor" width="880" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I decided to rewrite the original test to look similar to the others. This is when I notice something else is off: the type value is “day” but it should really be “1jour”.&lt;/p&gt;

&lt;p&gt;That’s a legit mistake. The source code refers to the type “night” as a special one. AI figured another variant would be “day”. Except that the valid values are in the database, and AI isn’t aware of this yet.&lt;/p&gt;

&lt;p&gt;I replace all “day” occurrences with “1jour” and run the tests again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1kzr1lMW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdp45v2rnipj3odbz9k1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1kzr1lMW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdp45v2rnipj3odbz9k1.png" alt="Some tests still fail" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, well, well… When looking closer, there are some obvious mistakes in the generated tests.&lt;/p&gt;

&lt;p&gt;One test isn’t testing what it says it is testing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BMHbmall--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7njv4pnti0c833fth4r0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BMHbmall--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7njv4pnti0c833fth4r0.png" alt="Mismatch between test label &amp;amp; assert" width="725" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, this test is even wrong. I can see the body of the test is the same as the original one. The expected cost should be the base cost, and I can confirm that with the test failure. Let’s scrap this one!&lt;/p&gt;

&lt;p&gt;As for the other tests, they fail because they consider the base cost to be 10:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a94vOB3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebmyis97kgir1yjcvbx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a94vOB3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebmyis97kgir1yjcvbx1.png" alt="Asserted result expects base cost to be 10 instead of 35" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But here again: the base cost is set in the database, and depends on the ticket type.&lt;/p&gt;

&lt;p&gt;Therefore, I need to correct these expectations manually. The test's failure helps me figure out the proper behavior. Sometimes the label is wrong, sometimes it’s the expected output that’s incorrect. However, the variation of inputs is the interesting part! Let’s see the scenarios that were properly covered:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hj3BNltg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9js4yqx3bqby9ogpcjq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hj3BNltg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9js4yqx3bqby9ogpcjq8.png" alt="Passing tests" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ type="1jour" =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;type="night" =&amp;gt; then cost is 0&lt;/li&gt;
&lt;li&gt;✅ type="night" &amp;amp; age &amp;lt; 15 =&amp;gt; then cost is night cost&lt;/li&gt;
&lt;li&gt;✅ type="night" &amp;amp; age &amp;gt; 64 =&amp;gt; then cost is 40% of night cost&lt;/li&gt;
&lt;li&gt;✅ type="1jour" &amp;amp; age &amp;lt; 6 =&amp;gt; then cost is 0&lt;/li&gt;
&lt;li&gt;✅ type="1jour" &amp;amp; age &amp;lt; 15 =&amp;gt; then cost is 70% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;✅ type="1jour" &amp;amp; age &amp;gt; 64 =&amp;gt; then cost is 75% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 &amp;amp; any date =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;li&gt;✅ type="1jour" &amp;amp; age = 15 &amp;amp; date is Monday =&amp;gt; then cost is 65% of day cost&lt;/li&gt;
&lt;li&gt;type="1jour" &amp;amp; age = 15 &amp;amp; date is Monday and Holiday =&amp;gt; then cost is day cost&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s 8 scenarios out of 11!&lt;/p&gt;

&lt;p&gt;Sure, the generated tests didn’t work. I also had to manually fix the syntax, most labels, and expected values. But I saved quite some time by &lt;strong&gt;not having to manually figure out the different parameters that have to change&lt;/strong&gt;. Rubberduck found them all for me!&lt;/p&gt;

&lt;p&gt;With this base, I would then use test coverage to see parts of the code that aren’t tested yet and figure out the missing variations to get them all.&lt;/p&gt;

&lt;p&gt;From my experience, Rubberduck generates better tests when the source code doesn’t depend on external sources, like a database or a 3rd-party service. And yet, this was helpful to give me ideas and get me started. It may come in handy when dealing with unfamiliar code that I want to write tests for 👍&lt;/p&gt;

&lt;p&gt;The final code is here: &lt;a href="https://github.com/nicoespeon/Refactoring-Kata-Lift-Pass-Pricing/tree/rbd-3-tests"&gt;rbd-3-tests&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra: Rubberduck helped me with a random question
&lt;/h2&gt;

&lt;p&gt;While writing this article, I pushed all the code to a forked repository, so you can follow along.&lt;/p&gt;

&lt;p&gt;At some point, I couldn’t remember the git syntax to push the tags to that specific remote… So I just open a new Rubberduck chat and asked. It gave me the command I was looking for. All of that without leaving VS Code ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--adHpvRrf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj6tgy5tlqza88hnrc59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--adHpvRrf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj6tgy5tlqza88hnrc59.png" alt="Interaction with OpenAI from VS Code" width="880" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, thank you Rubberduck 👍 🦆&lt;/p&gt;

&lt;h2&gt;
  
  
  How is it different than ChatGPT? Ponicode?
&lt;/h2&gt;

&lt;p&gt;It's &lt;strong&gt;very similar&lt;/strong&gt; to what ChatGPT could do. The main difference to me is the UX:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No need to get out of my editor is nice&lt;/li&gt;
&lt;li&gt;No fear of ChatGPT website being unavailable at the moment&lt;/li&gt;
&lt;li&gt;No hassle crafting up the perfect prompt to get the answers I need (Rubberduck crafts the prompt under the hood)&lt;/li&gt;
&lt;li&gt;And no little annoyances like AI stopping the prompt in the middle or messing up the code formatting&lt;/li&gt;
&lt;li&gt;Oh, and 💯 for the diff view + "Apply" button&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As for Ponicode, the experience is very different. &lt;a href="https://understandlegacycode.com/blog/ai-test-generation-for-legacy-code/"&gt;Ponicode wasn't capable of generating useful unit tests&lt;/a&gt; on random code (and the code was simpler). The generated tests didn't help me figure out the different parameters I could use… It was also less versatile.&lt;/p&gt;

&lt;p&gt;To be fair, OpenAI did bring a lot of new opportunities when they released their API. Their model is powerful, and the results are (finally) kinda usable on actual legacy codebases!&lt;/p&gt;

&lt;h2&gt;
  
  
  AI can help you write missing tests faster…
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;But don’t turn your brain off!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That would be my conclusion to this experiment. I was curious about what an AI assistant such as Rubberduck could do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Can it just do the work for me?&lt;/strong&gt; Nope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can it help if I’m not an expert at refactoring unfamiliar code?&lt;/strong&gt; Yes, it gave valuable insights to get out of the analysis paralysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can it make me go faster if I’m already familiar with the code/techniques?&lt;/strong&gt; Surprisingly, yes. There are parts of the process that always take some time, such as figuring out the different parameters to vary. While Rubberduck couldn’t write the exact tests for me, it generated a useful base to get started.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a bonus, it gave me easy input to query OpenAI, right from my editor. I was able to prompt random questions while coding without having to switch to my browser 🏆&lt;/p&gt;

&lt;p&gt;I would conclude this article with 2 thoughts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubberduck-ai/rubberduck-vscode"&gt;Rubberduck is open-source&lt;/a&gt;. I encourage you to give it a try, report bugs and suggest improvements. I'm now contributing to the tool myself, it's really cool!&lt;/li&gt;
&lt;li&gt;I have used Rubberduck to help me write missing tests on existing code… But now I’m curious about what it can do to help me &lt;strong&gt;refactor&lt;/strong&gt; the code! So that will be the topic of my next post about it. Stay tuned!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.ck.page/2146330ee6"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ORyVyzc_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xj65dqhhamh03zmgkkyc.png" alt="Looking for tips &amp;amp; tricks to deal with Legacy Code?" width="880" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>vscode</category>
      <category>legacycode</category>
      <category>testing</category>
    </item>
    <item>
      <title>7 advice to help you inherit a legacy codebase</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Tue, 15 Mar 2022 13:24:04 +0000</pubDate>
      <link>https://dev.to/nicoespeon/7-advice-to-help-you-inherit-a-legacy-codebase-5af8</link>
      <guid>https://dev.to/nicoespeon/7-advice-to-help-you-inherit-a-legacy-codebase-5af8</guid>
      <description>&lt;p&gt;I wanted to make a recap of the most common and useful advice one can give to tackle Legacy codebases.&lt;/p&gt;

&lt;p&gt;I have a few of my own, but I also went out to ask experimented developers about their opinions.&lt;/p&gt;

&lt;p&gt;I ended up with 7 advice. Let's dig into these!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Stay curious
&lt;/h2&gt;

&lt;p&gt;Approach the problem with an open mind.&lt;/p&gt;

&lt;p&gt;Legacy systems are often frustrating to work with. Yet, you probably aren't the only one in this.&lt;/p&gt;

&lt;p&gt;Ask questions to your colleagues, discuss with non-technical people, look up for information around. You may be surprised how much you can discover that wasn't obvious at first sight.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. First, get it running
&lt;/h2&gt;

&lt;p&gt;So many problems to address! An overwhelming mess! I know the feeling.&lt;/p&gt;

&lt;p&gt;When you are dealing with many problems, the most efficient way I know is to start with one and focus on that.&lt;/p&gt;

&lt;p&gt;Can you get the system running on your machine already? Start with that. Once you get it solved, it will help you tackle the other issues.&lt;/p&gt;

&lt;p&gt;You can run it? Great! Then, get the tests running. There is no test runner? Put one in place. Then, make sure you can deploy new versions to production. And so on…&lt;/p&gt;

&lt;p&gt;Pick one problem that will help solve others and focus on that. Get it done, then move to the next one. Don't try to tackle everything at once, that doesn't work well.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Take notes as you go
&lt;/h2&gt;

&lt;p&gt;When you onboard on a new, legacy codebase, you are in a great position to notice problems!&lt;/p&gt;

&lt;p&gt;As you learn the quirks of the system and how to solve them, take notes. These don't need to be polished and all. Raw Markdown notes would do.&lt;/p&gt;

&lt;p&gt;Put these notes in a place where they are easy to search for. That may be Markdown files versioned along with the source code &lt;a href="https://understandlegacycode.com/blog/earn-maintainers-esteem-with-adrs/"&gt;as we do for ADRs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I find it useful to take notes for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How I solved problem X (eg. &lt;em&gt;I got this error when installing dependencies, so I did that and it worked&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Jargon, terms I'm not familiar with&lt;/li&gt;
&lt;li&gt;The structure of the code, where to put things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A complimentary piece of advice is to train yourself to detect when you make assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Refactor as you go
&lt;/h2&gt;

&lt;p&gt;This is very similar to the previous point, but turning the dial up a bit.&lt;/p&gt;

&lt;p&gt;As you work with the code, your understanding will grow. You will have these aha moments. "Ooooh, that's what it does!"&lt;/p&gt;

&lt;p&gt;You can capture these learnings in notes and code comments. I also recommend you take an extra step and improve the names.&lt;/p&gt;

&lt;p&gt;Functions and variable names are important beacons for understanding. When something wasn't obvious at first and it finally clicked, take the time to improve the names so the intention is clearer!&lt;/p&gt;

&lt;p&gt;This is easier to do with a fresh pair of eyes.&lt;/p&gt;

&lt;p&gt;A technique I really like is called &lt;a href="https://understandlegacycode.com/blog/demine-codebase-with-exploratory-refactoring/"&gt;Exploratory Refactoring&lt;/a&gt; (or "Scratch Refactoring"). Allow yourself to do rogue changes in the code. You won't keep these changes, so you don't care if the code still compiles. The goal is to interact with the code instead of passively reading it. Timebox yourself for 30min. In the end, take notes of what you learned and scratch that work.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Read books
&lt;/h2&gt;

&lt;p&gt;There is not a ton of resources on dealing with legacy systems. But there are a few that are quite good if you ask me.&lt;/p&gt;

&lt;p&gt;​&lt;a href="https://understandlegacycode.com/blog/key-points-of-working-effectively-with-legacy-code/"&gt;Working Effectively with Legacy Code&lt;/a&gt;, from Michael Feathers, is a reference. I bet many of you have read it already. If you never had the chance, then it's the book I would recommend you to read.&lt;/p&gt;

&lt;p&gt;If you have read this one already, &lt;a href="https://understandlegacycode.com/#books"&gt;I listed a few more that I found the most relevant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Books, blog posts, talks, screencasts… These resources will expose you to new techniques and approaches. These will constitute a useful tool belt for when you are facing a new problem to solve, like identifying Seams in existing code so you can write tests for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Write the missing tests
&lt;/h2&gt;

&lt;p&gt;Tests are particularly useful for 3 reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gain an understanding of what the code actually does&lt;/li&gt;
&lt;li&gt;Reduce the risk of introducing a bug when making changes&lt;/li&gt;
&lt;li&gt;Identify the main problems of the code design (eg. too many hidden dependencies)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Writing good tests is a skill to practice. Writing tests on existing code is even harder, because you may need to refactor this code… without tests to have your back!&lt;/p&gt;

&lt;p&gt;That's where reading books and watching talks will help. You may already be doing &lt;a href="https://michaelfeathers.silvrback.com/characterization-testing"&gt;Characterization Testing&lt;/a&gt; by intuition. There are more techniques and tools that can help you do it faster and safer.&lt;/p&gt;

&lt;p&gt;Obvious candidates for me are &lt;a href="https://understandlegacycode.com/blog/legacy-of-socrates-8th-edition/#how-we-use-safe-refactoring-to-untangle-gnarly-code-without-tests"&gt;Safe Refactorings&lt;/a&gt; and &lt;a href="https://understandlegacycode.com/approval-tests"&gt;Approval Tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You don't need to write all the missing tests. Just write enough tests for the code you need to change. The time you invest here will be saved in the understanding you gain and the production support you won't have to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Make it better, not perfect
&lt;/h2&gt;

&lt;p&gt;The most important advice of all, if you ask me.&lt;/p&gt;

&lt;p&gt;This stuff is hard. It's overwhelming to work with an existing system, built on old technologies, patched everywhere, yet serving actual people in production! 🙀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's fine to have a partial understanding of the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your understanding will grow over time. You will have these aha moments. You can speed up that process by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talking to people&lt;/li&gt;
&lt;li&gt;Taking notes&lt;/li&gt;
&lt;li&gt;Using techniques such as &lt;a href="https://understandlegacycode.com/behavioral-analysis"&gt;Behavioral Analysis&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be mindful of yourself. This is a marathon. If you can make it just 1% better every single day, time will be on your side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RqgJUxw---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/na4ci1uzyumseebc9wdr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RqgJUxw---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/na4ci1uzyumseebc9wdr.jpeg" alt="Tiny gains compound over time" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you get overwhelmed by a problem, &lt;strong&gt;take a break and step away&lt;/strong&gt;. To talk to people, take a step back. It may not solve your problem, but it may also give you new ideas.&lt;/p&gt;

&lt;p&gt;I solved a lot of problems by simply "talking to others" about them.&lt;/p&gt;

&lt;p&gt;Another good example of the "make it better, not perfect" mindset is this: consider you want to automate the deployment process (lovely). This can be an overwhelming task, with a lot of intermediate problems to solve…&lt;/p&gt;

&lt;p&gt;Now, what about this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You take notes of the steps that need to be performed to deploy&lt;/li&gt;
&lt;li&gt;You put these steps in a script&lt;/li&gt;
&lt;li&gt;You expose this script with a command, like if it would deploy it for you&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a trick I learned from &lt;a href="https://twitter.com/arlobelshee"&gt;Arlo Belschee&lt;/a&gt; (I don't recall where, though).&lt;/p&gt;

&lt;p&gt;The script won't do the deployment itself. In fact, it may just start with telling you the &lt;em&gt;manual&lt;/em&gt; steps you need to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A7oDPiss--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vj3xbcdapcv93m7p8ikl.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A7oDPiss--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vj3xbcdapcv93m7p8ikl.jpeg" alt="A script that tells you which manual step to perform next" width="811" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's not fully automated yet but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You now have a command to run to "deploy"&lt;/li&gt;
&lt;li&gt;It reduces the risk of forgetting about a step&lt;/li&gt;
&lt;li&gt;You can iterate to progressively automate each individual step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, this process will be more and more automated. But it doesn't have to be 100% automated or nothing. Only a Sith deals in absolutes 😉&lt;/p&gt;




&lt;p&gt;What other advice would you give?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/#subscribe?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Yh0eOdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whrtje0cucib2inrj5bw.png" alt="You can get my advice to deal with Legacy Code from my newsletter" width="660" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>legacycode</category>
      <category>testing</category>
      <category>refactor</category>
    </item>
    <item>
      <title>What's the difference between Regression Tests, Characterization Tests, and Approval Tests?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Mon, 23 Aug 2021 19:26:53 +0000</pubDate>
      <link>https://dev.to/nicoespeon/what-s-the-difference-between-regression-tests-characterization-tests-and-approval-tests-2jf3</link>
      <guid>https://dev.to/nicoespeon/what-s-the-difference-between-regression-tests-characterization-tests-and-approval-tests-2jf3</guid>
      <description>&lt;p&gt;Before you start refactoring some Legacy Code, fellow developers would drop this advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure it's covered with a test first!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Of course&lt;/em&gt;, this code isn't tested. Since you have little time and deadlines to meet, people like me would advise you to use a nifty technique: &lt;a href="https://understandlegacycode.com/blog/3-steps-to-add-tests-on-existing-code-when-you-have-short-deadlines/"&gt;Approval Testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Approval Testing is the fastest way to put existing code under tests. You take some code, you throw a variety of inputs at it, and you capture outputs. Quickly, you build up a regression net that will tell you if you broke something when refactoring.&lt;/p&gt;

&lt;p&gt;Sometimes, people are familiar with &lt;a href="https://understandlegacycode.com/blog/key-points-of-working-effectively-with-legacy-code/"&gt;Working Effectively with Legacy Code&lt;/a&gt; from Michael Feathers. When that's the case, they usually pop the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yeah, I've heard about Approval Tests. Is this the same as Characterization Tests? Asking for a friend…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For people who never read Feathers' book, "Regression Tests" seems to be a common name for approaching code that isn't tested. Is there a subtle difference? 🤔&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AHkW5mPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/choice-paralysis.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHkW5mPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/choice-paralysis.jpg" alt="Paralyzed by choice" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I've been answering this question a few times already, I'm saving it here so we can all share it with our confused fellows 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes, Approval and Characterization Tests are the same thing
&lt;/h2&gt;

&lt;p&gt;The reason for the different names is because there is no recognized standard—so far. Different people came up with different names for the same technique. In fact, &lt;a href="https://trends.google.com/trends/explore/TIMESERIES/1629219000?hl=en-US&amp;amp;tz=-120&amp;amp;q=approval+tests,characterization+tests,regression+tests&amp;amp;sni=3"&gt;most people seem to be using "Regression Tests"&lt;/a&gt; to refer to it!&lt;/p&gt;

&lt;p&gt;What matters is the spirit of it.&lt;/p&gt;

&lt;p&gt;First, you should understand the goal: &lt;strong&gt;have tests that fail if the system behaves differently.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The process has roughly 3 phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Get the code run into automated tests&lt;/strong&gt; 👈 This typically is the hard part of the game. It involves cutting annoying dependencies. Feathers techniques will help you do that!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture interesting output.&lt;/strong&gt; That may be the returned response. Or you may introduce a custom logger to capture interesting facts that happen during execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vary inputs as much as possible to cover all possible scenarios.&lt;/strong&gt; Test coverage can help you identify what you're not testing. I like to introduce deliberate bugs to verify I got at least one test fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"Characterization Tests" is the name that has been popularized among Feathers' audience. I think it's a good name, especially for Legacy Code.&lt;/p&gt;

&lt;p&gt;"Approval Tests" is also a great name. I think it's even better.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why?&lt;/em&gt; Because of what it suggests. Name matters. Let me tell you about the other names to illustrate that—yep, there are more!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Golden Master and Snapshot Tests
&lt;/h2&gt;

&lt;p&gt;If you've been around for a while, you may recall the Golden Master technique. Well, it's the same story. The goal is to capture existing behavior, so we can compare future changes against it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The purpose of characterization testing is to document your system's actual behavior; not to check for the behavior you wish your system had"&lt;/p&gt;

&lt;p&gt;— Michael Feathers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The "Golden Master" name comes from the audio record-making industry, specifically &lt;a href="https://en.wikipedia.org/wiki/Mastering_(audio)"&gt;the process of mastering&lt;/a&gt;. It's the one truth from which copies can be made.&lt;/p&gt;

&lt;p&gt;While it embeds the spirit that &lt;em&gt;what the software does is more important than what it should do&lt;/em&gt;, I don't like "Golden Master" as a name. To me, it implies you won't touch it, which I don't think is a good mindset to have when dealing with Legacy systems.&lt;/p&gt;

&lt;p&gt;I prefer "Approval" because it suggests the behavior has been &lt;em&gt;approved&lt;/em&gt; by a human, and we can change that. I think the human aspect is important to this process. We are in control of evolving the software. That means we have to make decisions on how the system behaves. "Approval Tests" expresses that!&lt;/p&gt;

&lt;h3&gt;
  
  
  The (unknown) importance of a good Printer
&lt;/h3&gt;

&lt;p&gt;In September 2020, &lt;a href="https://twitter.com/emilybache"&gt;Emily Bache&lt;/a&gt; gave a talk on Approval Testing.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0ZVKcFsEp-4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/0ZVKcFsEp-4?t=940"&gt;At 15:40&lt;/a&gt;, she presents the four parts of an Approval Test:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Arrange&lt;/li&gt;
&lt;li&gt;Act&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Print&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Assert (compare with previous output)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;strong&gt;Print&lt;/strong&gt; step matters to make these tests really useful. A &lt;em&gt;Printer&lt;/em&gt; is what turns the output you're capturing into a string that will be convenient to compare new values with old values.&lt;/p&gt;

&lt;p&gt;A good Printer will scrub irrelevant and flaky data, and format the output in a way that makes sense for a human to read.&lt;/p&gt;

&lt;p&gt;For example, here's the output captured by an approval test &lt;a href="https://github.com/emilybache/SupermarketReceipt-Refactoring-Kata/blob/6c2fbe7ca8a8aea593fd16f12e3418dc2df1867c/java/src/test/java/dojo/supermarket/ReceiptPrinterTest.discounts.approved.txt"&gt;in the Supermarket Refactoring kata&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;----------------------------------------
Receipt date:           [date]
----------------------------------------
3 for 2(apples)                    -0.99

Total:                             -0.99
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the data was formatted to look like an actual receipt?&lt;/p&gt;

&lt;p&gt;Notice also that the actual date has been scrubbed (removed), because it may change between test runs.&lt;/p&gt;

&lt;p&gt;And that's why "Snapshots Tests" also miss the point!&lt;/p&gt;

&lt;p&gt;Snapshots have been popularized by Facebook and their testing library: &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;. They are useful when dealing with Legacy Code. They help you do Approval Testing.&lt;/p&gt;

&lt;p&gt;The name "Snapshots Tests" focuses on the action of capturing the output, and &lt;strong&gt;doesn't suggest the importance of a good Printer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I think Jest's snapshots are generally misused. It's common to reach a point where people just update snapshots without really understanding what's going on. Therefore, despite having tests, bugs appear because there's no easy way to tell whether the change is legit.&lt;/p&gt;

&lt;p&gt;Although the misuse of the technique isn't Jest's fault, I think Facebook should have known better and re-use the "Approval Tests" name. Instead, they came up with their own 🤷‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  "Approval Tests" will lead you to more resources
&lt;/h3&gt;

&lt;p&gt;I would use this one over the other because the name leads you to convenient tools that help you do that. While you can write Characterization Tests by yourself, I recommend you have a look at &lt;a href="https://approvaltests.com"&gt;approvaltests.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The "Approvals Tests" project has a lot of resources and documentation. It is implemented in a dozen of languages.&lt;/p&gt;

&lt;p&gt;Going further than the &lt;code&gt;approvaltests&lt;/code&gt; library, you may find products like &lt;a href="https://touca.io/"&gt;Touca&lt;/a&gt; which aim to simplify the process for you. It supports applying Approval Testing at scale, fixing the typical issues you'd have collaborating with the captured outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Call them "Approval Tests"
&lt;/h2&gt;

&lt;p&gt;Now you know that the technique of capturing existing code behavior into tests has different names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Characterization Tests&lt;/li&gt;
&lt;li&gt;Approval Tests&lt;/li&gt;
&lt;li&gt;Golden Master&lt;/li&gt;
&lt;li&gt;Snapshot Tests&lt;/li&gt;
&lt;li&gt;Locking Tests (thanks &lt;a href="https://twitter.com/maaretp"&gt;Maaret&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Regression Tests (that's how &lt;a href="https://touca.io/"&gt;Touca&lt;/a&gt; describe itself)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Among all, I recommend referring to it as "Approval Tests". Acknowledge other names refer to the same technique.&lt;/p&gt;

&lt;p&gt;Have a look at &lt;a href="https://approvaltests.com"&gt;approvaltests.com&lt;/a&gt; and think about implementing a Printer to make your Approval Tests more useful.&lt;/p&gt;




&lt;p&gt;Have you heard about another name for this technique?&lt;/p&gt;

&lt;p&gt;Have you used Approval Tests without knowing it had a name?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/nicoespeon"&gt;Let me know!&lt;/a&gt; 🤠&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/#subscribe?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Yh0eOdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whrtje0cucib2inrj5bw.png" alt="You can get my advice to deal with Legacy Code from my newsletter" width="660" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>legacycode</category>
      <category>testing</category>
      <category>refactor</category>
    </item>
    <item>
      <title>Is it OK to change code for the sake of testing?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Tue, 18 May 2021 15:28:46 +0000</pubDate>
      <link>https://dev.to/nicoespeon/is-it-ok-to-change-code-for-the-sake-of-testing-5gld</link>
      <guid>https://dev.to/nicoespeon/is-it-ok-to-change-code-for-the-sake-of-testing-5gld</guid>
      <description>&lt;p&gt;When you work with existing code that has no test, adding some is often a challenge. This code wasn't written to be executed in a different context than "production". It works™ under the right circumstances and environment, which are difficult to reproduce and automate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/first-aid-kit/"&gt;I have techniques&lt;/a&gt; to get such code under tests. Some of them require to do minimal changes to the existing code, so we can write the tests.&lt;/p&gt;

&lt;p&gt;When I present these, someone eventually raises this interesting question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is it fine that we create these extra parameters just for the tests?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a wonderful question to have! It shows that you care about the maintainability of the code in the future. You don't want to make the mess even worse, do you?&lt;/p&gt;

&lt;p&gt;The code is in a poor state. We want to write tests so we can change it safely, faster. But doing so often makes the code looks… worse. How does it look like to turn this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPurchaseExport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 200 lines of logic, http requests, logging, etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Into the same code, but with external dependencies passed as parameters?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPurchaseExport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sentry&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The same 200 lines, but easier to test 🤷&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usually, the question pops at some point in your head. I think it's an important one to address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes, it's fine to change code for the sake of testing
&lt;/h2&gt;

&lt;p&gt;For once, I don't have an "it depends" answer to give here. I've experienced both approaches enough times to have a good opinion on this. Yes, it's OK to do the change so you can test the code. Even if it looks worse &lt;em&gt;for now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The rationale here is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't know any other approach that would bring benefits in the future. Some tools may numb the pain for a while, letting you go in the wrong direction longer. Eventually, you have to come back from a worse state.&lt;/li&gt;
&lt;li&gt;The remaining alternative is to… do nothing. That's dangerous. Doing nothing is the easiest approach, but it harms the codebase more because it makes it harder and harder to come back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to Legacy Code, it's very easy to be paralyzed by the amount of work that is required to put things back on track. Ideally, you'd refactor the whole thing for a design that makes more sense. Maybe you'd rewrite all of it! In real life, you don't have time for that.&lt;/p&gt;

&lt;p&gt;I've learned that you can't afford doing nothing because it wouldn't be perfect. That's not productive.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't make it perfect. Make it a little bit better, every day.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why does it feel like we're making the code worse though? Because of the symptoms. Injecting dependencies explicitly to some code will make it clear that it does too much. We don't like seeing a class/function take 8 parameters. It will raise a red flag to most developers reading this code.&lt;/p&gt;

&lt;p&gt;Yet, having 8 &lt;em&gt;implicit&lt;/em&gt; dependencies is worse. It looks better because it's hidden. But that hidden complexity bites you whenever you need to change the code. It creates friction, preventing changes. The act of writing tests highlights this pain because you have to deal with the friction now. By making it visible, we create opportunities to address the root cause. Resist the temptation of hiding the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can preserve the API for clients
&lt;/h2&gt;

&lt;p&gt;Note that you can always add some proxy, so you can start cleaning the code without impacting all the clients of this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPurchaseExport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;getPurchaseExportAndDoOtherStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sentry&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPurchaseExportAndDoOtherStuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sentry&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The same 200 lines, but easier to test 🤷&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did 3 things here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I extracted the whole body of the original function into another one. This new function isn't used by clients and takes its dependencies as explicit parameters.&lt;/li&gt;
&lt;li&gt;I gave &lt;a href="https://understandlegacycode.com/blog/improving-legacy-function-names/#its-fine-to-use-and-but-your-journey-doesnt-stop-here"&gt;an honest name&lt;/a&gt; to this function. It does more than getting the "purchase export". I don't know what yet, but I want to make that clear. I'll rename it as I understand this function better.&lt;/li&gt;
&lt;li&gt;I exported it, so I can access it in tests. This is not ideal, but it is fine because it's my way out. If I'm concerned clients would start using this code, I can extract it in a different file that wouldn't be exposed at the module level. Or I could give it a more explicit name.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What matters is that we are addressing the root cause. Until we are done, we do compromises. It is fine as long as we make it clear, and we keep moving in the right direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  It becomes worse before it becomes better
&lt;/h2&gt;

&lt;p&gt;In her talk &lt;a href="https://www.youtube.com/watch?v=8bZh5LMaSmE"&gt;All the Little Things&lt;/a&gt;, Sandi Metz tackles &lt;a href="https://understandlegacycode.com/blog/5-coding-exercises-to-practice-refactoring-legacy-code/#1-the-gilded-rose"&gt;the Gilded Rose kata&lt;/a&gt;. When she starts refactoring the code, she makes it look worse.&lt;/p&gt;

&lt;p&gt;Actually, it doesn't just look worse. &lt;em&gt;It is worse.&lt;/em&gt; The intermediate result is much more complex than the original code. And yet, this intermediate complexity leads to ultimate simplicity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wVZH5bPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpp7ntlzhffd5of88gi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wVZH5bPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpp7ntlzhffd5of88gi0.png" alt="Sandi explaining intermediate refactorings increase code complexity until it drops suddenly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the gist of it: you need to know (or believe) that the intermediates refactorings will lead you to simplicity to keep moving towards that direction. Otherwise, you won't dare to do the necessary changes, because they seem to make the code worse. You can't see it's only temporary.&lt;/p&gt;

&lt;p&gt;At this point, here is the advice that helped me the most:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Practice in a safe playground&lt;/strong&gt;. Use a coding exercise to practice refactoring techniques and experience how it feels—&lt;a href="https://understandlegacycode.com/blog/5-coding-exercises-to-practice-refactoring-legacy-code"&gt;I've listed 5 great exercises here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarify the end goal.&lt;/strong&gt; Especially when working with a team. Explain what's the intention behind the intermediate refactoring, and how you plan to get there (roughly). Explain how the things that look worse, for now, will ultimately be simplified (e.g. responsibilities will be split, so this code will only compose other, well-tested functions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep track of the work&lt;/strong&gt;. Have an open issue for the remaining work. That helps to realize you already have work in progress before you decide to start another refactoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make little refactorings part of your daily habits&lt;/strong&gt;. Once you trust yourself that you will move the needle towards the end goal every time you touch the code, you don't need to keep track of the work so much. It takes time and practice, but ultimately makes working with legacy codebases less frustrating.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Give them a try, they may help you too 👍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>legacycode</category>
      <category>testing</category>
      <category>refactor</category>
    </item>
    <item>
      <title>7 tips for a successful code handover</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Wed, 10 Feb 2021 11:32:25 +0000</pubDate>
      <link>https://dev.to/nicoespeon/7-tips-for-a-successful-code-handover-2ld7</link>
      <guid>https://dev.to/nicoespeon/7-tips-for-a-successful-code-handover-2ld7</guid>
      <description>&lt;p&gt;Usually you would skim around this blog to learn tips and techniques to deal with some Legacy Code you inherited. In general, the original developers are long gone…&lt;/p&gt;

&lt;p&gt;But, sometimes, you can be in a very particular position: &lt;strong&gt;a code handover!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe you're inheriting a project from a different team and there's a dedicated knowledge transfer budgeted. Or maybe the original developer is still working with you, but he will leave soon.&lt;/p&gt;

&lt;p&gt;Well you might even be the one who's handing over a Legacy codebase and you want to give the best chances to your successor!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deadlines, deadlines, deadlines
&lt;/h2&gt;

&lt;p&gt;In any situation, &lt;strong&gt;your time is limited&lt;/strong&gt;. The codebase is huge. The technical debt backlog is bottomless. What should you document? Should you plan refactoring sessions with all developers? Is it smarter to focus on tests?&lt;/p&gt;

&lt;p&gt;Imagine the last day of the handover: that's quite an event! What if you could live this moment relaxed, being confident that you set the team on the right track? You'll enjoy the very last moment of this transition as you'd have proven &lt;strong&gt;how efficient and professional&lt;/strong&gt; you are!&lt;/p&gt;

&lt;p&gt;But this moment is coming soon and there's so much to do… What should you prioritize now?&lt;/p&gt;

&lt;p&gt;Hopefully, I've gathered 7 best practices that will help you go through this very particular period of your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  7 best practices to hand over Legacy Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Get everything under version control
&lt;/h3&gt;

&lt;p&gt;Careful, this is very easy to overlook!&lt;/p&gt;

&lt;p&gt;Get &lt;strong&gt;everything&lt;/strong&gt; under version control, including the scripts that are on the leaving developers' hard drive.&lt;/p&gt;

&lt;p&gt;Think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;backup procedures&lt;/li&gt;
&lt;li&gt;manual operations done periodically&lt;/li&gt;
&lt;li&gt;manual operations done exceptionally&lt;/li&gt;
&lt;li&gt;documentation of third-party softwares or APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure you have all the passwords, licenses, contacts you need to keep maintaining the project.&lt;/p&gt;

&lt;p&gt;New developers should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build the code&lt;/li&gt;
&lt;li&gt;test the code&lt;/li&gt;
&lt;li&gt;deploy the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure this is DONE before moving on!&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Get management support
&lt;/h3&gt;

&lt;p&gt;You don't have infinite time for this transition.&lt;/p&gt;

&lt;p&gt;A great transition will make a tremendous difference in the new team productivity. That's crucial for the business!&lt;/p&gt;

&lt;p&gt;Make sure that you get management on your side. Ask them to &lt;strong&gt;prioritize the knowledge transfer&lt;/strong&gt; over "just fixing those last few bugs before they leave".&lt;/p&gt;

&lt;h3&gt;
  
  
  3) New developers should write the docs
&lt;/h3&gt;

&lt;p&gt;Writing documentation can feel boring. Yet, it's really helpful and does wonder for passing knowledge!&lt;/p&gt;

&lt;p&gt;The developers who will maintain the code after the transition are in the best position to document: they know nothing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--soXqo1hz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://understandlegacycode.com/dd0dec2a3e439b38373901a1556b532d/you-know-nothing.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--soXqo1hz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://understandlegacycode.com/dd0dec2a3e439b38373901a1556b532d/you-know-nothing.gif" alt="" width="500" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you don't know, you have plenty of questions. This is where you can detect what's missing in the existing docs.&lt;/p&gt;

&lt;p&gt;Original developers can't unlearn what they know, they won't realize things are not obvious to you!&lt;/p&gt;

&lt;p&gt;Ask questions to the leaving developers. Document that knowledge. Make the leaving developers review the documentation.&lt;/p&gt;

&lt;p&gt;Documentation can (should) take many forms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;formal documents&lt;/li&gt;
&lt;li&gt;comments in the code to clarify the intent of the tricky bits&lt;/li&gt;
&lt;li&gt;class and interaction diagrams at different levels of abstraction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can make this massively easier by &lt;strong&gt;pairing&lt;/strong&gt;: the leaving developer explains and the new developer writes the documentation. Reformulating is the best indicator of successful knowledge transfer 👌&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Make it fast and fun for the leaving developers
&lt;/h3&gt;

&lt;p&gt;Did you notice how I suggested making the previous practice easier? It's deliberate.&lt;/p&gt;

&lt;p&gt;You'll realize that leaving developers' motivation will go down as their last day comes over.&lt;/p&gt;

&lt;p&gt;Leverage that by making them do the &lt;em&gt;exciting&lt;/em&gt; stuff. E.g. let them code while pairing with them if that's what they like.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) Write more tests to understand the system
&lt;/h3&gt;

&lt;p&gt;Most of the time, the system will be lacking tests.&lt;/p&gt;

&lt;p&gt;Find out what are the critical parts of the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is critical for the business?&lt;/li&gt;
&lt;li&gt;what parts are often impacted by bugs?&lt;/li&gt;
&lt;li&gt;what parts will be impacted by incoming features?&lt;/li&gt;
&lt;li&gt;where are &lt;a href="//../focus-refactoring-with-hotspots-analysis"&gt;the Hotspots&lt;/a&gt; of the system?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing these tests is a great investment for your future work. Not only, but it will pay off later, it's also a great way to understand how the system works and discover the most important quirks you'll have to deal with.&lt;/p&gt;

&lt;p&gt;Write more tests to understand the system and reveal potential bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  6) Try to predict where fixes should be made
&lt;/h3&gt;

&lt;p&gt;Take the list of old bugs and practice this exercise with new developers: &lt;strong&gt;can they accurately predict where the problem was?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If they can't, you probably have identified important knowledge that needs to be transferred.&lt;/p&gt;

&lt;p&gt;That's a great way to prioritize knowledge to transfer outside of doing a guided tour of the codebase.&lt;/p&gt;

&lt;p&gt;You'll likely have to deal with subparts of the system that fail to stabilize. That is, bugs keep appearing in the same subparts of the code. Identifying these will make the new team much more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  7) Don't refactor code without a specific goal
&lt;/h3&gt;

&lt;p&gt;This final advice is more of a warning.&lt;/p&gt;

&lt;p&gt;Sure, there is plenty of Tech Debt in this Legacy Code that's handed over. And maybe you've negotiated 2 Sprints of knowledge transfer. But spending this time refactoring the code isn't a good idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactoring code without a clear goal quickly becomes a bottomless pit&lt;/strong&gt; that ends up using way more of your available time.&lt;/p&gt;

&lt;p&gt;You can't afford it!&lt;/p&gt;

&lt;p&gt;Efficient refactoring is oriented towards a goal: implementing a new feature, fixing a bug, etc. Don't refactor for the sake of it. Refactoring isn't cost-effective to transfer knowledge.&lt;/p&gt;

&lt;p&gt;Focus on tests and documentation instead. This will set you in the best possible tracks 🚂&lt;/p&gt;

&lt;h2&gt;
  
  
  Remember the 7 practices
&lt;/h2&gt;

&lt;p&gt;Next time you participate in a code handover, these are the things that will get you the most bang for the buck:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get everything under version control&lt;/li&gt;
&lt;li&gt;Get management support&lt;/li&gt;
&lt;li&gt;New developers should write the docs&lt;/li&gt;
&lt;li&gt;Make it fast and fun for the leaving developers&lt;/li&gt;
&lt;li&gt;Write more tests to understand the system&lt;/li&gt;
&lt;li&gt;Try to predict where fixes should be made&lt;/li&gt;
&lt;li&gt;Don't refactor code without a specific goal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fdA19iB9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://understandlegacycode.com/be42341532b16387e74a2b5b922fc9fc/i-know-how-to-do-it.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fdA19iB9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://understandlegacycode.com/be42341532b16387e74a2b5b922fc9fc/i-know-how-to-do-it.gif" alt="" width="500" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Transitions are exciting because they mean "change". I know you'll make the best out of yours 👍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>legacycode</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Does refactoring Legacy Code pay off in your career?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Fri, 22 Jan 2021 02:14:54 +0000</pubDate>
      <link>https://dev.to/nicoespeon/does-refactoring-legacy-code-pay-off-in-your-career-16n6</link>
      <guid>https://dev.to/nicoespeon/does-refactoring-legacy-code-pay-off-in-your-career-16n6</guid>
      <description>&lt;p&gt;Legacy software is everywhere. In fact, if you are working on code that was deployed to production and bring money to your company, you're certainly dealing with it every day. And yet, it's not seen as &lt;em&gt;fun&lt;/em&gt; 😬&lt;/p&gt;

&lt;p&gt;Most developers I've talked to were more excited to work on new features, new projects, new concepts, new integrations… Something &lt;em&gt;new&lt;/em&gt;. Something to &lt;em&gt;make&lt;/em&gt;. Mending existing code might be a necessary chore, but building new things is what's exciting.&lt;/p&gt;

&lt;p&gt;Companies are looking for &lt;em&gt;makers&lt;/em&gt;. Developers who will architect, design, build, and deliver the next generation of features with exciting new technologies! Shipping, with high-quality for sure, brand new products to customers and outperform the competition!&lt;/p&gt;

&lt;p&gt;And there you are, stuck maintaining that 10-year-old software weeks after weeks. You wish you could try out some GraphQL like the cool kids. But you've to compose with these PostgreSQL databases that were &lt;del&gt;patched&lt;/del&gt; fine-tuned over the years.&lt;/p&gt;

&lt;p&gt;If only you could sell the idea of rewriting part of the app to use a more modern stack… Unfortunately, there's no budget for that. Some managers can't see outside of the "what works" area. Now you're afraid to be left behind with the tools and frameworks, relegated to minor bug fixes instead of honing your skills!&lt;/p&gt;

&lt;p&gt;Now, you have been committed to writing high-quality, well-tested software. Relentlessly improving the status quo of this codebase. But this doesn't seem to lead to career advancement and that's frustrating.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Managers with non-technical background don't see the benefits of maintainable code!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I must admit that seeing developers who rack up mountains of technical debt get praised and promoted feels unfair, to say the least.&lt;/p&gt;

&lt;p&gt;But the truth is: you've been honing extremely valuable skills across these months working on this legacy codebase. What you need now is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To realize what these skills are&lt;/li&gt;
&lt;li&gt;To learn how to communicate them to non-technical people&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's go through this together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Funderstandlegacycode.com%2Fassets%2Fthere-is-hope.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Funderstandlegacycode.com%2Fassets%2Fthere-is-hope.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The skills you've been practicing without realizing
&lt;/h2&gt;

&lt;p&gt;First of all, &lt;strong&gt;persistence in front of challenges&lt;/strong&gt;. Having to constantly go in and change unfamiliar, undocumented, and poorly tested code is hard. And yet, you have been doing that for weeks, months, maybe years! It's normal to feel like quitting, and most developers do. We're lucky enough to have the option to jump from a company to another every year or so. If you have been maintaining a codebase for more than 12 months, you surely have a good lump of tenacity in you 👏&lt;/p&gt;

&lt;p&gt;After all this time working with a convoluted codebase, you surely have developed &lt;strong&gt;a spider-sense to detect code smells&lt;/strong&gt;. I won't pretend that being exposed to bad code makes you a great developer—it doesn't, exposure to &lt;em&gt;great&lt;/em&gt; code will teach you that. But now, you have experienced the maintenance issues some patterns can cause. I think that after spending weeks playing whack-a-mole with a bug that should have taken 2 days to be solved, you have a sense of how painful it is for the logic to be spread across the codebase. You may not know &lt;em&gt;how to solve&lt;/em&gt; that issue, but you can anticipate this will happen, from experience.&lt;/p&gt;

&lt;p&gt;Then, my own history of working with Legacy software showed me that you learn to &lt;strong&gt;think out of the box&lt;/strong&gt;. Because side-effects can happen everywhere, and you've been burned a few times already, you have learned to ask relevant questions when change requests come by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this bug occurring when there is only 1 element in the list?&lt;/li&gt;
&lt;li&gt;Can this ID field change in the partner system for some reason?&lt;/li&gt;
&lt;li&gt;What if I try to access this field with the keyboard?&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Coupling is subtle and spread like fire. It makes everything more complex than it looks. Hopefully, this has made you a more discerning developer.&lt;/p&gt;

&lt;p&gt;Finally, working with Legacy Code teaches you &lt;strong&gt;pragmatism&lt;/strong&gt;. Because everything is broken and you have limited time, you have to make tradeoffs. Sure this codebase isn't perfect, but can it run decently and serve customers in production? Sure a lot of things are broken, but what should be fixed first? Working with such a system is learning to embrace &lt;em&gt;better than yesterday&lt;/em&gt; over perfection. At some point, you have to focus on what matters: delivering value to people.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get credit for your work
&lt;/h2&gt;

&lt;p&gt;OK, I suspect at this point you may be cheered up, but doubtful this would make a difference in &lt;em&gt;the real world&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've lost many jobs due to my wanting to push a culture of quality over sloppy hacks…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you heard such stories? Or maybe &lt;strong&gt;you&lt;/strong&gt; have lived in such a situation.&lt;/p&gt;

&lt;p&gt;So what? What can you do when companies seem to prefer developers who have a semblance of high productivity over those who really know about "craftsmanship" and "best practice"?&lt;/p&gt;

&lt;p&gt;Actually, you &lt;em&gt;can&lt;/em&gt; do something. It's not about the skills—you already have that. It's about focusing on what matters.&lt;/p&gt;

&lt;p&gt;And this starts with you grasping something critical: &lt;strong&gt;it's not about doing craftsmanship, it's about &lt;em&gt;being&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;a craftsperson.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's be honest here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most employers don't care about craftsmanship. You'll hardly find job descriptions mentioning refactoring skills, despite these being critical to evolve existing systems.&lt;/li&gt;
&lt;li&gt;Most developers ignore there are better ways than the way they've done things for years. There are always new tools, new frameworks to learn. How come making it harder to access the global state would help them ship faster?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yet, I've seen a lot of folks willing to do better and fall for this last one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many software developers use "craftsmanship" and "best practices" as an excuse to over-engineer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"Pushing a culture of quality" isn't the strategy to adopt, unless you're in a leading position to have a privilege. I don't mean you should stop writing automated tests neither.&lt;/p&gt;

&lt;p&gt;My point is that the best way to spark a change towards a culture of quality is the same as getting credit for your hard work on Legacy Code: &lt;strong&gt;focus on the business&lt;/strong&gt; and &lt;strong&gt;lead by example&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus on the business
&lt;/h3&gt;

&lt;p&gt;It is usually the missing part in developers' argumentation.&lt;/p&gt;

&lt;p&gt;Why all of this matter for the end-user? For the company? Dig into the root cause. Use &lt;a href="https://en.wikipedia.org/wiki/Five_whys" rel="noopener noreferrer"&gt;the 5 whys&lt;/a&gt; technique to explore the potential impact of your actions. Maybe you can find data on how much some problem is costing the company (e.g. time spent on bugs per iteration).&lt;/p&gt;

&lt;p&gt;Taking care of this Legacy Code has a much more direct impact on existing customers (and company wallet!) than a greenfield project—that is generally a risky bet. So use the data you have to demonstrate the value of your work!&lt;/p&gt;

&lt;h3&gt;
  
  
  Lead by example
&lt;/h3&gt;

&lt;p&gt;It works, even if you aren't in a leadership position.&lt;/p&gt;

&lt;p&gt;I don't mean being stubborn in forcing your coding techniques over the rest of your team. I mean introducing better ways progressively in the code you write, proving the added benefits of doing so are greater than the cost of changing The Way It Has Always Been Done™.&lt;/p&gt;

&lt;p&gt;If your team doesn't like to pair-program as a rule, that's fine! Take the opportunity of a non-trivial question to dome some without naming it. If your colleague doesn't know how to test some part of the code, propose to spend a 30min timebox on this together. While you collaborate, share your tips of "doing it better" with them.&lt;/p&gt;

&lt;p&gt;Actually, this is how I introduced my teammates to using automated refactorings: "I see you've extracted this variable. If you want, I can show you how to do that almost instantly with your editor. Let's try?". That felt like sharing a handy trick. After a couple of mentions across a few weeks, they started doing it by themselves. But most of all, they realized there were better ways to do that kind of work. This sparked the idea that there might be more… ☀️&lt;/p&gt;

&lt;h2&gt;
  
  
  Working on Legacy Code can pay off in your career
&lt;/h2&gt;

&lt;p&gt;Having to deal with a Legacy codebase feels like a chore for many. Yet, it teaches you skills that are &lt;strong&gt;extremely valuable for your career.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There will probably always be opportunities to jump from greenfield projects to greenfield projects. "Makers" are everywhere.&lt;/p&gt;

&lt;p&gt;There will certainly always be open positions to maintain applications that are running in production and serving actual customers. "Menders" are needed. Good ones are not so common, because it's so tempting to always chase for the latest technology, leaving very little time to focus on the basics of building maintainable software.&lt;/p&gt;

&lt;p&gt;Being persistent, creative, and pragmatic: Legacy Code teaches you all of that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can't make it perfect, but you know how to constantly make it better.&lt;/strong&gt; That's why companies will desperately need &lt;strong&gt;you&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;All big, successful companies have a whole load of legacy. If they are still alive after all these years, they are not running on the latest tech. They require experienced developers to keep this working while growing. This is where &lt;strong&gt;you&lt;/strong&gt; can make a key difference compared to any other candidate. "&lt;em&gt;I'll rewrite everything with X&lt;/em&gt;" is less sexy than "&lt;em&gt;I can migrate your system smoothly to new high standards&lt;/em&gt;". That is a valuable skill, generally overlooked.&lt;/p&gt;

&lt;p&gt;Can you put an existing software into automated tests? Can you minimize risks while making changes to such software? Then you'll have plenty of jobs. Legacy code isn't about COBOL. There are already plenty of Rails and JS legacy codebases. Not everyone can afford to rewrite everything from scratch…&lt;/p&gt;

&lt;p&gt;Working on Legacy software does pay off. You need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To realize what you learned, what you are capable of&lt;/li&gt;
&lt;li&gt;To explain these skills to non-technical people, focusing on your impact on the business&lt;/li&gt;
&lt;li&gt;To inspire your fellow developers by solving the annoying problems with your techniques&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Don't stay alone
&lt;/h3&gt;

&lt;p&gt;My final advice would be to get in touch with other developers like you. You'll find plenty of great persons you can talk to in these 2 communities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://slack.softwarecrafters.org/" rel="noopener noreferrer"&gt;Software Crafters community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.legacycode.rocks/community" rel="noopener noreferrer"&gt;Legacy Code Rocks community&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://join.slack.com/t/wedotdd/shared_invite/zt-kq3cuils-tg6sS2eNoRIPjfPlsJJRoA" rel="noopener noreferrer"&gt;WeDoTDD community&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you join there, come and say hi to me 👋&lt;/p&gt;

&lt;p&gt;In any case, take care of yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
    </item>
    <item>
      <title>When is it the best time to refactor?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Tue, 01 Dec 2020 13:13:27 +0000</pubDate>
      <link>https://dev.to/nicoespeon/when-is-it-the-best-time-to-refactor-5b28</link>
      <guid>https://dev.to/nicoespeon/when-is-it-the-best-time-to-refactor-5b28</guid>
      <description>&lt;p&gt;One of the hardest things to teach is probably &lt;em&gt;when&lt;/em&gt; to refactor.&lt;/p&gt;

&lt;p&gt;With expertise, that's an answer you may feel in your guts: sometimes a piece of code should be refactored, and sometimes it doesn't worth it. But it's hard to stand by the "gut check" when you have to justify your score in a poker planning session!&lt;/p&gt;

&lt;h2&gt;
  
  
  Different approaches, different problems
&lt;/h2&gt;

&lt;p&gt;Some people follow the TDD cycle: red-green-refactor. There's a dedicated moment for refactoring the code that has just been written.&lt;/p&gt;

&lt;p&gt;But what about Legacy Code? When you inherited a large pile of tangled code, how do you know the cost of refactoring would be less than the cost of not refactoring?&lt;/p&gt;

&lt;p&gt;You can't calculate these costs. You can at best estimate them. People can do a decent job of comparing things, but we are not very good at estimating them. How do you know if the code you see will have to change? How do you estimate the number of bugs it will generate? Just like all estimation techniques, there are theories, sophisticated algorithms to try to tell you how much hours each code smell would take to clean up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2fxrfxbbvunffyv0cjiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2fxrfxbbvunffyv0cjiu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it's not because some code &lt;em&gt;could&lt;/em&gt; be refactored that it &lt;em&gt;should&lt;/em&gt; be refactored.&lt;/p&gt;

&lt;p&gt;What's good in refactoring if it takes you more time to deliver the value &lt;strong&gt;every single time&lt;/strong&gt;? If you constantly argue that the Story the team sized is larger because you need to refactor everything that might come across, is it really helpful?&lt;/p&gt;

&lt;p&gt;On the other hand, if you're barely able to see the whole function in your big widescreen monitor in portrait mode, then any change to this code is considerably longer and riskier to make.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F77951wemq7w9vbn1j3uu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F77951wemq7w9vbn1j3uu.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Could there be some common understanding of when to refactor, so you don't waste time doing it when it doesn't worth it, but don't skip it when it does?&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor continuously, but only what needs to be
&lt;/h2&gt;

&lt;p&gt;I think the best moment to refactor is before you need to change the code.&lt;/p&gt;

&lt;p&gt;You shouldn't refactor every tangled spaghetti you're coming across. You don't have to refactor the first time you write the code. You can't just plan days of refactorings for the sake of it.&lt;/p&gt;

&lt;p&gt;Instead, make refactoring part of implementing the feature or fixing the bug. It doesn't mean you should size every story larger because you want to refactor everything first—although you should probably add some points if you don't have a clear understanding of the change that needs to be made, that's another topic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refactor continuously. But only refactor what needs to be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What if you don't know the code enough to see what needs to be refactored? I suggest you first do some &lt;a href="https://understandlegacycode.com/blog/demine-codebase-with-exploratory-refactoring" rel="noopener noreferrer"&gt;Exploratory Refactoring&lt;/a&gt; to learn more about it. You won't keep these changes. You do that so you have a clearer idea about what actually needs to be changed.&lt;/p&gt;

&lt;p&gt;Wouldn't that make the code inconsistent if you only refactor a part of it? It probably will and that's fine. Your team can keep track of ongoing refactorings in a Markdown file, versioned with the source code.&lt;/p&gt;

&lt;p&gt;The rationale is that if you have a hard time selling refactoring time to your stakeholders, you really shouldn't. &lt;strong&gt;Refactoring isn't a separate activity, it's part of your work.&lt;/strong&gt; It doesn't mean that every single task should take twice the time because you will clean up everything. Take a pragmatic approach, refactor as you go, focus your refactoring on what needs to be changed.&lt;/p&gt;

&lt;p&gt;Don't ask for time to refactor. Don't make refactoring sounds like a constant waste of time because you wouldn't be able to work on the existing code. You refactor because it will make changes faster and safer. Don't get side-tracked by all the refactorings that could be made though. Practice cleaning up what's in the way, and only that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
    </item>
    <item>
      <title>The best way to start testing untested code</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Tue, 17 Nov 2020 12:53:05 +0000</pubDate>
      <link>https://dev.to/nicoespeon/the-best-way-to-start-testing-untested-code-5d6o</link>
      <guid>https://dev.to/nicoespeon/the-best-way-to-start-testing-untested-code-5d6o</guid>
      <description>&lt;p&gt;Are you working on a large codebase, with no test whatsoever, and no time is given to add some?&lt;/p&gt;

&lt;p&gt;"Automated tests will help you work faster", people say, "they reduce your time-to-fix when an issue occurs". But when there's no test, someone needs to take hours to write them first. If you ask management, of course, there's no time for that: the project is already behind schedule! Next iteration, maybe 🤷‍♂️&lt;/p&gt;

&lt;p&gt;The truth is: you are not really sure where to even &lt;em&gt;start&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Should you write E2E tests first, so you can cover more critical cases faster? Or should you focus on unit tests, because you've heard they are faster to run and more useful feedback while you're coding? Or maybe something in between?&lt;/p&gt;

&lt;p&gt;Changing Legacy Code is a specific situation that's very different from writing new code from scratch. You need to adapt your approach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RO1MpEOK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/working-around-legacy.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RO1MpEOK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/working-around-legacy.jpeg" alt="" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully, there's a way to quickly add tests on the code you need to change, so you don't even need to ask management permission first.&lt;/p&gt;

&lt;h2&gt;
  
  
  E2E tests or unit tests?
&lt;/h2&gt;

&lt;p&gt;End-to-end (E2E) tests exercise your application as a black box, from the end-user point of view. They typically require your app to be built and run in a production-like environment.&lt;/p&gt;

&lt;p&gt;At first sight, you may think that they would be ideal since they would cover a lot of code with every single test.&lt;/p&gt;

&lt;p&gt;In practice though, I find they take a lot of time to set up if you don't have some already. They also tend to run more slowly than you'd like and fail for many different reasons. Therefore, they are most suited to run on a CI than on your own machine while you're coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Their feedback is too long to be useful during development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;"Unit tests" definition is more debatable. I've found that people think differently about what a unit is. I prefer to talk about &lt;a href="https://understandlegacycode.com/blog/if-you-mock-are-you-even-testing/#isolated-tests"&gt;isolated tests&lt;/a&gt;. Nevertheless, I'm referring to the tests that are closer to the code, run fast, and are reliable.&lt;/p&gt;

&lt;p&gt;Such tests are great while developing. Done properly, they don't focus on implementation details and help you guide the design of the code. They provide you with extremely quick feedback on the code. At first sight, they seem the companion you need to deal with this legacy!&lt;/p&gt;

&lt;p&gt;In practice though, &lt;strong&gt;it's hard to retrofit existing code into unit tests&lt;/strong&gt;. It's easy to get the "unit" level wrong and test implementation details you wish to refactor in a moment. Trying to force unit tests on code that wasn't designed with test feedback can be time-consuming and solidify the design too much: tests get in the way, making your refactoring attempts even more difficult!&lt;/p&gt;

&lt;p&gt;Legacy Code is a special beast. In general, the most practical approach should be more nuanced. E2E or unit tests aren't the best candidates.&lt;/p&gt;

&lt;p&gt;So, what's the best way to add tests on untested code?&lt;/p&gt;

&lt;h2&gt;
  
  
  Close to the code, but not too much
&lt;/h2&gt;

&lt;p&gt;In this old-still-gold video on "Testing and Refactoring Legacy Code", Sandro Mancuso is showing you how to address existing, untested code that's doing calls to a database:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_NnElPO5BU0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In particular, he presents a strategy for adding tests to Legacy Code. I think it is excellent:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OsjAQTyW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rf3v6uz3e1kdwl5ye6gw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OsjAQTyW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rf3v6uz3e1kdwl5ye6gw.png" alt="Start testing from the shortest to deepest branch, refactoring from the deepest to the shortest" width="880" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I generalized this tip and recommend the following mindset:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Start testing from the outside of the code, then refactor from the inside.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, let's detail that a bit…&lt;/p&gt;

&lt;h3&gt;
  
  
  Start testing from the outside
&lt;/h3&gt;

&lt;p&gt;You don't know much about untested code.&lt;/p&gt;

&lt;p&gt;Start writing tests from the highest level you want to work with is easier. It also helps you not tie tests to the implementation details too much. It's a similar mindset to the E2E tests, except that the level can be anywhere in the code that you need to work with.&lt;/p&gt;

&lt;p&gt;The key idea is to quickly build a safety net to ensure that you capture the existing behavior of the code. The best way to do that is to use &lt;a href="https://understandlegacycode.com/approval-tests"&gt;Approval Testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Approval tests are also known as Golden Master, Characterization tests, or, more recently, Snapshot tests.&lt;/p&gt;

&lt;p&gt;Whatever name and tool you use to write them, Approval tests are composed of 4 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;em&gt;Arrange&lt;/em&gt; step, where you set up what's necessary to run the test&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;Act&lt;/em&gt; step, where you execute the code&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;Printer&lt;/em&gt; step, where you generate an output string from the effects of the code&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;Assert&lt;/em&gt; step, where you compare the new output with the existing one&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's like writing unit tests, but much faster. You don't need to understand everything the code is doing at the moment. But you're building a test suite you can rely on, so you can safely refactor code and know instantly when something goes wrong.&lt;/p&gt;

&lt;p&gt;It is a perfect compromise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactor from the inside
&lt;/h3&gt;

&lt;p&gt;Once you have covered enough cases to be safe, you can start refactoring code.&lt;/p&gt;

&lt;p&gt;The simplest (and usually fastest) way to do it is to tackle small chunks. Extract meaningful parts of the code that's covered with tests, and redesign them.&lt;/p&gt;

&lt;p&gt;Because the tests are more coarse-grained, you have more &lt;em&gt;flexibility&lt;/em&gt; to craft new classes/functions. &lt;strong&gt;Tests don't get in the way&lt;/strong&gt;, but they verify the overall behavior still works as expected. You can even replace some code with a whole new function that produces the same result!&lt;/p&gt;

&lt;p&gt;As you're refactoring the code, it's a good time to write better tests for the "units" you extract. It's easier to do because the chunks are smaller. These tests will capture your understanding of what these smaller pieces do, making it easier to work with this code, and potentially re-use it in a different context.&lt;/p&gt;

&lt;p&gt;When you don't have much time, &lt;strong&gt;capturing the behavior of existing code with Approval Tests is the best way to get started&lt;/strong&gt;. Pick a coarse chunk of code that covers what you need to change. It will help you refactor this code safely so you don't waste time debugging when something breaks.&lt;/p&gt;

&lt;p&gt;With practice, doing this may even be faster than not testing at all and getting things done with no bug!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
    </item>
    <item>
      <title>What you can do when code is really hard to review</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Wed, 04 Nov 2020 11:06:50 +0000</pubDate>
      <link>https://dev.to/nicoespeon/what-you-can-do-when-code-is-really-hard-to-review-1a6h</link>
      <guid>https://dev.to/nicoespeon/what-you-can-do-when-code-is-really-hard-to-review-1a6h</guid>
      <description>&lt;p&gt;Code reviews are one way to improve the quality of the code by having more than one pair of eyes on the code. When you're dealing with a Legacy codebase, it's a helpful practice to spread knowledge, get more people familiar with the codebase, and reduce risks of breaking things.&lt;/p&gt;

&lt;p&gt;But sometimes, you may stumble upon a &lt;em&gt;very hard&lt;/em&gt; code review. Maybe the pull-request is big, or the change involves many parts of the code.&lt;/p&gt;

&lt;p&gt;Maybe the change isn't really big, but &lt;strong&gt;it's a risky one&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;You feel that &lt;em&gt;this&lt;/em&gt; code is fragile and complex. You don't feel safe poking too hard on it. The team doesn't know much about the possible side-effects of such change. The test suite isn't really exhaustive neither. The change &lt;em&gt;seems&lt;/em&gt; to work, but it would take an excessive amount of time to verify the absence of any regression. Perhaps even more time than what it took to do the change itself!&lt;/p&gt;

&lt;p&gt;So here's the dilemma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you try to spot obvious flaws and get it merged, hoping nothing slips through the cracks of your imperfect knowledge?&lt;/li&gt;
&lt;li&gt;Or do you block the merge until you've tested extra-thoroughly the change against all scenarios you can imagine, risking the deadline?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do you do when code is too hard to review, but you don't have time to do everything again?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--toHQJQoR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/dont-panic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--toHQJQoR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/dont-panic.jpg" alt="" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  If it's hard to review, it can't skip the review!
&lt;/h2&gt;

&lt;p&gt;First, if you feel the change is so difficult that you won't have time to review it properly, then it's the very last code you should spend less time reviewing.&lt;/p&gt;

&lt;p&gt;"Merge and cross fingers nothing bad happens" is a notoriously bad idea. When doing so, you rely on luck. When you're out of luck, you fail. Some day, you fail badly.&lt;/p&gt;

&lt;p&gt;Think about it: if the code is hard to review now, it will harder to maintain in 6 months! You'll lose the context around the decisions that were taken here. Changes without context stay untouched because we're afraid of unintended consequences! Hopefully, &lt;a href="https://understandlegacycode.com/blog/earn-maintainers-esteem-with-adrs"&gt;there's a simple solution for that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yet, it's hard to come up with a different approach when you're under the pressure to ship.&lt;/p&gt;

&lt;p&gt;What can you do if you don't understand the impact of the change you're reviewing? I think you can reverse the steam and move the project in the right direction if you follow these 5 tips.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 tips to review difficult code changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Focus on readability
&lt;/h3&gt;

&lt;p&gt;I always introduce the Software Crafters Montreal meetup with a slide saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code is not how you tell the computer what to do; it is how you tell &lt;strong&gt;another programmer&lt;/strong&gt; what you want the computer to do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this makes the difference between &lt;em&gt;something that works&lt;/em&gt; and &lt;em&gt;something that can be changed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Therefore, the first thing you should focus on in your review is readability. Do you understand the big picture of the change? Forget about trying to find all possible bugs. Do you get what's going on here? What is the problem being solved, and how your team has decided to solve it with this change?&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Ask for proofs
&lt;/h3&gt;

&lt;p&gt;Can you see what will happen to the software after this change? Do you understand the intentions behind the changes?&lt;/p&gt;

&lt;p&gt;Manual tests could do. Often, a legacy codebase requires quite a lot of changes before you can add automated tests. In the short term, it might be faster to simply test manually. Before/after screenshots or a video reproducing the scenario in the PR description will do a lot more than trusting the code. Doing a QA of changed code before you merge it, while it's fresh, is a great moment to spot issues.&lt;/p&gt;

&lt;p&gt;Focus on that, require to see the QA of the final result.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Ask for automated proofs
&lt;/h3&gt;

&lt;p&gt;Manual tests could do, but if you really need to save time you should automate these tests. You won't have time to manually reproduce all scenarios &lt;strong&gt;every time&lt;/strong&gt; you do a change. This grunt work is for the computer. This is why you should push your team to get the code under tests.&lt;/p&gt;

&lt;p&gt;If there is no test, ask for them.&lt;/p&gt;

&lt;p&gt;If there are automated tests along with the change, make sure you understand what they verify. It's easy to write tests for the sake of it and overlook their readability. Good tests will help you understand what the code is supposed to do. They act as living documentation of the software.&lt;/p&gt;

&lt;p&gt;What if code needs a lot of changes to be testable and you don't have time? If you don't know how to do better, go on survival mode: do the best review you can do, and fall back on manual tests. But you should make a top priority for the next iterations to address this issue.&lt;/p&gt;

&lt;p&gt;Among all the technical debt you want to pay off, being able to write automated tests on the codebase is one of the most important ones! It's the one that will save you time and make deadlines easier to hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated tests will execute faster than you can manually test&lt;/li&gt;
&lt;li&gt;Changes covered with automated tests will less likely introduce a regression, so you spend less time debugging and fixing code&lt;/li&gt;
&lt;li&gt;Tests will pressure you to improve the design of the code—pay attention to what's painful to test, and make that easier&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Ask for smaller changes
&lt;/h3&gt;

&lt;p&gt;A common clue for difficult code reviews is when the diff is more than 500 LOC, yet the description is as tiny as "&lt;em&gt;Implement cancellations&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;In such a case, ask the change to be broken down into smaller chunks. A series of small, self-contained changes is less risky. One formula that usually works is to first ship all the updates that don't change how the code works, then ship a smaller change that implements the feature.&lt;/p&gt;

&lt;p&gt;Here's what you can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm having trouble following everything going on here. Could you break this patch into smaller steps?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will also make previous points easier. That's less room for mistakes!&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Speed up the process with a synchronous code review
&lt;/h3&gt;

&lt;p&gt;Here's the secret trick if you can't afford spending days on this review: take notes and go through the review with the author &lt;strong&gt;synchronously&lt;/strong&gt;. Do it IRL or schedule a call, share your screen, and go through it together.&lt;/p&gt;

&lt;p&gt;It will help you understand the logic faster. It will help you discuss the trade-offs. It will help you explain what is critical to be changed vs. what is merely a nice to have.&lt;/p&gt;

&lt;p&gt;Having a synchronous code review is a great way to save all the waiting time between each back-and-forth of asynchronous comments. The discussion is smoother and the end result is much more productive! You will come up with a concrete plan to get this merged safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus tip: spark the change!
&lt;/h2&gt;

&lt;p&gt;If you have the power and the budget, you should really reject such a change. If the level of quality can't be proven, it's a very risky move to get such an uncontrolled change in the codebase.&lt;/p&gt;

&lt;p&gt;Maybe you're in a Senior or technical leading position. If so, you can help your team do a better job on the log run. You can make the situation progressively improve instead of letting things getting worse and worse. What will happen if you don't hit the deadline? Does it worth delivering software that may or may not work? Can you buy some time to clean things up for the next iteration?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investing in people understanding the code pays off over time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don't have such power, you can use my 5 previous tips to deal with such changes. Don't block, but ask the right questions. Focus on what matters: not just producing code, but delivering value to the end-user.&lt;/p&gt;

&lt;p&gt;The good part of Legacy software is that you already have people depending on it. So anything you do to improve it will have an actual impact. It's your opportunity to use your skills to help people. I find that to be a motivating challenge, don't you?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>legacycode</category>
    </item>
    <item>
      <title>Approval Tests, TCR, and Menders</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Mon, 28 Sep 2020 12:24:41 +0000</pubDate>
      <link>https://dev.to/nicoespeon/approval-tests-tcr-and-menders-1op2</link>
      <guid>https://dev.to/nicoespeon/approval-tests-tcr-and-menders-1op2</guid>
      <description>&lt;p&gt;On September 9, 2020, I co-organized the 3rd edition of The Legacy of SoCraTes with my partner in crime &lt;a href="https://twitter.com/adibolb"&gt;Adrian Bolboacă&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's a half-a-day remote conference where 5 speakers share their tips to deal with Legacy Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lqaH281K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/legacy-of-socrates-3rd-edition.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lqaH281K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/legacy-of-socrates-3rd-edition.png" alt="The Legacy of SoCraTes" width="880" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zBOW3w2K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/legacy-of-socrates-organizers.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zBOW3w2K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/legacy-of-socrates-organizers.png" alt="by Nicolas Carlo and Adrian Bolboacă" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This edition had an accent on bringing existing code under tests. We talked about Approval Testing a lot. I &lt;a href="https://understandlegacycode.com/approval-tests"&gt;have a dedicated page for that&lt;/a&gt;. These talks are a great opportunity to learn more and &lt;strong&gt;see it in action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Without further ado, let me tell you how each talk can help you deal with your existing codebase.&lt;/p&gt;
&lt;h2&gt;
  
  
  A deep dive into the Gilded Rose
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/branaby"&gt;Barney Dellar&lt;/a&gt; kick-started this conference by demonstrating the Approval Testing technique, on the Gilded Rose refactoring kata, in C++.&lt;/p&gt;

&lt;p&gt;I like this talk because it shows the technique in action, on concrete code.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://understandlegacycode.com/blog/5-coding-exercises-to-practice-refactoring-legacy-code/#1-the-gilded-rose"&gt;Gilded Rose is an exercise I recommend doing&lt;/a&gt; if you want to get better at refactoring existing code.&lt;/p&gt;

&lt;p&gt;Watch him get the code under test in no-time:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1bJzZvGLVzc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Approval Testing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Approval Testing, again?!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, this was intentional. &lt;a href="https://twitter.com/emilybache"&gt;Emily Bache&lt;/a&gt; follow-up on Barney's talk to go further with the techniques.&lt;/p&gt;

&lt;p&gt;She has been using this technique in lots of situations for many years. It's especially useful when working with legacy code. But there are common pitfalls to avoid and best practice to learn.&lt;/p&gt;

&lt;p&gt;Oh and if you don't know Emily, she's the expert in coding exercises. Most of the refactoring katas I would recommend can be found in her GitHub, in many different languages: &lt;a href="https://github.com/emilybache?tab=repositories&amp;amp;q=refactoring"&gt;github.com/emilybache?tab=repositories&amp;amp;q=refactoring&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch her talk to see how she compares unit tests and approval tests methodologies:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0ZVKcFsEp-4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Did you noticed she uses a similar definition of Legacy code of mine:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Valuable code that works, developers find it difficult and we need to change it safely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7 Techniques to understand Legacy Code
&lt;/h2&gt;

&lt;p&gt;This time, &lt;a href="https://understandlegacycode.com/blog/7-techniques-to-regain-control-of-legacy/"&gt;these techniques are not from me&lt;/a&gt; 😁&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/JoBoccara"&gt;Jonathan Boccara&lt;/a&gt; is the author of &lt;a href="https://leanpub.com/legacycode"&gt;The Legacy Code Programmer's Toolbox&lt;/a&gt;. It's one of the rare books that give you practical skills when you have to deal with Legacy code.&lt;/p&gt;

&lt;p&gt;In this talk, Jonathan presents 7 techniques that can be handy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Know your I/O frameworks&lt;/li&gt;
&lt;li&gt;Find a stronghold&lt;/li&gt;
&lt;li&gt;Analyze call stacks&lt;/li&gt;
&lt;li&gt;Start reading from the outputs&lt;/li&gt;
&lt;li&gt;Count words (I wrote &lt;a href="https://understandlegacycode.com/blog/reveal-long-methods-structure-with-online-word-counter/"&gt;about this one in details&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Filter on control flow&lt;/li&gt;
&lt;li&gt;Scan for the main action&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See them in action in his talk:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/OzwQXGLWI0g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  TCR is your best friend against legacy
&lt;/h2&gt;

&lt;p&gt;Test, Commit, or Revert (TCR) is a technique that was popularized by Kent Beck. You can think about it as a variant of TDD, pushing the programmer to do very small changes.&lt;/p&gt;

&lt;p&gt;My friend &lt;a href="https://twitter.com/khaledsouf"&gt;Khaled Souf&lt;/a&gt; had the opportunity to &lt;strong&gt;actually use it&lt;/strong&gt; on a Legacy rescue project. In this talk, he shares his experience and explains why it's really efficient when you're dealing with existing, untested code:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/DUSdArEttg0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Makers vs Menders
&lt;/h2&gt;

&lt;p&gt;Finally, I had the pleasure to host &lt;a href="https://twitter.com/mscottford"&gt;M. Scott Ford&lt;/a&gt; to talk about developers' mindsets.&lt;/p&gt;

&lt;p&gt;We're all familiar with "makers". But it takes a different state of mind to really enjoy &lt;em&gt;mending&lt;/em&gt; code.&lt;/p&gt;

&lt;p&gt;Scott presents 4 different traits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The rapid prototyper&lt;/li&gt;
&lt;li&gt;The feature builder&lt;/li&gt;
&lt;li&gt;The emergency responder&lt;/li&gt;
&lt;li&gt;The software remodeler&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;He goes into the details of what are the strengths and weaknesses of each, and in which case each is best suited.&lt;/p&gt;

&lt;p&gt;I personally don't like putting people inside boxes. We made that clear with Scott as we discussed how each of us can be in many boxes at the same time, or switch depending on the context.&lt;/p&gt;

&lt;p&gt;This is a really inspiring talk I recommend you to watch if you feel alone, fighting with your codebase:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/YW0BpCThjuM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you want to connect with other menders (like me!), I recommend you join the &lt;a href="https://www.legacycode.rocks/"&gt;Legacy Code Rocks community&lt;/a&gt;. There is a Slack group and a weekly virtual meetup.&lt;/p&gt;

&lt;p&gt;If you join there, come and say hi to me 👋&lt;/p&gt;

&lt;h2&gt;
  
  
  "These talks are really great, can we have more?"
&lt;/h2&gt;

&lt;p&gt;Sure thing!&lt;/p&gt;

&lt;p&gt;There's no fixed schedule, but I like to organize this short conference every few months. I reach out to a few speakers to build an interesting agenda, then we set up the event.&lt;/p&gt;

&lt;p&gt;Everything is FREE. Attending is the opportunity to directly ask questions to these speakers, from the comfort of your home.&lt;/p&gt;

&lt;p&gt;If that's something you'd like to hear about, &lt;strong&gt;subscribe to my newsletter&lt;/strong&gt; below 👇&lt;/p&gt;

&lt;p&gt;I publish my weekly tips on Legacy Code here. I also tell my subscribers in advance about the conference and the agenda, so they don't miss out. Join them!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
      <category>conference</category>
    </item>
    <item>
      <title>4 developers working their way through Legacy Code</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Thu, 24 Sep 2020 12:57:38 +0000</pubDate>
      <link>https://dev.to/nicoespeon/4-developers-working-their-way-through-legacy-code-4o0e</link>
      <guid>https://dev.to/nicoespeon/4-developers-working-their-way-through-legacy-code-4o0e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I'm wondering how my way of working through legacy code may be different from someone more seasoned…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ooooh I feel you. That is a very, very common thought to have my friend 😃&lt;/p&gt;

&lt;p&gt;In fact, most developers are only exposed to Legacy Code at work. You have to maintain an existing codebase because that's what is bringing money to the company. After all, &lt;a href="https://understandlegacycode.com/blog/what-is-legacy-code-is-it-code-without-tests#code-youre-not-comfortable-changing"&gt;Legacy Code is &lt;em&gt;valuable&lt;/em&gt; code you're afraid to change&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are lucky enough, you'll be working with seasoned developers who can teach you their tricks. But this is not very common.&lt;/p&gt;

&lt;p&gt;So how do you know if you're doing it right? What can you do to make it easier to deal with Legacy Code?&lt;/p&gt;

&lt;p&gt;Let me rephrase that: what can you do to make it easier to &lt;em&gt;perform&lt;/em&gt; working on Legacy Code?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You practice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--duvtjxXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/acrobatic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--duvtjxXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://understandlegacycode.com/assets/acrobatic.jpg" alt="Acrobats doing a pose" width="880" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever felt impressed by how acrobats make their moves look so easy, but you &lt;em&gt;know&lt;/em&gt; it's damn hard?&lt;/p&gt;

&lt;p&gt;The thing is: performers spend most of their time practicing.&lt;/p&gt;

&lt;p&gt;We, developers, spend most of our time performing. Little time is dedicated to actual practice. You know, trying some new moves, repeating them, perfecting them. Exercising our skills. Making it so when we are on the battlefield, we instinctively know what could work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practicing is key in making Legacy Code easier to deal with.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's briefly cover the few questions that are popping in your head:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Where to practice?&lt;/em&gt; On coding exercises that are designed for this. &lt;a href="https://understandlegacycode.com/blog/5-coding-exercises-to-practice-refactoring-legacy-code"&gt;Here's the list of 5 refactoring katas&lt;/a&gt; I recommend.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;When to practice?&lt;/em&gt; We don't all have the luxury to take on our personal time to do so. But your employer certainly has to train you every year. They have a budget to spend on this! Use it to develop skills that will help you in the long run 😉&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;What to practice?&lt;/em&gt; This is a very good question. Practicing is good, but it's better if someone can show you the moves…&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4 developers who show you what to practice
&lt;/h2&gt;

&lt;p&gt;When you're trying to learn new skills, it's a good idea to be looking for exposure to more experienced people on the topic.&lt;/p&gt;

&lt;p&gt;If you're a visual learner, then it can be really helpful to &lt;em&gt;see&lt;/em&gt; someone practice. Personally, it helps me understand how to apply techniques in front of actual code. Theory is good, but I prefer when it gets in contact with reality.&lt;/p&gt;

&lt;p&gt;It's not easy to find good resources online on that very topic.&lt;/p&gt;

&lt;p&gt;To help you save some time (and get you into practicing), here's my selection of 4 resources that will expose you to seasoned developers working their way through Legacy Code!&lt;/p&gt;

&lt;h3&gt;
  
  
  "Testing and Refactoring Legacy Code" from Sandro Mancuso
&lt;/h3&gt;

&lt;p&gt;This one is in the "old, still gold" category 🏅&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_NnElPO5BU0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It shows you useful refactoring moves on the Trip Service kata, dropping some tips doing so.&lt;/p&gt;

&lt;h3&gt;
  
  
  A 6-parts series on refactoring Legacy Code, from Jovche Mitrejchevski
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1ap11RbVGik"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I really like this one because Jovche is demonstrating how to behave in front of a code you never saw before.&lt;/p&gt;

&lt;p&gt;A lot of good refactoring techniques are presented in these videos, including different approaches to the same problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  A step-by-step refactoring in 2 parts, from Dave Farley
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/p-oWHEfXEVs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You may already know Dave Farley. He's the co-author of "&lt;a href="https://martinfowler.com/books/continuousDelivery.html"&gt;Continuous Delivery&lt;/a&gt;". You can tell he's a very seasoned developer.&lt;/p&gt;

&lt;p&gt;In these 2 videos, he shows how to address Legacy Code. Specifically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;a href="https://understandlegacycode.com/approval-tests"&gt;Approval Tests&lt;/a&gt; to quickly cover existing code with tests&lt;/li&gt;
&lt;li&gt;Removing clutter from the code with basic refactorings&lt;/li&gt;
&lt;li&gt;Reducing complexity by extracting methods that got composed together&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Refactoring techniques, from Gregor Riegler
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/yqzYLm27cGw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Gregor has a few videos like this on his channel.&lt;/p&gt;

&lt;p&gt;I like how they are straight to the point and digestible. It shows you a technique (e.g. Subclass &amp;amp; Override) that you can reproduce on a refactoring kata I mentioned previously.&lt;/p&gt;

&lt;p&gt;Do &lt;strong&gt;you&lt;/strong&gt; have videos you recommend for learning to work with Legacy Code? Let us know in comments 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
    </item>
    <item>
      <title>Where should you put the documentation?</title>
      <dc:creator>Nicolas Carlo</dc:creator>
      <pubDate>Wed, 09 Sep 2020 20:27:26 +0000</pubDate>
      <link>https://dev.to/nicoespeon/where-should-you-put-the-documentation-18gg</link>
      <guid>https://dev.to/nicoespeon/where-should-you-put-the-documentation-18gg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"Of course, there is never any documentation…"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the typical experience of a Legacy codebase.&lt;/p&gt;

&lt;p&gt;Sure, documentation is not fun to write. It often feels like a waste of precious time, because &lt;em&gt;we know&lt;/em&gt;. The problem is: we forget.&lt;/p&gt;

&lt;p&gt;Documentation is &lt;strong&gt;never&lt;/strong&gt; a priority for your project stakeholders. There are features to ship and bugs to fix! And then, one day, the lead developer who was working on the project for the last 5 years announces she's leaving in 2 months. You now have 60 days to learn how to live and work without her.&lt;/p&gt;

&lt;p&gt;So you start thinking about documenting everything she knows, all of the implicit knowledge. And very quickly comes up the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where should we put the documentation?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Should it be specific to the team? Should it live in the company's Confluence? Should you use the wiki that comes along GitHub/Gitlab? Are there tips that can help you? Traps you should avoid?&lt;/p&gt;

&lt;p&gt;Let's pause a moment here to think about what makes &lt;strong&gt;great&lt;/strong&gt; documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 traits of great documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Easy to find
&lt;/h3&gt;

&lt;p&gt;If knowledge is spread across wikis, google docs, emails, and Jira tickets, people will have a hard time finding the information they need.&lt;/p&gt;

&lt;p&gt;How do you know if documentation is easy to find? Well, there's a typical symptom when it's not: people frequently ask questions about things your team knows. Often, questions are the same. Because it's not clear where they can find the information &lt;em&gt;by themselves&lt;/em&gt;. Hunting for the answer will take longer (and more efforts) than raising the question and wait. That's a waste of time. Good documentation would fix that!&lt;/p&gt;

&lt;p&gt;When it's clear where the documentation is, and it's easy to search for specific information, you see less of that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aim for having your documentation in a single place&lt;/strong&gt;. A powerful search feature is a must.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Easy to read
&lt;/h3&gt;

&lt;p&gt;Basic formatting matters (and is usually given by most tools).&lt;/p&gt;

&lt;p&gt;But think about images, diagrams, and code. Are they easy to read? Is there a way to make some information stand out? Is there a way to hide details that would not be relevant for all readers?&lt;/p&gt;

&lt;p&gt;Adding information is simple. Keeping it relevant takes practice. Going to the point is a hard skill to earn, but it definitely worth it. Remove from your documentation what turns out to be unnecessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep information short, easy to skim. Make people practice writing docs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Speaking of writing…&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Easy to write
&lt;/h3&gt;

&lt;p&gt;This is usually overlooked, especially when it gets spoiled by heavy validation processes.&lt;/p&gt;

&lt;p&gt;Let me be clear on this one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The more friction you add to the process of updating the doc, the less updated it will be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Out-of-date documentation is untrustworthy, making people fall back to asking others instead of trusting it. Out-of-date information should be updated or deleted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fkKEkFKp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/49up7nvmwuqdoqtd268z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fkKEkFKp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/49up7nvmwuqdoqtd268z.jpg" alt="" width="586" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get rid of useless information. Add missing ones. Make newcomers update the onboarding documentation. Take questions as chances to clarify the doc!&lt;/p&gt;

&lt;p&gt;If updating the doc is frictionless, you'll save your team a loooot of time.&lt;/p&gt;

&lt;p&gt;You don't need a validation stamp from the Tech Lead to update the doc. Anyone should feel entitled to do it.&lt;/p&gt;

&lt;p&gt;2 things can help here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change history will make you stop being afraid of losing information by someone accidentally removing it.&lt;/li&gt;
&lt;li&gt;Frequent backups, in case something goes wrong&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Trust people in doing their job. Let them update the doc as if it was a living document.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, but WHERE to put the documentation?
&lt;/h2&gt;

&lt;p&gt;Following these 3 traits, I'd recommend keeping your documentation in 2 places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Everything that is codebase specific should be versioned along with the source code&lt;/strong&gt;. Instructions to help people work with the codebase should be here, in some README. I recommend you to &lt;a href="https://understandlegacycode.com/blog/earn-maintainers-esteem-with-adrs"&gt;document your architectural decisions here, with ADRs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else should be in a single place&lt;/strong&gt;. The exact solution doesn't matter. What matters is that it's consistent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is, if your company uses Confluence, then use Confluence. If there's no company standard on this, then make a decision for your team. Make it clear that all the documentation would be here.&lt;/p&gt;

&lt;h3&gt;
  
  
  What would be the best solution for documentation?
&lt;/h3&gt;

&lt;p&gt;If you don't have a place to put your documentation yet, or if you've decided to migrate from your existing solution, you should be looking for something that makes documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easy to find&lt;/li&gt;
&lt;li&gt;easy to read&lt;/li&gt;
&lt;li&gt;easy to write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As of 2020, I recommend going for &lt;a href="https://www.notion.so/"&gt;Notion&lt;/a&gt;. It ranks best in most of the characteristics that matter for building handy documentation, especially if you're working in software.&lt;/p&gt;

&lt;p&gt;Listing all reasons why Notion is my preferred choice would fill another blog post. But in short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;their search feature is powerful enough to make it easy to find information&lt;/li&gt;
&lt;li&gt;they handle formatting, embedding images, code syntax highlighting, calling out important bits, and hiding less relevant ones&lt;/li&gt;
&lt;li&gt;the content is editable in place, making it a breeze to write&lt;/li&gt;
&lt;li&gt;anything can be turned into anything else, so you can just dump what's in your head and then turn it into a more sophisticated page later&lt;/li&gt;
&lt;li&gt;comments and assignments make it great for collaborating&lt;/li&gt;
&lt;li&gt;they backup your content with change history, so you can go back in time if some information was deleted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're curious, have a look &lt;a href="https://www.notion.so/Showcase-of-useful-Notion-features-d344bc6bf3c64eb88522cd746a206077"&gt;at my showcase of useful Notion features&lt;/a&gt; for building company documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/Showcase-of-useful-Notion-features-d344bc6bf3c64eb88522cd746a206077"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KBjVfmbk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/skpkrujj5482iajf6dsh.png" alt="" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regardless of the solution you choose, don't get caught with perfectionism. Done is better than perfect. As long as you start putting useful knowledge in a single place that anyone can update, you're doing great!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://understandlegacycode.com/?utm_source=dev.to"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-4tRTTh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m0wce5zwrkm4854kvdbi.png" alt="Learn to understand Legacy Code with my weekly newsletter" width="678" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>refactorit</category>
      <category>legacycode</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
