<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rodrigo's Tech Life]]></title><description><![CDATA[Software best practices, clean code, testing and tutorials about how to become a great developer. Powered by my experience as a FullStack Software Engineer building UIs with React and APIs with Node]]></description><link>https://blog.rodrigomd.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 09:34:46 GMT</lastBuildDate><atom:link href="https://blog.rodrigomd.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Securing your GitHub Workflows with OIDC: Connection to AWS explained]]></title><description><![CDATA[We’ve all seen it: AWS keys sitting in GitHub Workflows. They might feel convenient at first, but if they ever leak, it’s a nightmare—especially when those keys have wide IAM permissions.
Rotating them every 90 days means generating new keys, updatin...]]></description><link>https://blog.rodrigomd.dev/github-actions-aws-oidc-explained</link><guid isPermaLink="true">https://blog.rodrigomd.dev/github-actions-aws-oidc-explained</guid><category><![CDATA[AWS]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[best practices]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Thu, 23 Oct 2025 03:00:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760970051127/29397a43-6ece-4de1-bd05-868d242aba0c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We’ve all seen it: AWS keys sitting in GitHub Workflows. They might feel convenient at first, but if they ever leak, it’s a nightmare—especially when those keys have wide IAM permissions.</p>
<p>Rotating them every 90 days means generating new keys, updating environments, and hoping nothing breaks in the process.</p>
<p>And really, why keep replacing long-term credentials every 90 days? That’s just turning long-term creds into short-term ones the hard way. Why not start with short-term credentials from the beginning and avoid the problem altogether?</p>
<h2 id="heading-how-does-oidc-work">How does OIDC work?</h2>
<p>That's where the OpenID Connect (OIDC) protocol comes in. Through a series of verifications between GitHub and AWS, it provides short-term credentials instantly to run the GitHub workflow and use resources from AWS.</p>
<p>This protocol has three main actors</p>
<ul>
<li><p>Github workflow</p>
</li>
<li><p>GitHub as an Identity provider</p>
</li>
<li><p>AWS STS as Authorizer and credentials issuer</p>
</li>
</ul>
<p>Let’s have a general picture of how the communication flows between these actors:</p>
<ol>
<li><p>GitHub, acting as an identity provider, issues a token when the GitHub workflow begins.</p>
</li>
<li><p>A GitHub Action <a target="_blank" href="https://github.com/aws-actions/configure-aws-credentials"><em>aws-actions/configure-aws-credentials</em></a> sends this OIDC token to <strong>AWS STS</strong> via the API call <code>AssumeRoleWithWebIdentity</code> , along with the IAM Role Arn and optional configurations like the TTL.</p>
</li>
<li><p><strong>STS validates</strong> the token against the OIDC identity provider you configured in AWS IAM, comparing its properties against the trust policy configured on the IAM Role, and returns short-term credentials in case of success.</p>
</li>
<li><p>The workflow now uses those short-term credentials to run AWS CLI commands, Terraform, CDK, etc, using the same permissions as the IAM role has defined</p>
</li>
</ol>
<p>Here is a Sequence-System Diagram of the above flow:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760973825754/c83b3c40-58e9-4666-8693-913e6e5fcf90.png" alt="sequenceDiagram     participant GA as GitHub Action (Workflow Runner)     participant GH as GitHub OIDC Provider     participant AWS as AWS STS (via IAM Role)      Note over GA: Workflow job starts in GitHub Action      GA-&gt;&gt;GH: Request OIDC token (includes: repository, ref, job info)     GH--&gt;&gt;GA: Return signed OIDC ID Token(JWT with claims:audience=sts.amazonaws.com,subject=repo:org/repo:ref,exp, etc.)      Note over GA: Runner now has a short-lived OIDC token (valid ~5 min)      GA-&gt;&gt;AWS: AssumeRoleWithWebIdentity(provides OIDC token + role ARN)     AWS--&gt;&gt;GA: Validate token against trust policy(checks issuer=token.actions.githubusercontent.com,audience, subject, repo, branch)      Note over AWS: If trust policy matches, continue. Otherwise reject.      AWS--&gt;&gt;GA: Return temporary STS credentials(AccessKeyId, SecretAccessKey, SessionToken)valid ~15 min      Note over GA: Runner now has short-term AWS credentials      GA-&gt;&gt;AWS: Use temporary credentials (e.g., deploy Lambda, push to S3, manage SQS)     AWS--&gt;&gt;GA: Acknowledge API calls with valid auth" class="image--center mx-auto" /></p>
<h2 id="heading-what-is-aws-sts">What is AWS STS?</h2>
<p>AWS <strong>STS</strong> (Security Token Service) is an AWS service that allows you to request <strong>temporary security credentials</strong> for AWS resources instead of using long-term IAM user access keys.</p>
<p>These temporary credentials are:</p>
<ul>
<li><p><strong>Short-lived</strong> (from a few minutes up to a maximum of 12 hours, depending on the request).</p>
</li>
<li><p><strong>Scoped</strong> to specific permissions (defined by IAM roles and policies).</p>
</li>
<li><p>Automatically <strong>expire</strong>, reducing the risk if they get exposed.</p>
</li>
</ul>
<p>It’s the key player on the OIDC implementation with AWS.</p>
<h3 id="heading-key-uses-of-aws-sts">Key uses of AWS STS:</h3>
<ol>
<li><p><strong>AssumeRole</strong> – Allows one AWS service, user, or external identity (like GitHub via OIDC) to temporarily act as another role with specific permissions.</p>
</li>
<li><p><strong>Federation</strong> – Provides temporary credentials for external users authenticated by identity providers (like Google, Active Directory, GitHub).</p>
</li>
<li><p><strong>Cross-account access</strong> – Lets you securely access resources in another AWS account without creating permanent users.</p>
</li>
<li><p><strong>IAM role session management</strong> – Useful for apps, CI/CD pipelines, or temporary jobs that only need short-lived access.</p>
</li>
</ol>
<p>Contrary to what you might think, this OIDC process falls under the second use case: Federation. This is because, initially, you don't have any AWS credentials. AWS federates an external identity into AWS to generate short-lived credentials. In other scenarios, you begin with AWS credentials and request temporary credentials to assume a role or start a new session for security reasons.</p>
<h2 id="heading-how-does-the-aws-sts-validation-process-works">How does the AWS STS validation process works?</h2>
<p>Here is a flowchart that describes in detail the validation process AWS STS performs to issue short-term credentials. This provides a deeper look into points 2 and 3 from the previous picture showing the general communication flow.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760973918728/87bc275b-2f88-431f-8820-d40717f16aa2.png" alt="flowchart TD   A[&quot;Start: STS receives AssumeRoleWithWebIdentity request with OIDC token + RoleArn&quot;] --&gt; B[&quot;Check: Does IAM account have an OIDC provider for this issuer?&quot;]   B --&gt;|No| Z[&quot;Reject: InvalidIdentityToken&quot;]   B --&gt;|Yes| C[&quot;Verify TLS certificate or thumbprint of OIDC provider endpoint&quot;]   C --&gt;|Invalid| Z   C --&gt;|Valid| D[&quot;Fetch JWKS from provider and verify JWT signature&quot;]   D --&gt;|Invalid signature| Z   D --&gt;|Valid signature| E[&quot;Validate JWT standard claims: iss, aud, exp, nbf, iat&quot;]   E --&gt;|Expired / invalid| Z[&quot;Reject: ExpiredToken or InvalidIdentityToken&quot;]   E --&gt;|Valid| F[&quot;Evaluate IAM role trust policy conditions against token claims (sub, repository, ref, aud, etc.)&quot;]   F --&gt;|Claims don't match| Z[&quot;Reject: IDPRejectedClaim&quot;]   F --&gt;|Claims match| G[&quot;Issue temporary STS credentials (AccessKeyId, SecretAccessKey, SessionToken, Expiration)&quot;]   G --&gt; H[&quot;Return credentials to caller&quot;]   H --&gt; I[&quot;Caller uses temporary credentials for AWS API calls until expiration&quot;]   I --&gt; J[&quot;End&quot;]" class="image--center mx-auto" /></p>
<p>In simple terms, the validation process goes like this:</p>
<ul>
<li><p>AWS checks whether it has configured GitHub as an identity provider.</p>
</li>
<li><p>It then verifies the authenticity of the OIDC token by checking if it was issued by GitHub and confirming its TLS certificate or thumbprint.</p>
</li>
<li><p>It verifies the token signature.</p>
</li>
<li><p>It compares the token payload claims against the properties set in the IAM Role trust policy, such as repository, branch, environment, sub, etc.</p>
</li>
<li><p>If everything is correct, AWS STS issues temporary credentials that are returned to the caller.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>OpenID Connect (OIDC) lets your GitHub Actions talk to AWS in a secure, short-lived, and trust-based way. No need to deal with long-term access keys ever again.</p>
<p>With OIDC, you can ditch static credentials, skip the rotation headaches, and set up fine-grained permissions that map directly to your repo and branches. It’s cleaner, safer, and follows AWS’s best practices for least privilege and temporary credentials.</p>
<p>So really, what are you waiting for to switch to OIDC?</p>
<h2 id="heading-next-steps">Next steps</h2>
<p>In the next article, I’ll walk you through how to set up OIDC between AWS and GitHub step by step, so you can securely assume roles without adding extra complexity.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Incremental Testing Library Migration: A Step-by-Step Guide: Part 1]]></title><description><![CDATA[At some point in your career, you’ll encounter a project developed by numerous developers who are no longer with the company. In such cases, you’ll likely discover a variety of tools used across the repositories, installed by different people over ti...]]></description><link>https://blog.rodrigomd.dev/incremental-testing-library-migration-a-step-by-step-guide-part-1</link><guid isPermaLink="true">https://blog.rodrigomd.dev/incremental-testing-library-migration-a-step-by-step-guide-part-1</guid><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Software Testing]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Mon, 25 Aug 2025 14:33:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756132339968/f94dbd79-774b-4c97-a830-5e5dfd04ad40.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>At some point in your career, you’ll encounter a project developed by numerous developers who are no longer with the company. In such cases, you’ll likely discover a variety of tools used across the repositories, installed by different people over time, often serving the same purpose.</p>
<p>That was my situation working for a client with a multi-repo project using numerous testing tools. We had Jest, Jasmine with Karma, and the trio: Mocha, Sinon, and Chai; for unit and integration tests in different repositories, plus other supporting tools like rewire and proxyquire, and mock utilities.</p>
<h2 id="heading-the-problem">The Problem</h2>
<p>Some problems with this setup are quite clear:</p>
<ul>
<li><p>Switching between libraries, even for the same task, because you need to work with code in different repos.</p>
</li>
<li><p>Many unnecessary package updates due to new features and outdated packages.</p>
</li>
<li><p>No unified way to write tests.</p>
</li>
<li><p>Constantly needing to check different documentation pages whenever you write a test.</p>
</li>
</ul>
<p>It was something:</p>
<p>❌ Difficult to work with</p>
<p>❌ Tedious</p>
<p>❌ Exhausting</p>
<p>For us, using a single testing library seemed a better option.</p>
<h2 id="heading-the-decision">The Decision</h2>
<p>As a team, we decided to standardize the testing tools across the repositories by migrating all tests to a single tool and removing the others.</p>
<p>We chose Jest because it is modern, stable, and has excellent documentation. We appreciated that it includes everything we need, like expectations and mock utilities, and offers many customization options if required. Additionally, our current team has extensive experience with Jest.</p>
<p>Another advantage is that it’s <a target="_blank" href="https://2024.stateofjs.com/en-US/libraries/testing/">widely used by developers</a>, making it easier to find someone who can start writing tests right away if needed.</p>
<h2 id="heading-disclaimer">🚧 Disclaimer</h2>
<p><strong>For simplicity, in this article, I used Jasmine as the old testing tool we wanted to migrate from in favor of Jest.</strong> However, the same principles and strategies discussed here can be, and in our case were, applied to <code>mocha</code> and other testing tools as well.</p>
<h2 id="heading-the-plan">The Plan</h2>
<p>If we were going to do this, we needed to make a few agreements.</p>
<p>Since we had hundreds of test files per repository, and migrating those files wasn't automatic or our main priority, we had to ensure they were migrated to Jest incrementally.</p>
<p>✅ Allow incremental migration</p>
<p>As the migration would happen slowly and incrementally, we had to ensure the project was never left unprotected. Regardless of the migration stage, the old and new testing tools should coexist and work together until the entire migration is completed.</p>
<p>✅ Never leave the project unprotected</p>
<p>Another consideration was maintaining a coverage threshold as a check in the CI pipeline. Therefore, during migration, we needed to keep a single coverage report to set the CI check, regardless of the migration progress.</p>
<p>✅ Keep a single coverage report</p>
<p>After considering the above rules, the resulting plan was divided into four main stages:</p>
<ol>
<li><p><strong>Set up →</strong> Install Jest and assign each tool a portion of the tests: new tests will run with Jest and old tests with Jasmine. Also, add a script to run both testing tools together with a single command.</p>
</li>
<li><p><strong>Merging →</strong> Combine testing results from Jest and the old testing library into one coverage report.</p>
</li>
<li><p><strong>Migrating →</strong> Gradually move old tests to Jest and remove them from the old test runner.</p>
</li>
<li><p><strong>Cleaning →</strong> Once all tests have been migrated, delete the old configurations and uninstall the old testing library and dependencies.</p>
</li>
</ol>
<p>This article will focus on the first two steps, as they allow us to safely begin migrating incrementally without leaving the project unprotected.</p>
<h2 id="heading-setting-up-the-two-testing-libraries-to-work-together-as-one">Setting up the two testing libraries to work together as one</h2>
<p>The idea is straightforward: we want to move from Jasmine to Jest. From now on, new tests should be written to work with Jest, while existing tests will continue to be handled by Jasmine until they have been successfully migrated to Jest.</p>
<p>First, install Jest and create a <code>jest.config.js</code> file at the root directory.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// jest.config.js</span>

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">collectCoverage</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">coverageDirectory</span>: <span class="hljs-string">'.coverage'</span>,
  <span class="hljs-attr">coverageReporters</span>: [<span class="hljs-string">'json'</span>, <span class="hljs-string">'text'</span>, <span class="hljs-string">'text-summary'</span>],
  <span class="hljs-attr">collectCoverageFrom</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/**/*.js'</span>],
  <span class="hljs-attr">testPathIgnorePatterns</span>: [<span class="hljs-string">'/node_modules/'</span>],
  <span class="hljs-attr">roots</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/'</span>]
};
</code></pre>
<p>To let these testing libraries know which tests they should handle, it's important to define a way to distinguish new tests from old ones and apply this distinction in the testing libraries' config files.</p>
<p>There are several ways to do this, so it's important to choose the right strategy to direct the test execution to be handled by both libraries.</p>
<h3 id="heading-subfolders-strategy-tests-folder">Subfolders strategy + tests folder</h3>
<p>One strategy, if your project already has a <code>tests</code> folder outside the source code, is to create a subfolder for each testing library and move the test files there. Initially, all tests will be in the Jasmine folder. As you start writing new tests and converting old ones from Jasmine to Jest, the Jest folder will gradually fill up, and the Jasmine folder will eventually become empty.</p>
<pre><code class="lang-jsx">project/
├── src/
│   └── auth/
│       ├── login.js
│       ├── signup.js
│       └── token.js
└── tests/
    ├── jasmine/
    │   └── auth/
    │       ├── login.spec.js              # Jasmine test
    │       └── token.spec.js              # Jasmine test
    └── jest/
        └── auth/
            ├── signup.spec.js             # ✅ Jest test
            └── passwordReset.spec.js      # ✅ Jest test
</code></pre>
<p>Following the file structure above, the config files should look like this:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// jest.config.js</span>

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">collectCoverage</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">coverageDirectory</span>: <span class="hljs-string">'coverage'</span>,
  <span class="hljs-attr">coverageReporters</span>: [<span class="hljs-string">'json'</span>, <span class="hljs-string">'text'</span>, <span class="hljs-string">'text-summary'</span>],
  <span class="hljs-attr">collectCoverageFrom</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/**/*.js'</span>],
  <span class="hljs-attr">testPathIgnorePatterns</span>: [<span class="hljs-string">'/node_modules/'</span>],
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">'tests/jest/**/*.spec.js'</span>],
  <span class="hljs-attr">roots</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/'</span>]
};
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// jasminerc.json</span>
{
  <span class="hljs-string">"random"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-string">"spec_dir"</span>: <span class="hljs-string">"test"</span>,
  <span class="hljs-string">"spec_files"</span>: [
    <span class="hljs-string">"tests/jasmine/**/*.spec.js"</span>
  ],
  <span class="hljs-string">"helpers"</span>: [
    <span class="hljs-string">"helpers/**/*.js"</span>,
  ]
}
</code></pre>
<p>We configure each library to find and run tests only within its respective subfolders.</p>
<h3 id="heading-suffix-strategy">Suffix strategy</h3>
<p>Another strategy, if you keep test and production code in the same folder, is to leave the test files in their original location and add a suffix to the old test files to show which testing library was used to run them. This will help us indicate to the old testing library how to find them.</p>
<p>For new tests, the extra suffix is not needed because we will configure Jest to only look for tests without the <code>.jasmine</code> suffix. These will be identified as new tests to be run by Jest.</p>
<pre><code class="lang-jsx">project/
└── src/
    └── auth/
        ├── login.js
        ├── signup.js
        ├── token.js
        ├── login.jasmine.spec.js           # Jasmine test
        ├── token.jasmine.spec.js           # Jasmine test
        ├── signup.spec.js                  # ✅ Jest test
        └── passwordReset.spec.js           # ✅ Jest test
</code></pre>
<p>To instruct Jest to run all tests except those with the <code>.jasmine</code> suffix, we need to add that expression to the <code>testPathIgnorePatterns</code> property.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// jest.config.js</span>

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">collectCoverage</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">coverageDirectory</span>: <span class="hljs-string">'coverage'</span>,
  <span class="hljs-attr">coverageReporters</span>: [<span class="hljs-string">'json'</span>, <span class="hljs-string">'text'</span>, <span class="hljs-string">'text-summary'</span>],
  <span class="hljs-attr">collectCoverageFrom</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/**/*.js'</span>],
  <span class="hljs-attr">testPathIgnorePatterns</span>: [<span class="hljs-string">'/node_modules/'</span>,  <span class="hljs-string">'/src/**/.*\\\\.jasmine\\\\.spec\\\\.js'</span>],
  <span class="hljs-attr">roots</span>: [<span class="hljs-string">'&lt;rootDir&gt;/src/'</span>]
};
</code></pre>
<p>For Jasmine, we need to set it up to run only the tests with the <code>.jasmine</code> suffix and skip the ones without it.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// jasminerc.json</span>
{
  <span class="hljs-string">"random"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-string">"spec_dir"</span>: <span class="hljs-string">"test"</span>,
  <span class="hljs-string">"spec_files"</span>: [
    <span class="hljs-string">"src/**/*.jasmine.spec.js"</span>,
    <span class="hljs-string">"!src/**/*[!jasmine].spec.js"</span>
  ],
  <span class="hljs-string">"helpers"</span>: [
    <span class="hljs-string">"helpers/**/*.js"</span>,
  ]
}
</code></pre>
<h3 id="heading-suffix-strategy-tests-folder">Suffix strategy + tests folder</h3>
<p>The suffix strategy can also be used if your code organization mirrors the same file structure inside an external <code>tests</code> folder.</p>
<pre><code class="lang-jsx">project/
├── src/
│   ├── auth/
│   │   ├── login.js
│   │   ├── signup.js
│   │   └── token.js
│   └── payments/
│       ├── charge.js
│       └── refund.js
└── tests/
    ├── auth/
    │   ├── login.jasmine.spec.js        # Jasmine test
    │   ├── token.jasmine.spec.js        # Jasmine test
    │   └── signup.spec.js               # ✅ Jest test
    └── payments/
        ├── refund.jasmine.spec.js       # Jasmine test
        └── charge.spec.js               # ✅ Jest test
</code></pre>
<p>In this case, you don't need to create separate subfolders for each tool inside the <code>tests</code> folder, nor do you need to move the test files. The only requirement is to add the <code>.jasmine</code> suffix to each test file meant to run with Jasmine, no matter which folder they are in. All tests without that suffix should be run with Jest.</p>
<p>The resulting configuration in both files is the same as when using the suffix strategy, except that in this case, the tests should be looked at inside the <code>tests</code> folder rather than the <code>src</code> folder.</p>
<h3 id="heading-how-to-choose-the-correct-strategy-to-separate-the-test-execution">How to choose the correct strategy to separate the test execution</h3>
<p>It all depends on how suitable the strategy is for your tests files organization</p>
<ul>
<li><p><strong>Use</strong> <code>folders</code> when:</p>
<ul>
<li><p>You want a clean, enforced separation between legacy and new tests.</p>
</li>
<li><p>Your tests are <strong>already</strong> in a root-level <code>tests</code> folder.</p>
</li>
<li><p><strong>Your team prefers a</strong> <strong>directory-based organization,</strong> and you can afford some initial overhead.</p>
</li>
<li><p>You don’t mind moving files from one folder to another.</p>
</li>
</ul>
</li>
<li><p><strong>Use</strong> <code>suffix</code> when:</p>
<ul>
<li><p>You want minimal disruption to the existing organization.</p>
</li>
<li><p><strong>Your tests are tightly coupled with the source code in the same folders</strong>.</p>
</li>
<li><p>You need an easier and faster path with fewer moves, relying on <strong>naming conventions instead of reorganization.</strong></p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-write-the-npm-test-script-to-execute-both-test-runners-sequentially">Write the npm test script to execute both test runners sequentially</h3>
<p>Once Jest is set up to run the new test files and Jasmine is set to run the old test files, you just need to trigger both test runners with a single <code>npm test</code> command.</p>
<p>I also suggest adding two extra scripts to run each test runner separately. This will be helpful when you start migrating tests.</p>
<p>Your <code>package.json</code> scripts should look like this:</p>
<pre><code class="lang-jsx"><span class="hljs-string">"scripts"</span> {
    <span class="hljs-string">"jest"</span>: <span class="hljs-string">"npx jest"</span>,
    <span class="hljs-string">"jasmine"</span>: <span class="hljs-string">"npx jasmine"</span>,
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"npm run jest &amp;&amp; npm run jasmine"</span>
}
</code></pre>
<p>Now, when we run the tests, both testing libraries will execute one after the other. To prevent failures, it's important to configure them to pass even without tests. At the start, there will be no Jest tests, and after the migration, there won't be any tests written for Jasmine. Therefore, it's crucial to run the tests with a flag like <code>--passWithNoTests</code>.</p>
<h2 id="heading-merging-coverage-results-into-a-single-report">Merging coverage results into a single report</h2>
<p>Since we use the coverage report as a pipeline check, we need to keep using it even after making the two testing libraries work together. We must merge their coverage reports into a final report that includes all tests, regardless of whether they were written for the old or new testing tool.</p>
<p>In our project, we decided to use <code>nyc</code> to combine the coverage reports from Jest and Jasmine. We were already using it to collect coverage for Jasmine tests, and it offers two useful commands for merging reports: <code>merge</code> and <code>report</code>.</p>
<p>Since we noticed some inconsistencies with the merged report using the <code>merge</code> command, we opted to use the <code>report</code> command instead. We decided not to stress over the minor differences when comparing the coverage results to the original results from using just one testing tool.</p>
<blockquote>
<p>To correctly use the <code>report</code> command of <code>nyc</code>, ensure reports don't overwrite each other by storing them in different folders when possible and renaming files if they need to be in the same folder.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755989073321/b9cf15ff-ad61-45b3-9756-7136a266f4a7.png" alt class="image--center mx-auto" /></p>
<p>To merge the coverage reports, the plan goes like this:</p>
<ol>
<li><p>Run Jest and collect the coverage of Jest test files into a temporary <code>.coverage-jest</code> folder.</p>
</li>
<li><p>Run Jasmine and collect the coverage of Jasmine test files into a temporary <code>.coverage-jasmine</code> folder.</p>
</li>
<li><p>Copy both coverage reports into a single temporary folder <code>.coverage-merge</code>, and rename the files by adding a suffix to identify the testing tool that generated them, preventing overwriting.</p>
</li>
<li><p>Run the <code>nyc report</code> command, specifying the final thresholds and the temporary folder where the reports are located. Also, indicate the output folder where the merged coverage will be saved.</p>
</li>
<li><p>Clean up all temporary files and folders.</p>
</li>
</ol>
<blockquote>
<p>💡 It's important to set the coverage threshold as flags when calling <code>nyc report</code> and remove any threshold from the Jasmine and Jest configuration files. This is necessary because we need to validate the threshold across all files, not just a subset.</p>
</blockquote>
<p>If you follow the steps described here, you'll end up with a <code>package.json</code> similar to the one below—I hope one much better —. We use the <code>shx</code> package to run commands across different operating systems to delete, copy, and move files and folders.</p>
<pre><code class="lang-jsx"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"copy-coverage-jasmine"</span>: <span class="hljs-string">"npx shx mv .coverage-jasmine/reports/coverage-final.json .coverage-merge/coverage-jasmine.json"</span>,
    <span class="hljs-string">"copy-coverage-jest"</span>: <span class="hljs-string">"npx shx mv .coverage-jest/coverage-final.json .coverage-merge/coverage-jest.json"</span>,
    <span class="hljs-string">"copy-coverage-files"</span>: <span class="hljs-string">"npx shx mkdir .coverage-merge &amp;&amp; npm run copy-coverage-jasmine &amp;&amp; npm run copy-coverage-jest"</span>,
    <span class="hljs-string">"clean-tmp-coverage"</span>: <span class="hljs-string">"npx shx rm -rf .coverage-jasmine &amp;&amp; npx shx rm -rf .coverage-jest &amp;&amp; npx shx rm -rf .coverage-merge"</span>,
    <span class="hljs-string">"multi-coverage"</span>: <span class="hljs-string">"npm run nyc-jasmine &amp;&amp; npm run jest &amp;&amp; npm run copy-coverage-files &amp;&amp; nyc report --check-coverage --lines=80--branches=70 --functions=70 --statements=80 --reporter=lcov --reporter=text --reporter=text-summary --temp-directory=.coverage-merge --report-dir=./.coverage &amp;&amp; npm run clean-tmp-coverage"</span>
 }
</code></pre>
<p>With the scripts above, instead of running <code>npm run test:coverage</code>, you would use <code>npm run multi-coverage</code>. It's up to you if you want to make it explicit that the coverage is from multiple tools. And that's how you make two testing libraries work together, keep the coverage functional, and set the stage to gradually start migrating tests from one tool to another.</p>
<p>✍🏻A couple of notes to keep in mind</p>
<ul>
<li><p>The final coverage script runs sequentially. It depends on which testing tool you run first in your <code>npm test</code> script. So, if the first tool fails, the next one won't run.</p>
</li>
<li><p>Merging reports isn't perfect. There are <a target="_blank" href="https://github.com/istanbuljs/nyc/issues/1302">open issues about it</a>, so pick the command that gets the resulting coverage closest to your current value and adjust the thresholds accordingly.</p>
</li>
</ul>
<hr />
<h2 id="heading-up-next">Up next:</h2>
<ul>
<li><p>We’ll learn to use tools to make the migration less tedious</p>
</li>
<li><p>Advice on what to pay attention to and how to migrate the tests without leaving the project unprotected</p>
</li>
<li><p>How to get rid of all of this config to leave a single testing tool after the migration</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Run an LLM on your computer with Ollama, Ngrok, and Cursor]]></title><description><![CDATA[Here’s a quick how-to guide for running a large language model (LLM) locally using three tools: Ollama, Ngrok, and Cursor IDE. This setup is perfect if you want to test AI features without relying on OpenAI’s cloud, or if you're just nerding out and ...]]></description><link>https://blog.rodrigomd.dev/run-llm-locally-ollama-ngrok-cursor</link><guid isPermaLink="true">https://blog.rodrigomd.dev/run-llm-locally-ollama-ngrok-cursor</guid><category><![CDATA[llm]]></category><category><![CDATA[AI]]></category><category><![CDATA[ollama]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Tue, 20 May 2025 13:00:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747705758246/852ae3e1-2627-4672-b9ed-1e618eddeec2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here’s a quick how-to guide for running a large language model (LLM) locally using three tools: <strong>Ollama</strong>, <strong>Ngrok</strong>, and <strong>Cursor IDE</strong>. This setup is perfect if you want to test AI features without relying on OpenAI’s cloud, or if you're just nerding out and want more control.</p>
<h2 id="heading-tools-well-be-using">Tools We'll Be Using</h2>
<h3 id="heading-cursor-httpswwwcursorcomhttpswwwcursorcomfeatures">Cursor (🔗 <a target="_blank" href="https://www.cursor.com/features">https://www.cursor.com</a>)</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747711902787/6950b9e7-fbf6-48ce-9985-c474a317c299.png" alt class="image--center mx-auto" /></p>
<p>Cursor is a developer-focused IDE with AI built right in. You can switch between different models and agents, all powered by your OpenAI API key (unless you’re paying Cursor directly to use theirs).</p>
<p>Some standout features:</p>
<ul>
<li><p>Understands your codebase and adds context to prompts</p>
</li>
<li><p>Runs commands for you (with your approval)</p>
</li>
<li><p>Spot bugs and offers fixes</p>
</li>
<li><p>Autocompletes like a champ</p>
</li>
<li><p>Built-in smart reviews</p>
</li>
</ul>
<p>It’s VS Code on steroids, with some real AI muscle.</p>
<h3 id="heading-ngrok-httpsngrokcomhttpsngrokcom">Ngrok (🔗 <a target="_blank" href="https://ngrok.com/">https://ngrok.com</a>)</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747711978194/64a3c159-8c4c-4fbf-8c59-9fb50f63181b.png" alt class="image--center mx-auto" /></p>
<p>Ngrok is an API Gateway service that handles everything related to your API so that you focus on your business rules without worrying about secure connections and infrastructure. While it offers a full suite of infrastructure tools, we’ll focus on <strong>Webhook testing</strong>, which gives us a public URL to test a local server.</p>
<h3 id="heading-ollama-httpsollamacomhttpsollamacom">Ollama (🔗 <a target="_blank" href="https://ollama.com/">https://ollama.com</a>)</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712136731/23c04917-bfab-4c3b-9a45-364d69eeae40.png" alt class="image--center mx-auto" /></p>
<p>Ollama is a fantastic open-source tool that lets you run LLMs locally. You can pull supported models, serve them through a local API, and even customize or train your own.</p>
<p>Browse available models at <a target="_blank" href="https://ollama.com/library">ollama.com/library</a></p>
<h2 id="heading-lets-get-started">Let’s Get Started</h2>
<h3 id="heading-1-pull-llms-with-ollama">1. Pull LLMs with Ollama</h3>
<p>Once everything’s installed, the first step is to pick and download an LLM. Just a heads-up: LLMs are resource-hungry. Bigger models need more RAM and CPU, so make sure your machine can handle it.</p>
<p>To install a model:</p>
<pre><code class="lang-bash">ollama pull &lt;model&gt;
</code></pre>
<p>For example:</p>
<pre><code class="lang-bash">ollama pull gemma3:4b
</code></pre>
<p>You can check installed models with:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712460296/dbb95165-263d-467a-90b0-5cd84656a83f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-set-up-ngrok">2. Set Up Ngrok</h3>
<p>Create an Ngrok account and enable Multi-Factor Auth (highly recommended). Then, install the CLI and connect it with your account by running:</p>
<pre><code class="lang-plaintext">ngrok config add-authtoken &lt;your-token&gt;
</code></pre>
<p>The command above will set up your computer to connect securely with the Ngrok Web platform, but it won’t secure any endpoint you later expose.</p>
<p>🚧 Heads up: Your Ngrok endpoint exposes your local server to the internet. Even on the free plan, Ngrok gives you options like auth, IP filtering, and rate limits. Worth setting up later!</p>
<hr />
<h3 id="heading-3-run-the-ollama-server-with-ngrok">3. Run the Ollama Server with Ngrok</h3>
<p>Ollama can serve your model through a local API. Start it like this:</p>
<pre><code class="lang-plaintext">OLLAMA_ORIGINS=* ollama serve
</code></pre>
<p>By default, it runs on port <code>11434</code>.</p>
<p>Now expose that port with Ngrok:</p>
<pre><code class="lang-plaintext">ngrok http 11434 --host-header="localhost:11434"
</code></pre>
<p>Ngrok will generate a public HTTPS URL, called “endpoint” in the ngrok language, that maps to your local server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712830076/525fa7e0-9165-4133-bf11-51c683c5483e.png" alt class="image--center mx-auto" /></p>
<p>We now have a public URL to connect us from the internet to our local Ollama server, It’s time to tell Cursor how to use this URL to hit our Ollama server locally.</p>
<hr />
<h3 id="heading-4-connect-ollama-to-cursor-ide">4. Connect Ollama to Cursor IDE</h3>
<h4 id="heading-add-your-model">Add Your Model</h4>
<p>In Cursor, go to <strong>Settings &gt; Cursor Settings &gt; Models</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712883769/89331623-cfcf-4e48-9aa2-e6e25fafacc7.png" alt class="image--center mx-auto" /></p>
<p>You’ll see some defaults, but they likely won’t match your locally installed model names. Hit <code>+ Add Model</code> and enter the exact model name from <code>ollama list</code>.</p>
<h4 id="heading-override-openai-api-base-url">Override OpenAI API Base URL</h4>
<p>Scroll to the <strong>OpenAI API Key</strong> section and expand <strong>Override OpenAI Base URL</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712956275/a37fb5b2-24be-46f0-ab22-b861500b1a03.png" alt class="image--center mx-auto" /></p>
<p>Paste your Ngrok HTTPS URL here. You can put anything in the API key field, it’s not validated in this case since our API doesn’t have any authentication method enabled yet.</p>
<hr />
<h3 id="heading-5-test-the-setup">5. Test the Setup</h3>
<p>To make sure everything works, you should:</p>
<ul>
<li><p>✅ Be running the Ollama server</p>
</li>
<li><p>✅ Have Ngrok expose port 11434</p>
</li>
<li><p>✅ Have your LLM registered in Cursor</p>
</li>
<li><p>✅ Set the Ngrok URL in the API override</p>
</li>
</ul>
<p>Now, toggle the <strong>Enable OpenAI API Key</strong> slider in Cursor’s settings. It’ll ping your API to confirm the connection.</p>
<p>If there's an error, Cursor will pop up the response — super useful for debugging.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747712982903/0e769d2a-2a14-41a8-8365-6ab3c6038bb1.png" alt class="image--center mx-auto" /></p>
<p>Once it connects, you’re good to go! Start chatting with your local LLM in the left panel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747713013716/8d7985cb-cf78-40f9-ba28-97ac78c85faf.png" alt class="image--center mx-auto" /></p>
<p>Make sure to:</p>
<ul>
<li><p>Switch prompt mode to <code>Ask</code> or <code>Manual</code></p>
</li>
<li><p>Disable automatic model selection</p>
</li>
<li><p>Manually pick one of your local models</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747713041829/5871b234-0324-44b0-bb6b-ff9eba1a6706.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-takeaways">Takeaways</h2>
<p>Congrats — you’ve got a local LLM running and integrated into your dev workflow.</p>
<p>That said, performance varies depending on your hardware. Try a smaller model (1b or 3b parameters) if your machine struggles. Models like 7b and up usually need at least 16GB of RAM.</p>
<p>And don’t be afraid to experiment. Pull multiple models and see which works best for your use case.</p>
<p>Also, remember: you don’t need Cursor or Ngrok specifically. All you need is:</p>
<ul>
<li><p>Ollama to run the model locally</p>
</li>
<li><p>Some way to expose it via a fixed URL (Ngrok, localtunnel, etc.)</p>
</li>
<li><p>An IDE that lets you set a custom OpenAI API base URL</p>
</li>
</ul>
<hr />
<h2 id="heading-next-steps">Next Steps</h2>
<ul>
<li><p>🔄 <strong>Automate it all</strong><br />  Right now, everything is manual. Consider scripting the startup, spinning up the server, and tunneling with one command.</p>
</li>
<li><p>🔐 <strong>Secure your endpoint</strong><br />  Even if your CLI is authenticated, your public API is wide open. Use Ngrok’s free auth and rate limits to avoid abuse. But then set the API Key in Cursor to successfully connect it to the API.</p>
</li>
<li><p>🔗 <strong>Claim a static Ngrok URL</strong><br />  Ngrok lets you claim a static domain for free. This means you won't have to update Cursor every time you restart the tunnel. Just be sure to lock it down properly if you go that route.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to make an unforgettable impression with an outstanding portfolio]]></title><description><![CDATA[Before telling you how to create an outstanding portfolio, I want to tell you my personal story of how I got my first job eight years ago.
How did I get my first job
I was a third-year college student about to start my vacations, the only moment when...]]></description><link>https://blog.rodrigomd.dev/how-to-make-an-unforgettable-impression-with-an-outstanding-portfolio</link><guid isPermaLink="true">https://blog.rodrigomd.dev/how-to-make-an-unforgettable-impression-with-an-outstanding-portfolio</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[week1]]></category><category><![CDATA[General Advice]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Mon, 22 Aug 2022 22:06:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/5Xwaj9gaR0g/upload/v1661088723074/c-An6rcOG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before telling you how to create an outstanding portfolio, I want to tell you my personal story of how I got my first job eight years ago.</p>
<h2 id="heading-how-did-i-get-my-first-job">How did I get my first job</h2>
<p>I was a third-year college student about to start my vacations, the only moment when I could do one of the two internships required to apply for my degree.</p>
<p>At one moment, I saw a job application that caught my interest. I called and had a remote meeting with the founders of a startup company looking for people to assemble their first team.</p>
<p>They were a B2B company selling a solution to reduce the costs and time spent in the last mile problem (The journey between when a package leaves the warehouse and arrives at your door). They were looking for developers to build a web platform for administrators and a mobile app to be used by the delivery guys.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661089326048/WfsSmng1n.jpg" alt="building a mobile app" />
<em>Photo by freestocks on Unsplash</em></p>
<p>The meeting went right, and they asked me to have a technical interview next week. I got nervous because I had zero experience in technical interviews and also coding real projects, but I had an idea, I was going to spend the whole week working to show them what I was capable of doing.</p>
<p>I was inexperienced in crafting software, but I spent that week learning the technologies they were using to design and code the mobile app they were looking for.</p>
<p>After that week, I presented them with a totally functional proof of concept of the mobile app...they were speechless. As you might guess, I got the internship, and later I got hired, but more impressive it was that <strong>they kept talking about that moment to every new hire for the next five years I worked there</strong>.</p>
<p>I didn't know back then, but what I did was called <strong><em>the briefcase technique</em></strong>.</p>
<h2 id="heading-the-briefcase-technique">💼 The Briefcase technique</h2>
<p>According to Ramith Sethi from <a target="_blank" href="https://www.iwillteachyoutoberich.com/blog/how-to-sell-yourself/">I'll teach you how to be rich</a>, who name the technique, the secret to selling yourself and the main idea behind the briefcase technique is to SHOW, DON’T TELL.</p>
<p>The technique consists of two parts:</p>
<ol>
<li>Gather information about the client<ul>
<li>What are they selling?</li>
<li>What are they looking for?</li>
<li>What are they working on?</li>
<li>What tools are they using?</li>
</ul>
</li>
<li>Look into the future<ul>
<li>Brainstorm how you can add value<ul>
<li>Building a Proof of concept?</li>
<li>A design change?</li>
<li>Your experience leading teams?</li>
<li>Creating a plan to improve skills within the team?</li>
</ul>
</li>
<li>Show your proposal highlighting the value you'll add working with them</li>
</ul>
</li>
</ol>
<p>As you notice, with this technique, the work starts even before you meet the client or future employer. You research their business and try to spot specific issues, pain points, challenges; things you could solve or do better for them. This will position you better compared with a person who is just answering questions like in any interview.</p>
<p>Think about interviews as auditions instead of boring questionnaires and make them easy to say to you YES by shifting the discussion about you to what you're capable of doing by using an effective proposal to solve their problems.</p>
<h2 id="heading-the-portfolio-era">🗂️ The portfolio era</h2>
<p>In my story, I recognize that having that first meeting was crucial to pulling off the briefcase technique, but I got that meeting just because I was a college student, and back then, that was enough to catch companies' attention. Now, I wouldn't stand a chance even to get noticed without more evidence of my skills and experience. </p>
<p>These days being a college student or having a degree doesn't have the same relevance as it was before because people realized that provable skills are more important than theoretical knowledge.</p>
<blockquote>
<p><strong>You need to show what you've built. This is the Portfolio era!</strong></p>
</blockquote>
<h3 id="heading-what-projects-should-you-leave-out-of-your-portfolio">What projects should you leave out of your portfolio?</h3>
<p>Not everything you build is worth showing off.</p>
<p>A portfolio is your presentation card to the world to tell what you can do that makes you stand out from the competition. Therefore, any kind of project that's repetitive and common should be discarded immediately.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/eggheadio/status/1350103181506842626">https://twitter.com/eggheadio/status/1350103181506842626</a></div>
<p>Projects like:</p>
<ul>
<li>❌ Calculator</li>
<li>❌ Todo list</li>
<li>❌ Pomodoro app</li>
<li>❌Your portfolio website 🙄</li>
<li>⚠️ Clone apps</li>
</ul>
<p>Projects developed for learning purposes are good for serving as examples in your blog posts and for documenting your process, but not for your portfolio. Why? because everyone had coded one of those at some point, and remember, you want to make an unforgettable impression.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/javascriptual/status/1549754344974737410?s=20&amp;t=fsmyj8RgngpIxJ5GEREIFw">https://twitter.com/javascriptual/status/1549754344974737410?s=20&amp;t=fsmyj8RgngpIxJ5GEREIFw</a></div>
<p>Cloned apps are a special case because if they're functional enough, they could serve to show off your UI/UX knowledge or your skills in building complex systems. So, It's ok to include them, but don't make them the main point of focus; treat them as complementary projects.</p>
<h3 id="heading-the-outstanding-portfolio">The Outstanding Portfolio</h3>
<p>For any job, there will be skills that you won't have because you don't have the technical or domain knowledge the company needs, and that's ok.</p>
<p>It's impossible to know EVERYTHING.</p>
<p>You should put your time and energy into building the skills that transcend particularities and are required for any software development role.</p>
<p>Like:</p>
<ul>
<li>👥 Team Player</li>
<li>🧠 Problem-solving</li>
<li>📐 System design</li>
<li>✅ Code quality (tests, clean code)</li>
<li>💡 Creativeness/Innovation</li>
<li>📚 Active learning</li>
<li>🚀 Deployment</li>
</ul>
<p>The key is to demonstrate those skills through multiple projects. To help you with that, I've classified the projects - <em>that your outstanding portfolio needs</em> - into three main categories that groups the skills earlier mentioned:</p>
<h4 id="heading-1-projects-to-show-everything-youve-learned-so-far">1. Projects to show everything you've learned so far</h4>
<p>Here is where you put: <em>Experiments,  theory, crazy algorithms, libraries</em>. The idea is to show what you've been learning so far.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661197005990/x0j2kxv1h.png" alt="Pathfinding Visualizer" />
(<a target="_blank" href="https://clementmihailescu.github.io/Pathfinding-Visualizer/#">Pathfinding Visualizer App</a>)</p>
<p>In <a target="_blank" href="https://www.youtube.com/watch?v=3IlaxZf3-s8&amp;t=885s">this video</a>, <a target="_blank" href="https://twitter.com/clemmihai">Clément Mihailescu</a> tells us that he built an algorithm visualizer for his portfolio, which helped him to get a job at Google.</p>
<h4 id="heading-2-projects-to-solve-a-real-problem-starting-from-one-of-your-own">2. Projects to solve a real problem, starting from one of your own</h4>
<p>Here goes probably the most interesting projects you can show because that's what we do, that's our job, to solve problems! </p>
<p>It was used by real users? Then talks about it!
What problems did you have? How did you solve them?</p>
<p>Those are the things we want to hear!</p>
<p>The solution will catch people's attention no matter how simple it's.</p>
<p>That's the case of <a target="_blank" href="https://twitter.com/katherinecodes">Katherine Peterson</a>. She built <a target="_blank" href="https://readme.so/">readme.so</a>, a website to quickly create a README.md file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661198818749/ue3QpVwga.png" alt="readme.so landing page" /></p>
<p>The website caught Githubt's attention, and she got interviewed thanks to the project. In the end, she accepted an offer, and now she is working there.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/katherinecodes/status/1400820538780446732?s=20&amp;t=Sbi7Xget4FIgqTiK68IyTg">https://twitter.com/katherinecodes/status/1400820538780446732?s=20&amp;t=Sbi7Xget4FIgqTiK68IyTg</a></div>
<h4 id="heading-3-projects-to-collaborate-and-build-in-public">3. Projects to collaborate and build in public</h4>
<p>Working with more people on the same project is a good indicator that you'll work fine within a team because you've real experience in coordinating work among a team, listening to ideas, and solving human problems. </p>
<p>Also, building Open Source projects and publicly talking about your progress makes people get interested; remember that you've to think of getting a job as an audition.</p>
<h3 id="heading-the-most-important-thing-about-your-portfolio-are-the-projects-and-content-not-the-website-itself">The most important thing about your portfolio are the projects and content, not the website itself</h3>
<p><a target="_blank" href="https://twitter.com/alyssaxuu">Alyssa X</a>has built a ton of good projects but look at <a target="_blank" href="https://alyssax.com/">her portfolio's website</a>; it's dead simple.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661201001101/jFnKKew9Q.png" alt="Alyssa's portfolio website" /></p>
<p>It can probably be enhanced with pictures of her projects but what it's interesting is when you enter each link and see the repositories, articles, and things she has built.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661201026737/0CwbwHCHh.png" alt="Sonuum: The intuitive audio
editor for everyone" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661201032517/lGOnJmsc8.png" alt="Mapus: Maps with real collaboration" /></p>
<p>The portfolio has everything. Well, some things are not working now, but it had blog posts, templates, ideas, real products, and so on.</p>
<h3 id="heading-it-doesnt-have-to-display-only-apps">It doesn't have to display only apps</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661141377904/-yLR80PY_.jpg" alt="amelie-mourichon-wusOJ-2uY6w-unsplash.jpg" />
<em>Photo by Amélie Mourichon on Unsplash</em></p>
<p>It's a mistake to think that a portfolio it's just a website exhibiting other websites. That works for a UI developer, but what if you're a backend developer?</p>
<p>Portfolios are an entry point to display what you've done. Not restricted to only whole functional apps, you can show designs, diagrams, research papers, ideas, talks, benchmarks, and even the thought process you follow when facing a challenge.</p>
<h2 id="heading-one-last-piece-of-advice">One last piece of advice</h2>
<p>Recently I've been reading <a target="_blank" href="https://www.amazon.in/V%C3%A9ndele-mente-gente-Sell-People/dp/6077472476">a book on neuroscience applied to sales</a>, and it's surprising how our brain works. </p>
<blockquote>
<p>Did you know that 85% of buys are made from an unconscious decision?</p>
</blockquote>
<p>We're emotional creatures, and I know that doing all that I said is hard and takes time, but if you need the job soon and you don't have your amazing portfolio yet, it's ok to <strong>try your luck by just asking for the job</strong> (but you've to give a good speech).</p>
<p>I can guarantee it works sometimes.</p>
]]></content:encoded></item><item><title><![CDATA[Refactoring vs. Optimization]]></title><description><![CDATA[The previous article discussed the cost of owning a mess, why good code is important, and how we can improve and prepare the codebase to require less effort in future changes thanks to refactoring.
https://blog.rodrigomd.dev/refactoring-the-one-techn...]]></description><link>https://blog.rodrigomd.dev/refactoring-vs-optimization</link><guid isPermaLink="true">https://blog.rodrigomd.dev/refactoring-vs-optimization</guid><category><![CDATA[General Programming]]></category><category><![CDATA[best practices]]></category><category><![CDATA[refactoring]]></category><category><![CDATA[clean code]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Wed, 26 Jan 2022 02:51:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643080029284/TbbdYuqR6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The previous article discussed the cost of owning a mess, why good code is important, and how we can improve and prepare the codebase to require less effort in future changes thanks to refactoring.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.rodrigomd.dev/refactoring-the-one-technique-that-can-save-your-project">https://blog.rodrigomd.dev/refactoring-the-one-technique-that-can-save-your-project</a></div>
<p>We still have things to grasp. One of them was motivated <a target="_blank" href="https://syntax.fm/show/059/hasty-treat-refactoring">after listening to a chapter about refactoring</a> on the SyntaxFM podcast, <strong><em>Highly recommended podcast</em></strong>, at some point  <a target="_blank" href="https://twitter.com/stolinski">Scott</a>  said something that confuses me for a moment.</p>
<blockquote>
<p>"...another benefit you could gain from refactoring here is performance enhancement..."</p>
</blockquote>
<p>I get what Scott is saying but performance is more related to optimization and that's why we'll discuss in this article what are the differences and similarities between <em>optimization</em> and <em>refactoring</em> and why often when we refer to "optimizing code" we are actually talking about refactoring.</p>
<h2 id="heading-what-confuses-people-both-alter-code">What confuses people: Both alter code</h2>
<h3 id="heading-refactoring">Refactoring</h3>
<p>Refactoring is the discipline used to alter the code without changing the observable behavior. The goal is to refine the code having as criterion simplicity, readability, and maintainability.</p>
<h4 id="heading-impact-on-users">👨‍💻 Impact on users</h4>
<p> After refactoring, the software keeps working as usual, and the changes remain unnoticeable to the users.</p>
<p>Since the changes happen at a structural level (code), only the team involved is aware of the changes.</p>
<h4 id="heading-example">👩‍🏫 Example</h4>
<p><strong>Before</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (orientation === ORIENTATION.PORTRAIT ||
    orientation === ORIENTATION.PORTRAIT_UP ||
    orientation === ORIENTATION.PORTRAIT_DOWN) {
    <span class="hljs-comment">// Put your logic here</span>
}
</code></pre>
<p>The above example may not seem challenging to read, but imagine having that piece of code spread all over your code. Whenever you see an <code>if</code> that starts with the same comparison, you'll lose time verifying it's the same <code>if</code> statement; otherwise, how to be sure it is doing the same?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/lp84cYEUoOyh0LBEbt/giphy.gif?cid=ecf05e4754o0i5mozo7uw7mky7mkozwu7ld4vdtlccmi2d3m&amp;rid=giphy.gif&amp;ct=g">https://media.giphy.com/media/lp84cYEUoOyh0LBEbt/giphy.gif?cid=ecf05e4754o0i5mozo7uw7mky7mkozwu7ld4vdtlccmi2d3m&amp;rid=giphy.gif&amp;ct=g</a></div>
<p>To avoid that mental overhead and take advantage of reusability, we can put that comparison inside a function and use its name to reveal the intention.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isPortrait</span>(<span class="hljs-params">orientation</span>) </span>{
    <span class="hljs-keyword">return</span> (
    orientation === ORIENTATION.PORTRAIT ||
    orientation === ORIENTATION.PORTRAIT_UP ||
    orientation === ORIENTATION.PORTRAIT_DOWN
    );
}
</code></pre>
<p>Now instead of having the same <code>if</code> statement repeatedly. We'll replace it with a call to the new function. Since the function is self-explanatory, it's easy to get what it does.</p>
<p><strong>After</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (isPortrait(orientation)) {
    <span class="hljs-comment">// Put your logic here</span>
}
</code></pre>
<p>Unfortunately, not all <em>refactors</em> are as easy to do; many involve breaking dependencies, writing or rewriting classes, protecting our code from third-party libraries, implementing abstractions, and even deleting unnecessary code. </p>
<h3 id="heading-optimization">Optimization</h3>
<p>Optimization has a different purpose. Its goal is to improve software's performance. Generally involves satisfying a specific metric to accomplish a <a target="_blank" href="https://www.scaledagileframework.com/nonfunctional-requirements/"><strong>nonfunctional requirement</strong></a> . The metric could be to  <strong>reduce memory, CPU usage, or response time</strong>; it depends on the problem you're trying to solve.</p>
<h4 id="heading-impact-on-users">👨‍💻 Impact on users</h4>
<p>Same as with refactoring: <strong>Change the code, not what it does</strong></p>
<p>Users may detect something has changed even when the functionalities remain the same. This effect happens when the difference after the optimization compared with before is too evident.</p>
<p>The most noticeable changes are when the application responds faster, can handle more concurrent operations or doesn't hang when a heavy file is loaded.</p>
<h4 id="heading-example">👩‍🏫 Example</h4>
<p>In JavaScript when people want to reverse a string usually we write a function like the following:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Reverse string using built-in array's function</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> str.split(<span class="hljs-string">''</span>).reverse().join(<span class="hljs-string">''</span>);
}
</code></pre>
<p>Since in JS <code>strings</code> don't have a reverse method but arrays do, we rely on the built-in functions <code>split</code> and <code>join</code> to convert the string into an array and use its <code>reverse</code> method to finally join the reversed array into a string again.</p>
<p>The above code solves the problem but there are more performant solutions like converting the string into an array and swapping the elements at both sides:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Reverse string via swapping elements in-place</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
   <span class="hljs-keyword">const</span> chars = str.split(<span class="hljs-string">''</span>);
   <span class="hljs-keyword">const</span> length = chars.length;
   <span class="hljs-keyword">let</span> temp;

   <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i &lt;= <span class="hljs-built_in">Math</span>.floor(length/<span class="hljs-number">2</span>); i++) {
       temp = chars[length - i - <span class="hljs-number">1</span>];
       chars[length -i  - <span class="hljs-number">1</span>] = chars[i];
       chars[i] = temp;
   }

  <span class="hljs-keyword">return</span> chars.join(<span class="hljs-string">''</span>);
}
</code></pre>
<p><a target="_blank" href="https://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/">Just for you to know, the solution that offers the most consistent fast results in multiple browsers is the classic for loop with concatenation</a>.</p>
<p>If we were offering this functionality as a parsing utility, users wouldn't notice any change in the results unless they work with huge strings. In that case, they would witness the app takes less time to finish or not hang anymore when working with larger strings.</p>
<h2 id="heading-why-its-important-to-know-the-differences">Why it's important to know the differences?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640032693888/zElpyWx2c.png" alt="preserving_behavior.png" /> (source: Working Effectively with legacy code, page 7)</p>
<blockquote>
<p><strong>During our careers, we'll spend the majority of time working to preserve behavior, either by refactoring or optimizing code</strong>.</p>
</blockquote>
<p>Therefore, it's important to understand their differences and tradeoffs to make better decisions.</p>
<p>In the <code>reverseString</code> example, the most used solution was the first, even when the second performed better. Why is that? </p>
<blockquote>
<p>💡 <strong>Hint</strong> : What do you think we invest more time as devs? Reading code or writing code?</p>
</blockquote>
<p><strong>We spend more time reading!</strong>. Therefore, <strong>unless optimizing resources becomes a crucial task to the project, we'll always prefer an unoptimized and straightforward solution instead.</strong></p>
<p>Take the increasing use of <code>map</code>, <code>filter</code>, and <code>reduce</code> array methods in JavaScript as reference. Apparently, <code>for</code> loops are no longer accepted <a target="_blank" href="https://leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of/">even when demonstrated to be more efficient</a>; that's because these array methods are much simple to understand compared with an imperative solution.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> prospects = devs
      .filter(<span class="hljs-function"><span class="hljs-params">dev</span>=&gt;</span> <span class="hljs-regexp">/python/gi</span>.test(dev.experience))
      .map(<span class="hljs-function"><span class="hljs-params">dev</span> =&gt;</span> ({ 
                   <span class="hljs-attr">phone</span>: dev.phone,
                   <span class="hljs-attr">firstName</span>: dev.firstName,
                   <span class="hljs-attr">lastName</span>: dev.lastName,
                   <span class="hljs-attr">email</span>: dev.email,
                   <span class="hljs-attr">socialMedia</span>: dev.socialMedia
      }));
</code></pre>
<p>OR</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> prospects = [];
<span class="hljs-keyword">for</span> (int i = <span class="hljs-number">0</span>; i &lt; devs.length; i++ ) {
    <span class="hljs-keyword">if</span>(<span class="hljs-regexp">/python/gi</span>.test(devs[i].experience)) {
        prospects.push({
            <span class="hljs-attr">phone</span>: devs[i].phone,
            <span class="hljs-attr">firstName</span>: devs[i].firstName,
            <span class="hljs-attr">lastName</span>: devs[i].lastName,
            <span class="hljs-attr">email</span>: devs[i].email,
            <span class="hljs-attr">socialMedia</span>: devs[i].socialMedia
    }
}
</code></pre>
<p>Although both pieces of code do the same. There is a natural inclination to the first example. Maybe it's due to the explicit actions <code>filter</code> and <code>map</code>, which lower the bar to understand what the code does.</p>
<p>Using <code>filter</code> and <code>map</code> creates new arrays on memory by default, making it an inefficient solution when working with massive collections compared with the implementation that uses a <em>for loop</em>.</p>
<p>The above examples reveal a certainty regarding optimization:</p>
<blockquote>
<p>There are always tradeoffs between readability, simplicity, and the optimal use of resources</p>
</blockquote>
<p>The above statement explains wh refactoring and optimization compete to generate the best outcome.</p>
<p>It's up to you to decide based on the system, users, and developer's needs what aspect deserves more attention at a given time.</p>
<h3 id="heading-free-to-decide-or-forced-to-comply">Free to decide or forced to comply?</h3>
<p>A difference between these two concepts is <strong>when to pursue them</strong>. </p>
<p>Both disciplines are designed to solve someone's pain:</p>
<ul>
<li><p>🔧  <strong>Refactoring</strong> solves the developer's pain of dealing with and working on complex code. In the long run, it prevents inheriting those problems to customers by making the system unreliable and unstable due to the difficulty of maintaining existing functionalities while adding new ones.</p>
</li>
<li><p>🚀 <strong>Optimization</strong> solves a user's or customer's pain. Another reason is to comply with an agreement between who provides the software and who's paying for it.</p>
</li>
</ul>
<p>Refactoring problems are often more visible than optimization problems due to being interacting every day with the code. Still, since the latter affects people who are paying for the software, it has a higher priority in the majority of cases.</p>
<p>Affecting customers' work or not fulfilling contract clauses gives code optimization a whole different level of urgency.</p>
<blockquote>
<p>Optimization is always made when required, not before, and involves a clear objective.</p>
</blockquote>
<p>On the contrary, since refactorization is developers' problem (until is not), and their needs have a lower priority than business people, customers, and users, it's up to the team to explain the effort invested and decide when to do it. </p>
<p>Since time is money, and to avoid " convincing" non-developers that the teams need time to conduct refactors. A good option is to do these changes on every user story when they still involve a small effort and low risk.</p>
<p>It is like having a mindset of cleaning as you go.</p>
<blockquote>
<p>Refactoring should be done frequently on every task, a little every time. That way, we ensure the refactor risk, cost, and effort remains low</p>
</blockquote>
<h3 id="heading-limited-or-continuous">Limited or continuous?</h3>
<p>When optimizing code, we are clear on one thing: <em>The desired metric's value</em>.</p>
<p>Knowing the magic number doesn't tell you anything about achieving it. The team needs to brainstorm what to do, choose an idea and get hands-on to see if it works. If not, the process is repeated.</p>
<p>As you can see, achieving an optimization goal can consume a lot of time. One strategy is to limit the time spent on every idea to get a fast outcome. The team decides whether to invest a few hours or days per idea until the goal is eventually met.</p>
<p>Due to the complexity that represents reaching the magic number, never try to go beyond that, or you could find yourself in an impossible crusade.</p>
<p>Now, what about refactoring? I'll explain it through a hypothetical case:</p>
<p>Imagine that your code is a house, and every time you see something bad or that could be better, you represent it with a defect in the house. It might be a broken window, paint damage, a missing door...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642454497789/DePtFCusg.jpeg" alt="tim-arterbury-5Uh-wTSz-q0-unsplash.jpeg" />
(Source:  <a target="_blank" href="https://unsplash.com/@tim_arterbury">Tim Arterbury, on Unsplash @tim_arterbury</a> )</p>
<p>If you do nothing, your house will lose all of its value at some point.</p>
<p>But, if only you could repair immediately any damage by replacing the broken window, repainting the house, and installing a new door where it was missing. You'd be spending less money and effort to maintain the high value of your house.</p>
<p>The same analogy works with gardening. You must remove the weeds, move the earth, and water the plants, all of this regularly, otherwise, your garden will lose all its beauty, and in extreme cases, it can die.</p>
<blockquote>
<p>Refactoring should be done regularly, like gardening. But keep in mind that plants are going to die either way if you're watering them once a month.</p>
</blockquote>
<h3 id="heading-refactimization-or-optifactoring">Refactimization or Optifactoring?</h3>
<p>We know now that these disciplines are designed to address different purposes, and each one affects the counterpart.</p>
<p>Although you can benefit both by targeting <em>the golden mean</em>.</p>
<p>You can do some refactorizations without impacting the performance, like the one we saw at the beginning of this blog post. We can rename variables, extract methods, modularize the code, or remove duplicated code.</p>
<h4 id="heading-example">👩‍🏫 Example</h4>
<p>2-dimensional arrays are a widely used data structure, also known as <em>Matrix</em>. </p>
<p>Images are often represented using 2-dimensional arrays.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642612706027/deC_Nsi_G.png" alt="picture_matrix.png" /></p>
<p>For example, a picture of 1920x1080 has 1920 rows and 1080 columns.</p>
<blockquote>
<p>1920x1080 = 2073600 cells!</p>
</blockquote>
<p>Each cell contains a color represented in RGB (Red, Green, Blue) with a 3-tuple, the first value for the red channel, the second as the green, and the last as the blue. Each value could be between 0 and 255.</p>
<p>2-dimensional arrays are easy to understand by humans, but operations over these arrays tend to have an O(n^2) complexity. A linear approach O(n) would be better to reduce the processing time on large images.</p>
<p>A simple optimization is to encode the 2-dimensional array into a 1-dimensional by remembering the splits to identify cells as part of different rows within a single array.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642616862420/rAdAk9YCmW.png" alt="Encoding of a 2-array into a 1-array" /></p>
<p>Doing the above process every time we need to deal with images is challenging. In order to make the developer's life easier, we can refactor that code to hide the encoding process behind a class. That way, anyone using that class will think it is dealing with 2-dimensional arrays, but they won't know the story is different internally.</p>
<p>We offer specialized methods to retrieve each cell and perform common matrix operations in the class.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> SquareMatrix {
    <span class="hljs-keyword">private</span> cells: <span class="hljs-built_in">number</span>[] = [];
    <span class="hljs-keyword">private</span> mColumns: <span class="hljs-built_in">number</span>;

    <span class="hljs-comment">// Encode Bidimensional array into a one-dimensional array</span>
    SquareMatrix(arr: <span class="hljs-built_in">number</span>[]) {
          <span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>;
          <span class="hljs-keyword">let</span> j = <span class="hljs-number">0</span>;
          <span class="hljs-keyword">while</span>(i &lt; arr.length**<span class="hljs-number">2</span>) {
              <span class="hljs-keyword">if</span>(j === arr.length) {
                  j = <span class="hljs-number">0</span>;
                  i++;
              }
              <span class="hljs-built_in">this</span>.cells.push(arr[i][j]);
              j++;
          }

         <span class="hljs-comment">// Since it's a square matrix the number of columns is the</span>
         <span class="hljs-comment">// same as the number of rows</span>
          <span class="hljs-built_in">this</span>.mColumns = arr.length;
    }

    <span class="hljs-keyword">public</span> cell(rowIndex: <span class="hljs-built_in">number</span>, colIndex: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">number</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.cells[<span class="hljs-built_in">this</span>.mColumns*rowIndex + colIndex];
    }
    <span class="hljs-comment">/*
       .
       .
       .
    */</span>
}
</code></pre>
<p>The end result is an optimized code that's readable and hide complexity through a public API (The class's public methods).</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> matrixA = <span class="hljs-keyword">new</span> SquareMatrix([
    [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>],
    [<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>],
    [<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>]
]);

matrixA.cell(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>) <span class="hljs-comment">// 1</span>
matrixA.cell(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>) <span class="hljs-comment">// 6</span>
matrixA.cell(<span class="hljs-number">2</span>,<span class="hljs-number">2</span>) <span class="hljs-comment">// 9</span>

<span class="hljs-keyword">const</span> matrixB = <span class="hljs-keyword">new</span> SquareMatrix([
    [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">1</span>],
    [<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>],
    [<span class="hljs-number">3</span>,<span class="hljs-number">2</span>,<span class="hljs-number">0</span>]
]);

<span class="hljs-keyword">const</span> matrixC =  matrixA.add(matrixB);
</code></pre>
<p>To simplify the example above, we assume that the encoding would suffice, but in a real scenario, we need to test the change and if it works, only then refactor. We can use the optimization metric to control the refactoring by knowing if we are getting worse than what we achieved with the optimization.</p>
<p>The other way around is more difficult because optimizing the software can diminish the refactoring benefits done, and we don't have any metric to know when that happens.</p>
<h2 id="heading-conclusions">Conclusions</h2>
<ul>
<li>Refactoring and optimization are not the same</li>
<li>Both have different purposes, although they both change the code without changing the observed behavior</li>
<li>In the case of optimization, users may detect something has changed because the application got faster or can handle more operations</li>
<li>Optimization always has a deadline, and it's scoped while refactoring is up to the devs to decide when they should do it, although the best approach is to incorporate this discipline daily</li>
<li>Both disciplines can be combined, considering that focusing on one will affect the other. It's a matter of finding the golden mean.</li>
<li>A suggestion is to always apply refactoring at last. Because there are always changes to do that won't affect the optimization accomplished.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Refactoring: The one technique that can save your project]]></title><description><![CDATA[You enter a restaurant, order the food, the minutes pass, and when you are about to leave for the long wait, the waiter arrives with your food, but it's cold or isn't what you asked for 🤦‍♀️
What happened? The restaurant wasn't full, the attention w...]]></description><link>https://blog.rodrigomd.dev/refactoring-the-one-technique-that-can-save-your-project</link><guid isPermaLink="true">https://blog.rodrigomd.dev/refactoring-the-one-technique-that-can-save-your-project</guid><category><![CDATA[best practices]]></category><category><![CDATA[General Advice]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Developer]]></category><category><![CDATA[refactoring]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Sun, 06 Jun 2021 18:59:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1623016458715/H_NrB6Nra.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You enter a restaurant, order the food, the minutes pass, and when you are about to leave for the long wait, the waiter arrives with your food, but it's cold or isn't what you asked for 🤦‍♀️</p>
<p>What happened? The restaurant wasn't full, the attention was friendly, but the order was wrong and delivered with a significant delay...</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/3o7qDVXIwd93atON8c/giphy.gif">https://media.giphy.com/media/3o7qDVXIwd93atON8c/giphy.gif</a></div>
<p>It was the kitchen!! </p>
<p>Apparently, they were too busy cooking and dispatching the orders that no one took the time to properly clean and replenish ingredients, causing a big mess with the service.</p>
<p>It could all have been avoided if the kitchen team had organized better to clean the tools and the place while others focused on preparing each meal.</p>
<h2 id="heading-the-total-cost-of-owning-a-mess">💣 The total cost of owning a mess</h2>
<p>As a developer, can you relate to the previous story?</p>
<p>If you've been coding for a while, something similar already happened to you. Customers mad because the team was taken too long to fix bugs or delivering new features, and internally everything was a mess, like in the example.</p>
<p>When we work on a project where the main goal is to deliver features as fast as possible or with unrealistic deadlines, developers start taking shortcuts to satisfy everyone with the results. We succumb to the pressure of managers, customers, and even colleagues. The team is too busy with unrealistic compromises, leaving no time to clean-or at least that's what we believe-, so we avoid it until one day we start noticing that we are no longer going as fast we used to.</p>
<p>When the team stops worrying about how complex the code is becoming, it's a matter of time until it reaches a state where it will become a complete pain to work with. Soon adding just one line of code will require an enormous effort.</p>
<p>Eventually, the internal problem will be reflected externally in:</p>
<ol>
<li>Customer feedback: due to delays, poor quality deliveries, too many errors.</li>
<li>Lack of ability to respond quickly to events: Everything will require more time and effort due to how complex it is to continue working with the code.</li>
</ol>
<p>Soon, productivity will drop, our customer complaints will rise, raising alarms within the company.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621651841858/Y_IkicxnR.png" alt="productivity-quickly-tending-to-zero-over-time" /></p>
<p>Once the project reaches that state of complexity, people will end up leaving the company, tired of working and maintaining a rotten code. In addition, the company will lose valuable knowledge and skills that took months, even years, to build. </p>
<blockquote>
<p>Nobody wants to work with rotten code.</p>
</blockquote>
<p>Developers will demand to drop it all ways and start over because it's no longer sustainable to work with that code. In the end, after lengthy discussions and multiple complaints, <strong>the company will acknowledge the problem and will be forced to drop the current project  BUT not until the new project have all the existing features😭</strong></p>
<p><img src="https://i.pinimg.com/originals/97/04/57/9704574c3f1e30615c31024aaa7425b7.jpg" alt="Barney Stinson from HIMYM saying &quot;True story&quot;" /></p>
<h2 id="heading-good-code-matters">Good code matters</h2>
<p>Working with good code always matters, to you, to me, to everybody on the team, to customers and managers. Because if we can work smoothly, blazing-fast, and with less effort, it's a win-win situation for everyone.</p>
<p>The problem is that code has a natural tendency to get messy over time, which can be caused by many factors; some of the most common are:</p>
<ul>
<li>Having a system coupled to ever-changing technologies and dependencies</li>
<li>Not following coding guidelines</li>
<li>Taking shortcuts to fulfill customers or managers wishes</li>
<li>The loss of knowledge due to the enter and leave of coworkers on the project</li>
<li>Changes in the requirements</li>
</ul>
<p>We call some of those causes <em><strong>accidental complexity</strong></em>; things just happen and affect our code. But, there is also <strong><em>essential complexity</em></strong>, which has to do with the intrinsic complexity of the problem we are trying to solve. For example, writing a personal blog is less complex than building an airline reservation system (ARS).</p>
<p>Nevertheless, an experienced developer can write simple, readable, easy-to-understand code no matter the complexity. Still, it's worth nothing if the rest of the team keeps adding sloppy code.</p>
<blockquote>
<p>Writing good code is the responsibility of the whole team</p>
</blockquote>
<p>How can we assure the code keeps clean if we are constantly adding more code?.</p>
<p>Simple. Making code improvement a frequent task.</p>
<h1 id="heading-refactoring-to-the-rescue">🚁 Refactoring to the rescue</h1>
<p>A good chef knows the importance of maintaining the kitchen clean and the tools sharp and ready. As developers, we should do the same to make our job a breeze.</p>
<p>In software development, the technique that helps us with that is called <em>refactoring</em> and one definition is:</p>
<blockquote>
<p>Discipline technique for restructuring an existing body of code, altering its internal structure without affecting its external behavior — Martin Fowler</p>
</blockquote>
<p>In simple words, refactoring is all about changing the code but not what it does. <strong><em>If the code was doing A, after the refactor should still be doing A, not A.1 not A + B, just A.</em></strong></p>
<p>If we end up breaking something during refactoring, then it isn't a refactor; it's a rewrite of code. The same goes when we start including features that we never were included initially.</p>
<p>The objective is to make the code maintainable and extensible. We do it by simplifying the code; when the code is simple, it's easy to read, contributing to understanding it better. This is key in reducing technical costs because by working with simple and clean code, we face less resistance to changes accelerating the software development process.</p>
<p>Through refactoring, we make the developer's life easier; while the customers are unaware of the changes, they kept seeing the same product.</p>
<p>There is a downside, though. Because we are changing how the code is written, how it looks, and how it feels to works with it. There are many instances to screw it up in an epic way. However, the risks are proportional to the size of the refactor, so we should always aim to keep the changes as minimal as possible. One rule that supports this idea it's the <em>boy scout rule</em>.</p>
<h2 id="heading-boy-scout-rule">🏕️ Boy Scout rule</h2>
<blockquote>
<p>Leave the campground cleaner than you found it</p>
</blockquote>
<p>Whether you're working on an important feature or a critical bug, if you spot something that can be improved, do it.</p>
<p>With this in mind, refactors will be tiny enough to not represent a significant risk.</p>
<h4 id="heading-what-then-when-i-discover-that-we-need-a-massive-change-do-i-pause-my-current-task-and-start-the-tedious-big-refactor">What then when I discover that we need a massive change? Do I pause my current task and start the tedious big refactor?</h4>
<ul>
<li>Absolutely not</li>
</ul>
<p>Big changes are consequences of bad decisions and require a ton of effort. It's more usual to face this kind of monster while trying to improve legacy code. In this case, notify the team, evaluate the impact and treat it like any other feature. Treat big refactors as the exception to the rule; in general, we should avoid scheduling refactors because we run the risk of never doing them.</p>
<p>If there is a chance to tackle the problem one little refactor at a time, do this instead.</p>
<p>You can think of refactoring as gardening, a set of essential and frequent activities to have a beautiful garden. Like with plants, we have to maintain our code.</p>
<h2 id="heading-benefits-of-refactoring">✅ Benefits of refactoring</h2>
<ul>
<li>It helps to simplify the code, making it flexible, easy to understand and to work with it</li>
<li>It helps to keep the project updated with new technologies and standards</li>
<li>It reduces waste by allowing us to delete unnecessary and over-complicated code</li>
<li>It helps to keep the big picture of the project. By refactoring often different pieces of code, we refresh the idea of how those parts work</li>
</ul>
<h2 id="heading-the-outcome-of-refactoring">🏆 The outcome of refactoring</h2>
<p>Even when there isn't an ultimate form of our code because it's in constant change, we should always strive to have a <em>clean code</em> as an outcome. Although this cannot always be achieved, which makes good code an acceptable viable option.</p>
<p>The difference between both is that the <em>clean code</em> is the end state of the code in a given moment; when you read it, you can't find a way to make it better in that specific moment. Whereas with good code, we acknowledge that it has improvements, but we could keep polish it.</p>
<p>Depending on our time availability, we can choose to keep a good code rather than seeking to improve it; those changes can be delayed without causing any harm to the team.</p>
<blockquote>
<p>Seek progress, not perfection</p>
</blockquote>
<p>Refactoring is the never-ending pursuit of having a clean project.</p>
<h2 id="heading-when-we-should-refactor">When we should refactor?</h2>
<p>There are many reasons to get back to read old code and refactor it, but without a clear purpose, we could end rebuilding things, falling into premature optimization, violations of the <a target="_blank" href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">YAGNI</a> principle, or adding new functionalities.</p>
<blockquote>
<p>Don't refactor if you don't have a clear purpose in mind</p>
</blockquote>
<p>Refactoring is a means to an end, so we need valid reasons to justify doing it, and "I don't like Pete's code" is not a valid reason.</p>
<p>A powerful reason is to simplify the code because it has become an obstacle to work with, or the team has gained a deeper understanding of the domain and the tools being used that can help to reduce the complexity.</p>
<p>Other valid and more specific reasons are:</p>
<ul>
<li>Preparing the code for incoming breaking changes on third-party dependencies</li>
<li>Improving extensibility to facilitate new integrations</li>
<li>Reveal the intent of obscure pieces of code to identify security issues easily</li>
<li>Remove clear violations to good practices</li>
</ul>
<p>The best time to consider refactoring is before adding any updates or new features to the existing code. In those situations, it's good to return and read the code to evaluate if it's in good shape to use it as a base for the incoming changes.</p>
<p>Not having a clear purpose doesn't mean we can forget about refactoring. On the contrary, we should always keep looking for what to improve, trying to find that purpose.</p>
<h2 id="heading-considerations-before-refactoring">Considerations before refactoring</h2>
<p>How can we improve something that we don't know it's wrong?</p>
<p>To refactor code, we first have to build knowledge about good practices, principles, and techniques that can help us to write better code. That way, we'll have the tools to identify bad code and know how to improve it.</p>
<p>Refactoring can be a high-risk discipline due to changing the code and preserving the current behavior, but with <em>control version tools</em> and <em>automated tests</em>, it becomes a less risky operation. Although, there are still things we should be aware of before starting refactoring code.</p>
<ol>
<li><strong>Never refactor without automated tests</strong>. If you don't have tests, you lack the feedback to know if you broke something during the process. So abort the refactor and start writing those tests!</li>
<li><strong>Never add functionality</strong>. As devs, we can be tempted to use the opportunity to add new functionality, but by doing so, we could be introducing undetectable errors.</li>
<li><strong>Set limits to refactors</strong> to a file, module, class, etc.</li>
<li><strong> Always work at baby steps </strong>. You get early feedback, it's easier to start over, reducing the chances of wasting time.</li>
<li><strong>Back up every successful refactor using your control version tool once your tests are green</strong>.</li>
</ol>
<p>The key is to have automated tests; that's the best way to ensure we are changing the code and preserving the behavior simultaneously.</p>
<h1 id="heading-final-thoughts">Final thoughts</h1>
<p>Refactoring is not an optional task. It's a matter of professional survival. If something hurts now, tomorrow it will hurt more, so pay special attention to the signs.</p>
<p>Develop the knowledge required to identify what is needed to do and trust your tests.</p>
<p>Embrace continuous refactoring to reduce the high risks of dealing with large refactors.</p>
<p>Do it often, with a clear purpose in mind, and focus on progress, not perfection.</p>
<hr />
<h1 id="heading-further-reading">Further reading</h1>
<p>Fowler, M. (2018). Refactoring: Improving the Design of Existing Code (2nd Edition) (Addison-Wesley Signature Series (Fowler)) (2nd ed.). Addison-Wesley Professional.</p>
<p><a target="_blank" href="https://tigerabrodi.hashnode.dev/lessons-and-takeaways-on-refactoring">Here</a> is an excellent article from <a class="user-mention" href="https://hashnode.com/@tigerabrodi">Tiger Abrodi</a> about the learnings of the above book</p>
<p>Martin, R. C. (2008). Chapter 1: Clean Code. In Clean Code: A Handbook of Agile Software Craftsmanship (1st ed., pp. 1–15). Pearson.</p>
<p>Beck, K. (2002). Chapter 31: Refactoring. In Test-Driven Development: By Example (1st ed., pp. 181–191). Addison-Wesley Professional.</p>
<p>Feathers, M. (2004). Chapter 1: Changing Software. In Working Effectively with Legacy Code (1st ed., pp. 3–8). Pearson.</p>
]]></content:encoded></item><item><title><![CDATA[Test Double - dummy, mocks, stubs, fake objects, spies. All the gang in one place]]></title><description><![CDATA[Have you heard about stunts doubles in action films? You know? Those people whose job is to receive punches, kicks, and pretty much any type of damage to sell an action scene convincingly, without exposing the actors and actresses to any danger.
In m...]]></description><link>https://blog.rodrigomd.dev/test-double-dummy-mocks-stubs-fake-objects-spies-all-the-gang-in-one-place</link><guid isPermaLink="true">https://blog.rodrigomd.dev/test-double-dummy-mocks-stubs-fake-objects-spies-all-the-gang-in-one-place</guid><category><![CDATA[Testing]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[#codenewbies]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Thu, 29 Apr 2021 06:15:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618597002513/MlShMWZU6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you heard about stunts doubles in action films? You know? Those people whose job is to receive punches, kicks, and pretty much any type of damage to sell an action scene convincingly, without exposing the actors and actresses to any danger.</p>
<p>In movies, stunt doubles are hired due to their preparation for action scenes. Furthermore, they allow the movie's production to not depend on the actor to do those dangerous scenes, exposing himself to injuries that could affect the film deadlines and incur on extra expenses.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618649725249/6VH6hUiJO.png" alt="Hollywood actors and their stunts" />
<em>Hollywood actors and their stunts</em> (Keanu Reeves, Andrew Garfield, Will Smith, and Jackie Chan 🤣).</p>
<h1 id="heading-table-of-contents">Table of contents</h1>
<ul>
<li><a class="post-section-overview" href="#what-are-test-doubles">What are Test Doubles</a></li>
<li><a class="post-section-overview" href="#types-of-test-doubles">Types of Test Doubles</a><ul>
<li><a class="post-section-overview" href="#dummy-objects">Dummy objects</a></li>
<li><a class="post-section-overview" href="#fake-objects">Fake objects</a></li>
<li><a class="post-section-overview" href="#stubs">Stubs</a></li>
<li><a class="post-section-overview" href="#spies">Spies</a></li>
<li><a class="post-section-overview" href="#mocks">mocks</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#mocks-arent-stubs">Mocks aren't stubs</a></li>
<li><a class="post-section-overview" href="#practice-is-different-from-the-theory">Practice is different from the Theory</a></li>
<li><a class="post-section-overview" href="#practical-situations-when-test-doubles-could-help">Practical situations when Test Doubles could help</a></li>
<li><a class="post-section-overview" href="#prepare-your-nose-for-the-smells">Prepare your nose for the smells</a></li>
<li><a class="post-section-overview" href="#final-thoughts">Final thoughts</a></li>
</ul>
<h1 id="heading-what-are-test-doubles">What are Test Doubles?</h1>
<p><strong><em>Test Doubles</em></strong>, is a term coined by Gerard Meszaros (Meszaros, 2007) they are the equivalent of stunt doubles, but in testing. The context is different, but the idea remains.</p>
<blockquote>
<p>💡 Replace the original object with a copy that looks the "same" but behaves differently</p>
</blockquote>
<p>Unlike stunt doubles who replace the original actor in some scenes, we don't replace our original code under test; we replace its dependencies. More specifically, we are breaking a real dependency to use our Test Double instead.</p>
<h2 id="heading-why-do-we-want-to-replace-the-dependencies-of-the-code-under-test">Why do we want to replace the dependencies of the code under test?</h2>
<p>Michael Feathers explains it well in his book called "Working effectively with legacy code" (totally recommended).</p>
<p>There are two main reasons to break dependencies in favor of testing: <strong>Sensing</strong> and <strong>Separation</strong> (Feathers, 2004, p21-22)</p>
<ol>
<li><em>Sensing</em>. We want to see what is happening inside the code. What values are computed, and what calls are made.</li>
<li><em>Separation</em>. We want to separate the code from its dependencies because we can't even start testing due to multiple requisites to instantiate dependencies or because they take too long to be ready.</li>
</ol>
<h1 id="heading-types-of-test-doubles">Types of Test Doubles</h1>
<p>There are multiple Test Doubles, created for different situations. In this section, we'll learn them, their applications, and their characteristics.</p>
<blockquote>
<p><strong>Note</strong>: From now on, we'll follow the testing jargon, referring to the "<em>code under test</em>" which is also called "<em>System Under Test</em>" as <strong>SUT</strong> and to its dependencies as <strong>collaborators</strong>.</p>
</blockquote>
<h2 id="heading-dummy-objects">Dummy objects</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619489994968/1wyVXWg3m.png" alt="dummy.png" /></p>
<p>Perhaps you've encountered functions or methods that require more arguments than we actually need for testing a specific logic path during a test, and to avoid errors when calling the function, we fill the spaces with <code>null</code> or empty objects. Those objects that we passed down to the function are called <em>dummy objects</em>.</p>
<p>Dummy objects are passed around but never actually used in the code; their only purpose is to prevent errors when we don't respect the function's signature.</p>
<p>We can use any type of value like numbers, strings, and so on, is not necessary to always use <code>null</code> objects; </p>
<h2 id="heading-fake-objects">Fake objects</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619490795447/gNVpNteo-.png" alt="fake.png" /></p>
<p>Fake objects are usually objects whose implementation is written by us, just like our code under test. They are used to replace the original implementation because it's too slow or it cannot be called during the test.</p>
<p>For example, on some tests, devs write their own "persistence layer" to save data and use a hash map as an In-memory database instead of calling the actual database due to is much slower and will slow down the tests.</p>
<blockquote>
<p>Fake objects are often used in pseudo-integration tests or to replicate effects that could be too hard to do with the real collaborators</p>
</blockquote>
<p>The downside is that we have to maintain those objects and write tests specifically for their functionalities.</p>
<h2 id="heading-stubs">Stubs</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619565813966/6shhsGct5.png" alt="stub.png" /></p>
<p>Stubs are like a fake object but with no more than one line of code implemented: A return statement.</p>
<pre><code class="lang-JavaScript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stub</span>(<span class="hljs-params">value</span>) </span>{
    <span class="hljs-keyword">return</span> value;
}
</code></pre>
<p>They are used to return constant values, and <em>_most of the times, they don't return anything</em></p>
<pre><code class="lang-JavaScript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">theMostSimpleStub</span>(<span class="hljs-params"></span>) </span>{
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">theMostSimpleAsyncStub</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.resolve();
}
</code></pre>
<p>Its main use is to direct code's execution and contribute to verify state changes in the SUT through the values provided by itself.  Stubs guide the code execution path, providing the necessary values to enter conditionals (if and switch statements) or to continue immediately when the code reaches a blocking point, like waiting for asynchronous code to finish its execution.</p>
<h2 id="heading-spies">Spies</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619566169048/cy96P1c7-.png" alt="spy.png" /></p>
<p>Spies' purpose is to record the calls we made to a function or method. They are used to <em>sense</em> the behavior under test, to know if the functions are executed in the correct order and with the expected arguments.</p>
<p>They are stubs augmented, thereby don't execute the real implementation, and instead, they can return predefined values, but additionally, they act as proxies to record arguments.</p>
<p>Spies perform the same verification as mocks but with a different syntax. In both cases, we check the behavior rather than verifying if the resulting state after executing the SUT has changed.</p>
<blockquote>
<p>There are two types of spies: Some are anonymous functions, while others wrap existing methods on collaborators</p>
</blockquote>
<h2 id="heading-mocks">Mocks</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619579993624/Tqo7tQmHw.png" alt="mock.png" /></p>
<p>Mocks are objects pre-programmed with responses but different from Stubs because they also contain expectations that work as contracts of the calls they are expected to receive. They have a verification phase where all the calls made to the mock are compared to seek if they match against the calls and arguments expected. In case of any inconsistency, mocks can throw an exception when they receive an unexpected call.</p>
<h1 id="heading-mocks-arent-stubs">Mocks aren't stubs</h1>
<p>Developers tend to relate the word <em>"mock"</em> to the general action of replacing real objects with special objects that mimic the structure of the original ones. As we saw before, we can use many special objects to achieve that, and not necessarily need to be <em>mock objects</em>; we can do the same with stubs. Because of that, sometimes devs, when talking about "mocks," are referring to stubs or spies without knowing it.</p>
<p>Mocks and stubs have similarities but also differences. The main one is that mocks set expectations for their future calls. That distinction creates a whole different way of doing testing.</p>
<p>When we use mocks, our goal is to test the code and know how it was executed; what paths it took, which arguments its mocked dependencies received, how many times those functions were called, and sometimes, even in what order were executed. By using mocks, we are doing <strong>behavior verification</strong>.</p>
<p>In contrast, with stubs, we fake values and method responses to drive the system under tests (SUT) execution, and at the end of the test, we check if the final state was the expected. By using stubs, we perform a <strong>state verification</strong>.</p>
<blockquote>
<p>Mocks aren't the equivalent of stubs. They are similar but mocks enable a different style of testing.</p>
</blockquote>
<p>Behavior verification, using mocks, changes the common  <a target="_blank" href="https://blog.rodrigomd.dev/introduction-to-software-testing#3-anatomy-of-a-functional-test"><em><em>anatomy of functional tests</em></em></a>, altering how the tests are structured by adding new segments to set the mock's expectations and also requesting its expectations verification.</p>
<p>Nevertheless, we can also perform <em>behavior verification</em> without using mocks, but spies instead. With them, we can fake the implementation, set expectations to check the code to behave as we want, and all of that without changing the test structure because with spies, the verifications are writing at the end of the tests as we usually do.</p>
<p>So, you may wonder, why use mocks over spies then? Or why follow a behavior verification over verifying the state? I suppose it is a simple preference. In software development, we can do things in many different ways. Some devs prefer using mocks and other spies to do <em>behavior verification</em>. Others avoid faking dependencies as much they can and check the end state of each test.</p>
<h1 id="heading-practice-is-different-from-the-theory">Practice is different from the Theory</h1>
<p>Although we have different Test Doubles defined, with their specific uses. Testing libraries difficult the task of identifying each one them, the line is blurred due to the lack of standardization regarding Test Doubles definition. In some libraries, multiple Test Doubles are hidden behind a single public utility function or not implemented at all.</p>
<h2 id="heading-comparing-jest-httpsjestjsiodocsmock-function-api-and-sinonhttpssinonjsorgreleasesv1001">Comparing  <a target="_blank" href="https://jestjs.io/docs/mock-function-api">Jest </a> and  <a target="_blank" href="https://sinonjs.org/releases/v10.0.1/">Sinon</a></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619651417389/fGoCw3cIx.png" alt="comparison.png" /></p>
<p><em>Dummy objects</em> weren't included in the comparison because they are the easiest Test Double to create, and we don't require a library for that.</p>
<p>As you can see, in practice, each testing library uses its own definition of Test Doubles.</p>
<h1 id="heading-practical-situations-when-test-doubles-could-help">Practical situations when Test Doubles could help</h1>
<ul>
<li>Test an error hard to trigger (e.g., memory overflow)</li>
<li>File management</li>
<li>Caching</li>
<li>Third-party system integrations (e.g., email, databases, APIs)</li>
<li>Fake services to test functionalities in environments where those services are disabled(e.g<br />cell phone sensors)</li>
</ul>
<h1 id="heading-prepare-your-nose-for-the-smells">Prepare your nose for the smells</h1>
<p>Now you know what are Test Doubles, but I can't let you go without telling you the most important thing.</p>
<blockquote>
<p>Test Doubles are a code smell!!</p>
</blockquote>
<p>A code smell is not necessarily bad, but it definitely must catch our attention when reviewing the code. But why is that?</p>
<p>Let's see their benefits, with Test doubles we can:</p>
<ul>
<li>Run our tests faster</li>
<li>Isolate our coupled code</li>
<li>Corroborate logic by verifying Test Doubles calls while executing the SUT</li>
</ul>
<p>Unfortunately, they also have disadvantages. When we create stubs or mocks we are faking an implementation, if we overuse them we could end testing nothing more than Test Doubles results and not the feature we intend to build.</p>
<p>Here an example, let's say we want to test a function whose purpose is to capitalize a given string. The function uses internally the <code>capitalize</code> utility from a third-party library called  <a target="_blank" href="https://lodash.com/docs/4.17.15#capitalize">lodash</a> .</p>
<pre><code class="lang-JavaScript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> _ <span class="hljs-keyword">from</span> <span class="hljs-string">'lodash'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> _.capitalize(str);
}
</code></pre>
<p>Tell me, if we replace the <code>_.capitalize</code> function with a stub. Could that test tell us something useful?
-- Absolutely nothing!!</p>
<p>However, if we prove that the <code>_.capitalize</code> function is called with the same input as our function, the test might be useful but it will be coupled to the library.</p>
<p>In this specific example, what we want is to know if our <code>capitalize</code> function returns the capitalized version of a given string. We can test that directly, and by doing so we free ourselves from <code>lodash</code> , allowing us to replace the library without having to change the tests if we want. The tests will fail only when the feature fails.</p>
<pre><code class="lang-JavaScript"><span class="hljs-keyword">import</span> { capitalize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./text-utils/capitalize'</span>;

test(<span class="hljs-string">'capitalize function should return the capitalized version of a given string'</span>, <span class="hljs-function">() =&gt;</span> {
    expect(capitalize(<span class="hljs-string">'testing is awesome'</span>)).toEqual(<span class="hljs-string">'Testing is awesome'</span>);
});
</code></pre>
<p>We coupled the tests to the real implementation when we do a behavior verification using mocks or spies. If the system under test changes the calls of its collaborators, those mocks and expectations will fail, which isn't a problem when the code is used in one place, but if it's shared among multiple files, every related test will fail.</p>
<blockquote>
<p>Behavior verification makes our tests brittle.</p>
</blockquote>
<p>When the tests depend on how we call the SUT collaborators, it also affects refactoring. If the code change, it's more likely to break multiple tests even without changing the behavior (Refactoring doesn't change a feature behavior). </p>
<p>You must train your senses to know when is enough of faking dependencies and when we should use the real collaborators instead.</p>
<p>I can give you the last recommendation: <em><em>never fake business rules</em></em>, you can fake any other thing, but your core logic must remain intact. Favor testing with real collaborators when you test your core logic.</p>
<h1 id="heading-final-thoughts">Final thoughts</h1>
<p>Test Doubles have lots of benefits and are used by lots of developers, so it's a good use of time, learning about them. Besides their advantages, consider that they can also become a source of problems when we overuse them. <strong>Don't abuse Mocks, Spies, and Stubs</strong></p>
<p>Knowing all the types of Test Doubles and their use cases can help you identify when to use them, but nevertheless, remember that testing libraries could have their own definitions. Therefore you must also learn how to implement Test Doubles using your testing library of choice.</p>
<p>Abusing of Test Doubles can make the tests brittle and can make refactoring a challenging task, also give a false sense of security; to avoid these problems, follow these tips:</p>
<ul>
<li>Use real implementations whenever you can.</li>
<li>Write your software composing small pieces of code. That will restrict the need for fake dependencies.</li>
<li>Use mocks and spies on external dependencies and not on core business logic.</li>
</ul>
<p>Test doubles are required when we want to sense and separate our code during testing, a helpful characteristic on legacy code to match our test with already working behavior. </p>
<p>If we think about it, Test Doubles are more and more required as long as our code remains coupled, so if we focus on decomposing the code into smaller units, and we probably won't need to fake any dependency.</p>
<h1 id="heading-references">References</h1>
<p>Meszaros, Gerard (2007, May 11). Chapter 11 Using Test Doubles. In Addison-Wesley; 1st edition (Ed.). xUnit Test Patterns: Refactoring Test Code</p>
<p>Feathers, Michael (2004, Sept 22). Chapter 3: Sensing and Separation. In Pearson (Ed.). Working Effectively with Legacy Code (p21-22).</p>
<p>Fowler, Martin. (2006, January 16). TestDouble. 
https://martinfowler.com/bliki/TestDouble.html</p>
<p>Fowler, Martin. (2007, January 02). Mocks Aren't Stubs.
https://martinfowler.com/articles/mocksArentStubs.html</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[What every developer should know to start writing tests]]></title><description><![CDATA[We test things daily, from "how hot or cold the water is before entering the shower" to "taste and qualify a new meal before buying it again."
Sometimes we don't test things directly, but we adjust our behavior based on others' feedback, like reading...]]></description><link>https://blog.rodrigomd.dev/introduction-to-software-testing</link><guid isPermaLink="true">https://blog.rodrigomd.dev/introduction-to-software-testing</guid><category><![CDATA[Testing]]></category><category><![CDATA[best practices]]></category><category><![CDATA[software development]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[test]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Fri, 16 Apr 2021 01:36:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618344729455/3PFBWfU1Z.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We test things daily, from <em>"how hot or cold the water is before entering the shower"</em> to <em>"taste and qualify a new meal before buying it again."</em></p>
<p>Sometimes we don't test things directly, but we adjust our behavior based on others' feedback, like <em>reading a product's review before buying it on the internet.</em></p>
<p>We know about <strong>testing</strong> and <strong>feedback</strong>, but apparently, some companies and developers believe that they can skip that process as long as they deliver a working product.</p>
<p>They can't be more wrong...</p>
<p>A product that works is just the tip of the iceberg. Once it solves someone else's problem, it needs to improve its quality and be easy to maintain to secure its future; and even better when the product is built from the start on quality.</p>
<p>That's why developers who know how to write tests are attractive to recruiters, companies, and future team members because they can ensure quality, saving time and money.</p>
<p>If you don't believe me, down is the face of a Tech Lead interviewing a candidate who says he <strong><em>knows how to write automated tests</em></strong> 👇👇👇</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/l4EpfbRWMHRGhUjRe/giphy.gif">https://media.giphy.com/media/l4EpfbRWMHRGhUjRe/giphy.gif</a></div>
<h1 id="heading-what-is-testing">What is testing?</h1>
<p><em>Testing</em>, or more formally <em>software testing</em>, is performed to ensure the software behaves as expected by putting the production code under different scenarios, conditions, and situations. It also works to discover any problem that the team didn't consider during the software's conception.</p>
<p>Although testing is a broader discipline that involves different roles, such as QA testers and software developers, <strong>in this article, we'll focus on what a developer can do to contribute to quality assurance.</strong></p>
<h1 id="heading-what-happens-when-we-dont-test-our-code">What happens when we don't test our code?</h1>
<p>The time to deliver software is shortened every day, meaning that we need to ship new features and reach competitors' delivery capabilities sooner; otherwise, we'll be quick-off out of the market. The situation left no space for errors. Therefore, companies prefer to invest in improving the product's quality before it gets to the user's hands.</p>
<p>The reasons can vary, but we could lose our customer trust because of having too many errors or be replaced by our competition by not shipping features faster.</p>
<h1 id="heading-why-manual-testing-isnt-enough">Why manual testing isn't enough?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617854853357/VdopwS2rt.png" alt="product increment vs iterations_with_logo_final.png" /></p>
<p>Although manual testing can help to increase the quality of a product, it has three main problems:</p>
<ul>
<li>Is NOT scalable</li>
<li>Is NOT fast</li>
<li>Is NOT repeatable</li>
</ul>
<p>These problems come into account, especially when our software begins to grow; more lines of code result in a growing density of defects which increases the complexity of testing the product manually.</p>
<p>If the number of defects surpasses the QA team's capabilities, they will pass from a <strong>proactive attitude to a reactive one</strong>. If that happens, they will stop every improvement and future ideas, prioritizing the defects.  The problem can also occur in teams where developers and QA testers collaborate in the same team.</p>
<p>The amount of people restricts manual testing. There are a limited number of tasks a human can take. If the number of defects is greater, eventually, those will pass to production and will have to be solved, cutting the amount of time to build new features and improve the code.</p>
<p>There are other problems caused by doing manual testing while developing :</p>
<ul>
<li><strong>Create over-complicated solutions, unintentional:</strong> When we start immediately to work on something without considering how we'll test the code, we ended up with code that's hard to maintain.</li>
<li><strong>Requiring extra documentation to understand what to test:</strong> Most likely, only two people will know how to test the code, the person who wrote it and the QA tester, if he exists. To remove the authorship of code, the team will need to document the testing process so that anyone could test it in the future.</li>
<li><strong>Fear of refactoring:</strong> By not having a test to rely on, and execute whenever we can, developers will avoid improving, separating, and changing extensive code because of the fear of breaking something without knowing.</li>
</ul>
<p>All of us do manual testing at a certain point. It's valuable to get visual insight and to discover flaws in the system by taking unexpected and crazy paths, just like real users do. To scale, we need to automate whatever we can, letting developers free of testing manually to focus on writing new features or improving the existing ones.</p>
<h1 id="heading-what-is-an-automated-test">What is an automated test?</h1>
<p>As you may guess, an automated test is a test whose logic has been automated through code.</p>
<blockquote>
<p> It's a piece of code written with the sole purpose of testing our production code, the code that is going to be used by our customers and users.</p>
</blockquote>
<p><em>** For the sake of simplicity and because it's widely used the word "test" to refer to an "automated test", we'll keep that correspondence on the rest of the article</em></p>
<h1 id="heading-benefits-of-automated-testing">Benefits of automated testing</h1>
<ul>
<li><strong>Tests work as a safety net to make changes (refactoring)</strong>: Now we feel confident on changing code because if we break something our tests will warn us</li>
<li><strong>Tests can work as live documentation when they are descriptive enough</strong>: Additional to documentation files now we can read our tests to understand how the code works and access to useful examples</li>
<li><strong>Tests prevent wasting unnecessary time on debugging</strong>: Rather than spending time trying to figuring out what is wrong, we use the feedback our broken tests will give us. If we have a bug, we write a new test to expose the error</li>
<li><strong>Tests are the first users of our code</strong>: We can have early feedback from our code, even before running the code if we write the tests before the code</li>
<li><strong>Thinking in testing while coding can lead us to a better design</strong>: Now we write our code to make it easier to test, to do so we rely on design patterns and best practices to build a decoupled code</li>
</ul>
<p><a target="_blank" href="https://mariocervera.com/non-obvious-benefits-automated-testing">Here</a>  is a great article written by <a class="user-mention" href="https://hashnode.com/@macerub">Mario Cervera</a> to complement the benefits of testing.</p>
<h1 id="heading-example-a-simple-automated-test">Example: A simple automated test</h1>
<p>Suppose we need to write a code to reverse a string. That function will be used in our production code.</p>
<p>As we already know, a test is just code. Therefore, a test for the <code>reverse</code> function could look like the following function:</p>
<pre><code class="lang-JavaScript"><span class="hljs-keyword">import</span> reverse <span class="hljs-keyword">from</span> <span class="hljs-string">'../reverse'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">value, expected</span>) </span>{
    <span class="hljs-keyword">return</span> reverse(value) === expected;
}
</code></pre>
<p>As we can see, a basic test just uses the real <code>reverse</code> function and compares its result against an expected result. We call that verification an <em>assertion</em> or <em>expectation</em>.</p>
<p>With our test function written, we can start adding more tests cases:</p>
<ul>
<li>Reverse function must return the same string if it's a palindrome<pre><code class="lang-JavaScript">test(<span class="hljs-string">'level'</span>, <span class="hljs-string">'level'</span>);
</code></pre>
</li>
<li>Reverse function must return the inverted string<pre><code class="lang-JavaScript">test(<span class="hljs-string">'amazing'</span>, <span class="hljs-string">'gnizama'</span>);
</code></pre>
</li>
<li>Reverse string must return an empty string when receiving an empty string<pre><code class="lang-JavaScript">test(<span class="hljs-string">''</span>, <span class="hljs-string">''</span>);
</code></pre>
</li>
</ul>
<p><em><strong>Now, what if we also want the reverse function to throw an error when receives a null or undefined value?</strong></em></p>
<p><em>How can we test that?</em></p>
<p>We'll need to modify our <code>test</code> function to support the new requirement in the <code>reverse</code> function, the changes that we need to include are:</p>
<ul>
<li>Accept strings and errors as the second argument, the expected result</li>
<li>Catch any error thrown by the <code>reverse</code> function and compare its error message with the message of the expected error</li>
</ul>
<p>With the new requirements in mind, the resulting code of the <code>test</code> function will look like this:</p>
<pre><code class="lang-JavaScript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">original, expected</span>) </span>{
    <span class="hljs-keyword">if</span> (expected <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) {
        <span class="hljs-keyword">return</span> testError(original, expected);
    }
    <span class="hljs-keyword">return</span> testString(original, expected);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testString</span>(<span class="hljs-params">original, expected</span>) </span>{
   <span class="hljs-keyword">return</span> reverse(original) === expected;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testError</span>(<span class="hljs-params">original, expected</span>) </span>{
    <span class="hljs-keyword">try</span> {
       reverse(original);
       <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    } <span class="hljs-keyword">catch</span>(e) {
       <span class="hljs-keyword">return</span> e.message === expected.message;
    }
</code></pre>
<p>With the <code>test</code> function updated, we can now add the new test cases:</p>
<ul>
<li>The reverse string function must throw an error when receives a null<pre><code class="lang-JavaScript">test(<span class="hljs-literal">null</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'invalid value, expected an string'</span>));
</code></pre>
</li>
<li>The reverse string function must throw an error when receives an undefined<pre><code class="lang-JavaScript">test(<span class="hljs-literal">undefined</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'invalid value, expected an string'</span>));
</code></pre>
</li>
</ul>
<p>Finally to know if the tests passed or not we will print the result in the terminal using the function <code>console.log</code></p>
<pre><code class="lang-JavaScript"><span class="hljs-built_in">console</span>.log(test(<span class="hljs-string">'level'</span>, <span class="hljs-string">'level'</span>));
<span class="hljs-built_in">console</span>.log(test(<span class="hljs-string">'amazing'</span>, <span class="hljs-string">'gnizama'</span>));
<span class="hljs-built_in">console</span>.log(test(<span class="hljs-string">''</span>, <span class="hljs-string">''</span>));
<span class="hljs-built_in">console</span>.log(test(<span class="hljs-literal">null</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'invalid value, expected an string'</span>)));
<span class="hljs-built_in">console</span>.log(test(<span class="hljs-literal">undefined</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'invalid value, expected an string'</span>)));
</code></pre>
<p>After running our tiny script we'll see something like the following</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/UguI0WAE3fAf2Y7Z57/giphy.gif">https://media.giphy.com/media/UguI0WAE3fAf2Y7Z57/giphy.gif</a></div>
<p><strong><em>Voila! We wrote our first test!</em></strong>. Although the feedback is really basic 🤔</p>
<h1 id="heading-how-pros-do-it">How PROS do it?</h1>
<p>Short answer, as we did in the previous section.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/mjwUZYo0Nduso/giphy.gif">https://media.giphy.com/media/mjwUZYo0Nduso/giphy.gif</a></div>
<p><em>it was a joke, of course not</em></p>
<p>Remember that we said an automated test is just code? If the test is in charge of verifying the production code to work as expected, <strong> who checks the test to work as expected? </strong></p>
<p>Our tiny <code>test</code> function from the previous example was so smaller that we could easily identify a problem with our eyes.</p>
<p>But, also remember that as soon as we added another possible outcome, passing from returning a string to throwing an error, we needed to adapt the behavior of the <code>test</code> function to support that change without breaking the test.</p>
<p>Every time we are forced to add logic into the test because of a concern that's different from testing the production code, like extending the available assertions, the risk of committing an error increases. Imagine adding asynchronous code, numbers, timeouts, intervals, objects, and so on. Now it doesn't seem to be so easy to detect problems on our tests. </p>
<p>To avoid any problem writing custom test functions, developers use libraries specifically designed to help writing tests.</p>
<p>Those libraries present utilities in the form of functions or classes to structure our tests, and that way, we focus on writing the logic inside. We can find them as third-party libraries or as a part of the programming language, like the <em>unittest</em> package in Python.</p>
<p>Other libraries help us to deal with assertions. They supply utilities to compare different types of values, partially match objects or arrays,  match regular expressions, catching errors, handling promises, knowing the number of function calls, order of the function calls, and more. These libraries are called <strong><em>assertion libraries</em></strong>, and you can find them as a standalone library or as part of a whole testing framework.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618201725482/HnBRnMMQF.png" alt="Screen Shot 2021-04-12 at 00.28.20.png" />
<em>In the above image <strong>Describe</strong>, <strong>it</strong> and <strong>expect</strong> are global functions set by the Jest library</em></p>
<p>In JavaScript, a standalone assertion library is <a target="_blank" href="https://www.chaijs.com/"><em>chai</em></a> whereas <a target="_blank" href="https://jestjs.io/docs/getting-started"><em>Jest</em></a> includes its assertion utilities inside the whole framework.</p>
<p>There is another challenge when we write our own testing functions: <em>we need a way to run the tests written in different files</em> , because in a big project having everything in one file is not optimal. Therefore we will need a way to find those test files, run them separately from our production code_, perhaps also in parallel to optimize time spent and execute a subset of tests when we need to.</p>
<p>Fortunately, there are command-line programs that can solve the problems mentioned before; those programs are called <strong><em>test runners</em></strong>.  In JavaScript  <a target="_blank" href="https://mochajs.org/"><em>mocha</em></a> , <a target="_blank" href="https://jestjs.io/docs/getting-started"><em>jest</em></a> and <a target="_blank" href="https://karma-runner.github.io/latest/index.html"><em>karma</em></a> are test runners.</p>
<p>We can execute our tests from a terminal with a test runner, allowing us to run them in Continuous Integrations pipelines to validate the code before merging it.</p>
<p>As we see, testing libraries provide us all the power we need to focus solely on writing the logic behind our tests.</p>
<p>Oh!, and the feedback improves drastically using libraries compared with the <code>test</code> function that we wrote in the prior section.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1617160696997/a_66ys_OD.png" alt="Screen Shot 2021-03-31 at 00.15.38.png" /></p>
<h1 id="heading-how-can-we-test">How can we test?</h1>
<h2 id="heading-1-choose-wisely-the-type-of-test">1. Choose wisely the type of test</h2>
<p>There are many types of tests, each one with different purposes and characteristics, but in general, we can classify them as <strong><em>functional tests</em></strong> and <strong><em>non-functional tests</em></strong>. The first exist to probe an expected behavior in our code, to check that what we coded is aligned with a functionality. The latter is for testing non-functional aspects of the system such as reliability, availability, scalability, and all the -ilities; they provide handy metrics to make data-driven decisions and to know when we need to optimize the code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618259791739/WCbuXDkEZ.png" alt="tests_classification.png" /></p>
<p>Functional tests provide us with feedback when working on the solution, while non-functional tests only after the solution worked, but it didn't satisfy a metric. Because of that, we'll likely have lots of functional tests and few non-functional tests in our project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618285304580/0fxBvteQN.png" alt="different_levels_of_testing_with_logo.png" /></p>
<p>The reason behind having different types of functional tests (unit tests, integration, end-to-end) is that each one focuses on different aspects of our code, testing components in isolation, testing how they integrate together, or testing the whole application including UI.  </p>
<p>Why are differences? Because when we require more integration(e.g., waiting until multiple services are up and running), the tests will be slower. With less integration, the feedback provided by the tests will be less confident regarding the whole system.</p>
<p>For example, let's said we are going to test a car's wheel, but we don't test if the wheel was bolted correctly to the chassis; even if the wheel passes its tests, the car could be bad ensembled because the wheel's tests don't tell us anything about it.</p>
<p>There is a concept called <a target="_blank" href="https://martinfowler.com/articles/practical-test-pyramid.html"> <em>"The testing pyramid"</em></a> used to balance how fast we get the feedback with how confidence it delivers us. The idea is to have as much as we can of isolated tests because they are faster and complement them with a few high-level tests involving multiples parts to test the application's crucial features and have high confidence.</p>
<h2 id="heading-2-prepare-the-environment">2. Prepare the environment</h2>
<p>After deciding which tests our system requires, the following questions can help us know more about what we need.</p>
<ul>
<li>Who will be in charge of writing those tests? Our team? another team? a QA team?</li>
<li>The tests can be written completely isolated from the production code? Or do we need to have them as closes to the code we can to review the code and the tests together?</li>
<li>Do we need to write the tests using a special syntax and file structure?</li>
</ul>
<p>Answering the questions can guide us to decide whether we should write our tests in the same project or as a whole different project. </p>
<p>For example, End-to-End tests use UI elements to detect changes in the layout and navigate the application under test; they don't use the code!!. Therefore, we can write those tests in a whole different project, but if we have to maintain those tests, do we have enough people to handle another project?. A better decision could be to include them as a folder inside the same project as the production code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618466027739/q0zljtuwh.png" alt="test_folders.png" /><em>Folder structure and file names for unit tests on JS and Python</em></p>
<p>Finally, we cannot forget to read the documentation of the testing library or framework that we chose. Probably they will be configured to search a specific folder structure, filename, or even syntax. </p>
<h2 id="heading-3-anatomy-of-a-functional-test">3. Anatomy of a functional test</h2>
<p>Although there are different types of tests for various purposes, functional tests share a typical structure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618467397924/HmWq-C-sp.png" alt="anatomy2.png" /></p>
<ol>
<li>Import production code under test (not required in all the type of tests. e.g End-to-End)</li>
<li>Create a test suite (not required in all the languages neither libraries)</li>
<li>Write a test case</li>
<li>Write SetUp code</li>
<li>Call the production code</li>
<li>Assert the outcome</li>
</ol>
<p>The above picture is a unit test, but the structure doesn't change too much in other functional tests. In each one, we'll have a setup to do everything needed to set the stage of the test case. Perhaps execute some actions or create data.</p>
<p>The setup can change from creating just objects to start up a database, create records on it, or even open a web page. The call to action could be a call to an API endpoint, and the assertion can be to compare if it has the correct status code.</p>
<p>How the structure is implemented can change from test to test, but its structure is more likely to remain the same.</p>
<h3 id="heading-how-we-arrange-the-code-in-a-test">How we arrange the code in a test?</h3>
<p>Steps four to six receive different names depending on the type of tests, but all means the same. It is like telling a linear story.</p>
<ul>
<li><em>Introduction, Development, Conclusion</em></li>
<li><em>SetUp, Call to action, Assertion</em></li>
<li><strong><em>Arrange, Act, Assert</em></strong></li>
<li><strong><em>Given, When, Then</em></strong></li>
</ul>
<h1 id="heading-ok-they-are-great-but-what-about-time-will-we-go-slower">OK. They are great but, What about time? Will we go slower?</h1>
<p>Yes, absolutely. At least at first, compared to not testing.</p>
<p>Every new skill requires time to develop. In this case, we need to change our mindset to start thinking about how the code we are writing can be tested. Also, learn to master the testing library or framework of choice.</p>
<p>At first it will appear that we are doubling our efforts by writing tests and production code. But, in reality, what was happening was that we were writing only half of the code required by not writing the tests.</p>
<blockquote>
<p>To write tests should be seen as part of implementing the feature or fixing the bug</p>
</blockquote>
<p>Writing tests doesn't mean we'll go twice as slow. After building enough experience, we'll write tests much faster. Besides, we'll write automated tests just once and update them only when the behavior they're testing has changed.</p>
<p>Automated tests offer quick feedback (between milliseconds and seconds) and the opportunity to execute the tests as many times as we want without effort. We get all the benefits of testing but with zero-waste.</p>
<blockquote>
<p>By writing automated tests, we are betting on the long run; we seek stability.</p>
</blockquote>
<p>In the end, it's a lot of time and money saved compared with debugging the code and having everyone busy handling defects without producing neither maintaining code.</p>
<h1 id="heading-if-are-so-great-why-some-devs-avoid-writing-automated-tests">If are so great, why some devs avoid writing automated tests?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618028754404/UB6rgU7QK.jpeg" alt="1__6PGPKCLntCj43I1fyyAtw.jpeg" /></p>
<p><strong>-- Because it's not an easy task</strong> 😟</p>
<p>Many things make writing tests a complex task.</p>
<ul>
<li>Not having the knowledge</li>
<li>Fear of experimenting in an environment full of pressures (overload of work or unreal expectations and deadlines)</li>
<li>Not having the culture</li>
<li>Having a code so coupled and convoluted that it's tough to add tests</li>
</ul>
<p>Everything worth the effort will be hard to achieve at the beginning because it demands discipline and consistency.</p>
<blockquote>
<p>Nothing is harder than working under a tight deadline and still taking the time to clean up as you go - Kent Beck, author of "Test-Driven Development by example" book.</p>
</blockquote>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Writing automated tests is a necessary skill that all companies need to adopt to scale and survive in a fast-paced industry growing each day. </p>
<p>Ensuring the quality of the products we build is a must to secure our customer's fidelity, and with the help of automated tests, we can do it without wasting the team's time. QA testers can use their time to automate and experiment proactively, and devs can use their time to build new features trusting that at any moment a test will break, delivering enough information to solve the problem as soon as something unexpected happens.</p>
<p>A good approach is to start with small, isolated, and fast tests. Then, complement with a few of slow and high confident tests.</p>
<p>Write tests using libraries and frameworks specifically designed for that. Also, organize the tests and code following their guidelines.</p>
<p>The whole idea of writing automated tests is to detect any defect immediately, moving the feedback more and more close to the development phase. But that doesn't mean we should discard manual tests completely; they are still a valuable tool for simulating unexpected user behavior that cannot be generalized either automated.</p>
<blockquote>
<p>The most important thing is gathering courage and losing the fear of going slow initially but knowing that things will improve eventually.</p>
</blockquote>
<h1 id="heading-whats-next">What's next?</h1>
<p>Thanks for reading until here. I hope I answered some of your questions, and much better if I raised questions you didn't know you have.</p>
<p><strong>This is the first of many articles about software testing. I'll explain different types of tests, teach how to write them, improve them to have clean tests, teach about Test-Driven Development, and much more.</strong></p>
<p>Finally, If you like, you can support me here 👇</p>
]]></content:encoded></item><item><title><![CDATA[Comments are useful. You just need to learn when to use them]]></title><description><![CDATA[Perhaps you may have heard some of the following statements:

"Code needs to be self-explanatory"
"Comments are useless"
"Don't write comments in your code"
"Comments are a code-smell" 
"Comments are an anti-pattern"

The thing is that writing commen...]]></description><link>https://blog.rodrigomd.dev/comments-arent-as-bad-as-people-tell-you</link><guid isPermaLink="true">https://blog.rodrigomd.dev/comments-arent-as-bad-as-people-tell-you</guid><category><![CDATA[coding]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[best practices]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Mon, 22 Mar 2021 21:25:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616382995176/_aYkPgN05.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Perhaps you may have heard some of the following statements:</p>
<ul>
<li>"Code needs to be self-explanatory"</li>
<li>"Comments are useless"</li>
<li>"Don't write comments in your code"</li>
<li>"Comments are a code-smell" </li>
<li>"Comments are an anti-pattern"</li>
</ul>
<p>The thing is that writing comments aren't bad at all if you know in which cases they can help us and on which others they are a complete waste of time.</p>
<h2 id="heading-code-needs-to-be-self-explanatory">Code needs to be self-explanatory</h2>
<p>Code should be expressive and intention-revealing. Needing comments to explain the code means the code doesn't describe itself well enough.</p>
<p>Our code is read far more times than is written, so we should aim to make it as easier to read as we can.</p>
<p>Sometimes it's clear when a comment is redundant.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Calculate the square root of the sum of two numbers</span>
<span class="hljs-keyword">const</span> result = <span class="hljs-built_in">Math</span>.sqrt(a + b);

<span class="hljs-comment">// Timeout of 30 seconds</span>
<span class="hljs-keyword">const</span> timeout= <span class="hljs-number">30000</span>;
</code></pre>
<p>But other times, knowing if the use of comments is a good or bad idea, is confusing.</p>
<p>Consider the following example:</p>
<ul>
<li>Can you know in less than ten seconds, what the code does without reading the comments?</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">/*
* Calculate the total amount to pay for a pizza going through each
* item in the order and obtain the cost of each ingredient of the
* price object searching by section and ingredient name to then add 
* each subtotal and return the total to pay
*
* @param {object} o: pizza order
* @param {object} p: ingredients prices
* @return {number} total: total price to pay for the order
*/</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">total</span>(<span class="hljs-params">o, p</span>) </span>{
    <span class="hljs-keyword">let</span> total = <span class="hljs-number">0</span>;

    <span class="hljs-built_in">Object</span>.keys(o).forEach(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (key !== <span class="hljs-string">'ingredients'</span>) {
            total += p[key][o[key]];
        } <span class="hljs-keyword">else</span> {
            o.ingredients.forEach(<span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> {
                total += p.ingredients[value];
            });
        }
    });

    <span class="hljs-keyword">return</span> total;
}
</code></pre>
<p>In the above code, we used JSDocs, which is a fancy way to create documentation automatically by reading the comments and special tags like <strong> <code>@ param</code> </strong> or <strong><code>@ return</code> </strong> that specify the parameters and the value returned by each function. Although JSDocs has justified use, when used for the sole purpose of describing what the code does, it becomes useless.</p>
<p>We can refactor the previous example using descriptive and meaningful variable names, moving some logic to a smaller function so that its name explains what the function does. After that, we will soon realize that all such comments are unnecessary.</p>
<pre><code class="lang-javascript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculatePriceToPay</span>(<span class="hljs-params">pizzaOrder, tableOfPrices</span>) </span>{
    <span class="hljs-keyword">let</span> totalPrice = <span class="hljs-number">0</span>;

    <span class="hljs-built_in">Object</span>
        .entries(pizzaOrder)
        .forEach(<span class="hljs-function">(<span class="hljs-params">[section, item]</span>) =&gt;</span> {
            totalPrice += getItemPrice(section, item, tableOfPrices);
     });

    <span class="hljs-keyword">return</span> totalPrice;
}


<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getItemPrice</span>(<span class="hljs-params">section, item, tableOfPrices</span>) </span>{
    <span class="hljs-keyword">let</span> price = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(item)) {
        price = item.reduce(<span class="hljs-function">(<span class="hljs-params">accumulator, subItem</span>) =&gt;</span> {
             <span class="hljs-keyword">return</span> accumulator +=  tableOfPrices[section][subItem];
        }, <span class="hljs-number">0</span>);
    } <span class="hljs-keyword">else</span> {
        price = tableOfPrices[section][item];
    }
    <span class="hljs-keyword">return</span> price;
}
</code></pre>
<p>The only reason to hesitate before removing those comments is if the JSDocs is actually been used to create documentation, if not, or if they were another type of comments we could remove them immediately.</p>
<p>See?. The <code>calculatePriceToPay</code> function is much simple to read after the refactoring even without the comments.</p>
<blockquote>
<p>When we write code with intention revealing and easy to understand, every previous comment whose sole purpose was to explain the code, lose completely its value, and therefore we can remove it</p>
</blockquote>
<h2 id="heading-avoid-using-comments-to-explain-what">Avoid using comments to explain "What"</h2>
<p>Whenever you find comments used to explain what the code is doing I invite you to question if they are really necessary because in the majority of cases those comments aren't needed, and can be deleted.</p>
<p>We should understand what the code is doing after reading it line by line and not by reading a description.</p>
<p>Another reason is that code is alive, it changes more frequently than comments and even documentation, so it's more likely to see comments become obsolete sooner than you think.</p>
<p>As in everything, there are exceptions. There is one case in which we can use comments to describe the code; If we are building a really complex algorithm or performing complex operations, in non-trivial domains like statistics, physics, chemistry, healthcare. In those situations, we can give additional context and reinforce the self-described code through the use of comments.</p>
<p>Let's see an example:</p>
<pre><code class="lang-Python"> <span class="hljs-keyword">import</span> math

EARTH_RADIUS_KM = <span class="hljs-number">6371</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_distance_km</span>(<span class="hljs-params">src_lat, src_lng, dest_lat, dest_lng</span>):</span>
<span class="hljs-string">"""
Calculate the linear distance between two points in the earth using
the Haversine Formula

The points are GPS coordinates with a latitude and longitude 
represented as floats numbers
"""</span>   

    DEGREES_TO_RADIANS = math.pi / <span class="hljs-number">180.0</span>

    <span class="hljs-comment"># Convert latitude and longitude to</span>
    <span class="hljs-comment"># spherical coordinates in radians</span>
    phi_src = (<span class="hljs-number">90.0</span> - src_lat) * DEGREES_TO_RADIANS
    phi_dest = (<span class="hljs-number">90.0</span> - dest_lat) * DEGREES_TO_RADIANS

    theta_src = src_lng * DEGREES_TO_RADIANS
    theta_dest = dest_lng * DEGREES_TO_RADIANS

    <span class="hljs-comment"># Compute spherical distance from spherical coordinates.</span>
    cos = (math.sin(phi_src) * math.sin(phi_dest) * math.cos(theta_src - theta_dest) +
           math.cos(phi_src) * math.cos(phi_dest))
    arc = math.acos(cos)


    distance_in_km = arc * EARTH_RADIUS_KM

    <span class="hljs-keyword">return</span> distance
</code></pre>
<p>As you can see above, even with a clean code, it's difficult, near to impossible to understand what the code does. Why is that?, It's because it requires domain-specific knowledge. The variable names represent mathematical symbols and formulas but none of them give us context to understand what those formulas are needed, the use of comments, in this case, guides us through the steps and delivers us additional context. </p>
<p>With the use of those comments, we can quickly search on google for "Haversine function" and "spherical coordinates" to get the missing information.</p>
<h2 id="heading-write-comments-to-explain-why">Write comments to explain "Why"</h2>
<p>Comments are a great tool to explain things that cannot be explained through the code.</p>
<p>They are good for express the reasons behind our decisions and provide additional context.</p>
<ul>
<li>Why we write the code in this way?</li>
<li>Why we took that decision?</li>
<li>Why we are using this library?</li>
</ul>
<h3 id="heading-cases-where-is-a-good-idea-to-use-comments">Cases where is a good idea to use comments:</h3>
<ul>
<li>Add links to issues that are affecting our code<pre><code class="lang-JavaScript"><span class="hljs-comment">// Needed to handle Webpack and faux modules</span>
<span class="hljs-comment">// See https://github.com/fastify/fastify/issues/2356</span>
<span class="hljs-comment">// and https://github.com/fastify/fastify/discussions/2907.</span>
</code></pre>
</li>
<li>Explain exceptions to our code style<pre><code class="lang-JavaScript"><span class="hljs-comment">// Typescript quirk - libraries exported with `export = ` need to be imported using `require()`.</span>
<span class="hljs-comment">// See: https://github.com/microsoft/TypeScript/blob/master/doc/spec.md#1135-export-assignments</span>
<span class="hljs-keyword">import</span> chaiShallowDeepEqual = <span class="hljs-built_in">require</span>(<span class="hljs-string">'chai-shallow-deep-equal'</span>);
</code></pre>
</li>
<li>Redirect to an external source of information<pre><code class="lang-javascript"><span class="hljs-comment">// Override timezone formatting for MSSQL</span>
<span class="hljs-comment">// link: https://stackoverflow.com/questions/47056395/how-to-pass-a-datetime-from-nodejs-sequelize-to-mssql</span>
</code></pre>
</li>
<li>Justify our choices when writing the code<pre><code class="lang-JavaScript"><span class="hljs-comment">// Some errors contain not only line numbers in stack traces</span>
<span class="hljs-comment">// e.g. SyntaxErrors can contain snippets of code, and we don't</span>
<span class="hljs-comment">// want to trim those, because they may have pointers to the column/character</span>
<span class="hljs-comment">// which will get misaligned.</span>
<span class="hljs-keyword">const</span> trimPaths = <span class="hljs-function">(<span class="hljs-params">string: string</span>) =&gt;</span>
string.match(STACK_PATH_REGEXP) ? trim(string) : string;
</code></pre>
</li>
<li>Build automatic documentation<pre><code class="lang-JavaScript"><span class="hljs-comment">/**
* Initialize a new `View` with the given `name`.
*
* Options:
*
*   - `defaultEngine` the default template engine name
*   - `engines` template engine require() cache
*   - `root` root path for view lookup
*
* <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">name</span></span>
* <span class="hljs-doctag">@param <span class="hljs-type">{object}</span> <span class="hljs-variable">options</span></span>
* <span class="hljs-doctag">@public</span>
*/</span>
</code></pre>
</li>
<li>Prevent unexpected errors by providing warning messages<pre><code class="lang-JavaScript"><span class="hljs-comment">// This file is autogenerated by build/build-validation.js, do not edit</span>
</code></pre>
</li>
<li>Explain business decisions<pre><code class="lang-JavaScript"><span class="hljs-comment">/**
* This module contains code that will be migrated into 
* its own service
* DO NOT ADD MORE CODE INTO THIS MODULE
* see epic: https://jira.example.com/browse/PM-32145
**/</span>
</code></pre>
</li>
</ul>
<h2 id="heading-what-comments-should-we-avoid">What comments should we avoid?</h2>
<p>Comments can lie, that is a fact that can create confusion because, unlike code that keeps evolving, comments are rarely updated because it's not our priority as developers</p>
<p>In addition to redundant comments, we should avoid those that are implicitly or explicitly time-limited.</p>
<p>For example, the famous TODO or FIXME comments are used to let everyone know that there is a debt in the code that we must pay in the future, but the future could never come, and no one will review the code when the team needs to prioritize work. Instead of those comments, create a task in your task manager and label it "Technical Debt" to track how much debt the project generates and how much is paid in each iteration.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Comments are just another tool to solve a specific problem. The problem here is the lack of understanding after reading our code.</p>
<p>They aren't the bad of the movie, but they can become a waste of text so it's important to know when they are beneficial and when using them is nonsense.</p>
<p>As a general rule, write comments when you need to provide additional information that cannot be expressed through clean code.</p>
<p>If you like the article and want to support me to write more content like this</p>
]]></content:encoded></item><item><title><![CDATA[How we change our client’s thought that we were a slow team]]></title><description><![CDATA[One day, at the end of the sprint, my manager calls me. Very polite tells me that our client is upset and worried because he doubts we could deliver the product on time to the release date. We were hours before the end of the sprint, and I was busy t...]]></description><link>https://blog.rodrigomd.dev/how-we-change-our-clients-thought-that-we-were-a-slow-team</link><guid isPermaLink="true">https://blog.rodrigomd.dev/how-we-change-our-clients-thought-that-we-were-a-slow-team</guid><category><![CDATA[Scrum]]></category><category><![CDATA[software development]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Rodrigo]]></dc:creator><pubDate>Thu, 15 Oct 2020 21:55:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1602798888447/MYw581Mgd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One day, at the end of the sprint, my manager calls me. Very polite tells me that <strong>our client is upset and worried because he doubts we could deliver the product on time to the release date</strong>. We were hours before the end of the sprint, and I was busy trying to fix a problem in the checking account balance endpoint of our API. It was a complex problem that requires a lot of testing, but my manager didn’t understand why. He starts asking me some questions about our processes and methodology, trying to get to the problem’s root. He keeps asking until one of those questions triggers me.</p>
<blockquote>
<p>Don’t you think that five days is too much time to finish just one endpoint?</p>
</blockquote>
<p>I Immediately react, trying to justifying the time invested, perhaps so fast and in a high tone that my boss thought I was furious and tried to calm me down. He starts saying that he doesn’t enjoy making me work until late, neither in my day off (yup, I wasn’t supposed to working that day; it was an earned day off). He says that we were doing something wrong, and in the retrospective, we’ll together discover the causes with the team.</p>
<p>After the phone call, my impression was that he doesn’t know how difficult the problem was and all the team’s effort to recover us from a delay caused by the client’s changes at the last moment. That day the sprint review went all right, the project manager from the client side was pleased with our work, but the problem wasn’t him otherwise their boss. Until the call, we always had been thinking that we were doing right. These thoughts explain why it took the whole team and me for a surprise to know that our client wasn’t satisfied with our performance.</p>
<p>The day after the call with my manager, I decided to try his words, and after thinking about it, I started to believe that maybe he was right. We were doing something wrong because the time wasn’t enough to finish our work even with all the team’s extra effort.
We end it up moving the retrospective to the next day. On that morning, we receive a mail from our manager moving the meeting to another hour because he wanted to participate. When I saw the reactions of my teammates, well, there wasn’t much enthusiasm about it. So, because I was preparing myself to talk about the problem, I volunteered to lead the meeting and do the Ishikawa’s diagram to discover the causes of our client’s wrong impression of us. After a very long meeting with a lot of collaboration and constructive discussion, we came to this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1602798265153/dpRXRFUPB.png" alt="ishikawa_with_background.png" />It resulted in many causes that influenced our client’s perception. Some of them were <strong>poor communication, lack of information to start the sprint, changes introduced after planning, lack of crucial processes, and external factors not considered when planning</strong>. We recognize that all these problems came from a bad implementation of Scrum. It was the first time we used this framework. Without proper training, it would likely look like a Zombie Scrum implementation with many anti-patterns.</p>
<p>As a team, we resolve to implement two solutions: one in the short term and the other in the long way. In a short time, we made an extraordinary deploy to production to accomplish the client release date. After that, we set some actions to correct the remaining problems in the next few sprints.</p>
<p>Here are the actions that we implement and the learnings that we gain</p>
<h2 id="know-what-they-want-and-let-them-know-what-you-need">Know what they want and let them know what you need</h2>
<p>In the scrum framework, this is called the <em>definition of ready</em>. It means that the requirements to develop a user story should be identified and completed before starting to work on it. For example, we can’t start working on an endpoint that should use the client’s database model -given by them- without knowing the details of the model, the business meaning of the tables and columns, ordering, possible filters, and so on.</p>
<p>When you have identified these requirements, ask for them, be clear and set a deadline to the client, explaining that if they don’t meet the deadline, the user story will be re-prioritized and excluded from the following sprint. All this was made at the refinement meeting when we revisit some stories to polish their definitions and identify dependencies between them whenever possible. Also, discovering the development tasks needed to accomplished the story.</p>
<h2 id="anticipate-to-changes-communicate-at-a-fast-speed-when-things-happened">Anticipate to changes, communicate at a fast speed when things happened</h2>
<p>Sometimes the client isn’t clear about what user stories prioritize to develop them on the next sprint and change his mind barely hours before the start. One strategy to prevent this is to have the backlog updated and estimated with user stories to add and swap from the suggested sprint. That way, we can give the client the flexibility he needs to prioritizes according to the results and past events. There were other cases when the stories that were planned suffer changes in their definition or something unexpected occur that requires immediate attention and we need to include it in the sprint. Thus, it’s crucial to inform the team when these cases arise and update stories and acceptance criteria to reorganize the active sprint.</p>
<blockquote>
<p>Have the backlog updated and estimated with user stories to add and trade from the suggested sprint. That way, you can give the client the flexibility he needs to prioritizes according to the results and past events.</p>
</blockquote>
<p><strong>It’s essential to back up every change and agreement made with the client</strong>. Do this via email and copy it to every stakeholder for transparency, indicating the changes’ impact and how the development team will act according to these changes. Explain to the customer when a delay may happen because of the changes and advise him when to stop adding, updating, or removing stories. Additionally, it’s crucial to explain what they can exchange from the current sprint to include new stories; ideally, it should always be work equivalent in effort.</p>
<h2 id="there-is-always-space-for-improvement-optimize-your-development-cycle">There is always space for improvement: Optimize your development cycle</h2>
<p>When your clients think that your team is slow, it’s related to three main problems:</p>
<ul>
<li>Bad communication</li>
<li>Unoptimized workflow</li>
<li>Unattainable commitments</li>
</ul>
<p>They don’t understand what you are doing or how you do it, so you have a <strong>communication problem</strong>. Also, maybe you aren’t satisfying the client expectations about what should be delivered and when; this could be caused by an <strong>unoptimized workflow</strong> or setting higher expectations that you cannot accomplish (<strong>unattainable commitments problems</strong>).</p>
<p>Having and unoptimized development workflow is a tough one because even by resolving it, you can’t be sure that your client will change their mind if you keep setting unattainable commitments.</p>
<p>Optimize your development cycle and search for dead spaces in the sprint. Understand why team members are waiting until someone finishes a story needed to start working on their own stories.</p>
<p>Invest time on:</p>
<ul>
<li>Implementing CI/CD</li>
<li>Test automatization</li>
<li>Refine stories until you have almost only independent and small stories.</li>
<li>Increase the coverage, boost the confidence to refactor the code</li>
<li>Refactor what you can to allow easy extendability and maintainability.</li>
<li>Reduce loss time, use contracts, and API specs to split the development so that nobody has to wait until others finish their work. (try swagger virtualization). Keep an eye on the number of dependant stories in the sprint. It could mean that the team is planning badly.</li>
<li>Revisit your git workflow and implementation. Maybe it isn’t the most suitable for this project.</li>
<li>Search for tools to increase the team’s productivity: code generators, CLI, libraries, packages, frameworks.</li>
<li>Begin to measure; the time to finish stories, how many times a story is blocked, occurs unexpected events that affect development?. Then set actions together on every retrospective to eliminate the bad behavior and maintain good practices.</li>
<li>Reduce the batch size of the sprint. That way, you reserve free space for unplanned work.</li>
</ul>
<p>The primary purpose is to gain more time through automation and use that time on any unplanned work during the sprint. Also, keep watching any constraints to avoid possible bottlenecks on the sprint.</p>
<p>There is always room for improvement, so you must increase team efficiency to be effective. After eliminating one of the reasons why the team is considered slow, you’ll see a boost in productivity but beware of unattainable compromises. Measure the velocity and capacity of the team, explain it clearly to the client before building the next sprint, and remember never to plan above the maximum capacity of the team, or you’ll continue losing the client’s faith for not meeting your commitments.</p>
<h2 id="be-the-technology-expert-explain-your-development-workflow-to-the-client">Be the technology expert: Explain your development workflow to the client</h2>
<p>Even these days are companies that don’t know how software is made, perhaps because they don’t need to know to use it. But, it’s valuable for the team that your client knows this type of information. It can help to understand why you make some decisions or why it’s too challenging to build something in a short period.</p>
<p>As a software engineer — or any equivalent role — It’s expected that you identify obstacles and risks to develop an application or system. You and your team are the technology experts, so behave like that and express your thoughts, emphasize what is essential and crucial to do for the project’s sake from a technological perspective. Also, inform the consequences of sacrifice quality against velocity. Even if you don’t work for an external client you are the technology consultant for the company that hires you.</p>
<p>Some clients have an IT area and maybe they do things faster than your team. But that doesn’t mean they do it right, this way of thinking can be easily changed when you explain why there is a development process with defined stages and what does every step in the process. Why you expend time writing tests or why sometimes is necessarily doing a refactor before adding new functionality.</p>
<p><strong>The trick is explaining to your customer that they are paying for a product that is expected to be durable, that should fulfill a necessity, and to do so you need to focus not only on building new features fasts but also on doing it right</strong>. It should be a concern no only to make the software but also to prepare it to be easy to maintain, that way, it remains valuable for a long time and at a low cost.</p>
<h2 id="build-a-trustworthy-relationship-by-improving-the-communication">Build a trustworthy relationship by improving the communication</h2>
<p>One of the main things that helped us to change our client’s perception was to improve communication. We increase the dialog through phone calls and emails and always show them respect and a complete compromise to do our best for their product. But, there is a difference between making our best effort and do everything right. Sometimes you can commit errors and accidentally delete the client’s data or take down a reviewed service during a meeting. These things can happen all the time, and rather than trying to hide our blame someone else, it has more value to be transparent and honest.</p>
<p>There are plenty of companies and professionals capable of doing the work that your client needs, so always focus on building a trustworthy relationship; this adds value on top of high-quality products and is an advantage over your competition.</p>
<blockquote>
<p>Mistakes are hidden opportunities to win the confidence of your client, all depends on how you approach them.</p>
</blockquote>
<p>If you commit a mistake, don’t worry. Tell your client the situation and explain the options to solve the problem, also design ways to ensure that it will not happen again. The same approach can be applied when there are delays in the planning, raise your hand early to reveal what happened, and what should be the actions to prevent these delays.</p>
<h3 id="now-continuing-with-the-story">Now, continuing with the story</h3>
<p>In the next sprints, we apply and keep track of all the actions described above. We deliver more without sacrificing the quality by organizing the stories better and improving the timing between requesting the required information to start working on a story and added into a sprint. Also, we increase the messages in the communication channels and transparent any problem or external events that could affect the planned work.</p>
<p>We change our mindset from following the client’s indications without arguing to proposing better approaches and solutions to their product.</p>
<p>We notice changes in the attitude during the sprint reviews. The product manager and the manager itself -from the client-side- start to give us compliments about our work. They were genuinely impressed with the quality and the compromise of the team. They tell our manager that they were astonished about the team performance and velocity; they appreciate the effort and are glad for the results.</p>
<p>The changes that we implement helped us to make an impact on our relationship with the client. Now they understand and also believe in our way of work, all because of the outstanding results. In the final weeks of the project, they only call our boss to compliment and coordinate new opportunities for working together.</p>
<p>With all the team’s compromise to implement the actions described in this article, we manage to deliver a high-quality product that satisfies our client’s needs and changes their opinion about us.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1602798695169/TAQ1nr0JT.png" alt="four_steps_to_improve.png" />The process described here is summarized in the diagram. It’s an iterative process in which we first use a neutral approach taking all the complaints to analyze them and discover insights that tell us what we are doing wrong. Then we set actions to correct bad behavior and improve the good ones. The most significant change introduced was to invest time teaching and educating our clients to know how we work and the benefits of our methodology. To keep the whole process sharp, we keep track of every experiment and action defined through metrics. That way, we know what is working and what should be corrected or discarded.</p>
<blockquote>
<p>Perhaps the process described here is not suitable for your problems, and even if it does it, you may not have the same results with your clients.</p>
</blockquote>
<p>Remember that <strong>every client is a different world, and for some of them, it is more important to meet a deadline rather than have a fully tested, robust, and reliable system</strong>.</p>
<p>You should invest time talking with your clients to know what their worries are. Usually, a product owner knows these things but is vital to transmitting to every team member to be aligned and on the same page.</p>
<p>We went through everything due to Scrum’s poor implementation and not having the proper knowledge, neither a coach nor a scrum master. Getting out of that situation was very difficult, so I suggest starting well and hiring an agile coach or an experienced scrum master to teach the principles and values ​​to all those involved in developing the product, but most importantly to the non-developers. Your team will save a lot of headaches if you can count on that support.</p>
]]></content:encoded></item></channel></rss>