<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Building Inventa - Medium]]></title>
        <description><![CDATA[Building inventa.shop to help millions of small entrepreneurs to succeed! - Medium]]></description>
        <link>https://medium.com/building-inventa?source=rss----c27bd647448b---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Building Inventa - Medium</title>
            <link>https://medium.com/building-inventa?source=rss----c27bd647448b---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 13:53:03 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/building-inventa" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Infrastructure Resource Creation with Backstage]]></title>
            <link>https://medium.com/building-inventa/infrastructure-resource-creation-with-backstage-f36ae01633b5?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/f36ae01633b5</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[backstage]]></category>
            <category><![CDATA[devex]]></category>
            <category><![CDATA[iac]]></category>
            <category><![CDATA[developer-experience]]></category>
            <dc:creator><![CDATA[Lucca La Fonte]]></dc:creator>
            <pubDate>Thu, 08 Aug 2024 13:47:08 GMT</pubDate>
            <atom:updated>2024-08-08T13:47:08.072Z</atom:updated>
            <content:encoded><![CDATA[<h3>TL;DR</h3><p><em>This article explores AWS resource creation (using SQS as an example) via the Backstage Software Templates plugin and how it helps application and platform developers deploy code faster, cleaner, and safer.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gofw4gcTJWPAsaBx" /><figcaption>Photo by <a href="https://unsplash.com/@danist07?utm_source=medium&amp;utm_medium=referral">Danist Soh</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>As a company grows in size, number of collaborators, and system complexity it’s good (or dare I say, essential) as an engineering platform team to look at the <a href="https://github.blog/enterprise-software/collaboration/developer-experience-what-is-it-and-why-should-you-care/">Developer Experience</a> as a whole.</p><p>One of the aspects of DevEx that flourished over the past couple of years is the implementation of <a href="https://internaldeveloperplatform.org/what-is-an-internal-developer-platform/">Internal Developer Platforms</a> (IDPs) to make application developers deliver software cleaner, safer, and faster than before. This is done by decreasing the cognitive load on those teams by providing tools like a software catalog, service quality tiers, and software templates.</p><p>At Inventa we built our IDP using <a href="https://backstage.io/">Backstage</a> as our central piece. In this article, we will explore how we leverage <a href="https://backstage.io/docs/features/software-templates/">Backstage’s Software Templates plugin</a> (plus some extensions 👀) and Terraform to enable application developers to create AWS resources on our infrastructure easier and faster.</p><h3>Quick Recap</h3><p><a href="https://medium.com/building-inventa/defining-an-effective-infrastructure-as-code-iac-strategy-building-for-scalability-and-3d3bad8beeee">We already have an awesome article that goes into detail on our Terraform repo file distribution and deployment</a>, but the TLDR is that we have a monorepo where each one of our services declares the necessary resources in the following way:</p><pre>&quot;product-catalog&quot; (* Project main path *)<br>├─ &quot;catalog-info.yaml&quot; (* Backstage configuration file *)<br>├─ &quot;ecr.tf&quot; (* Elastic Container Register definitions *)<br>├─ &quot;env&quot;<br>│   ├─ &quot;backends&quot;<br>│   │   ├─ &quot;dev.tfvars&quot; (* Terraform backend file definitions *)<br>│   │   └─ &quot;prod.tfvars&quot; (* Terraform backend file definitions *)<br>│   ├─ &quot;dev.tfvars&quot; (* Terraform variables with values for development environment *)<br>│   └─ &quot;prod.tfvars&quot; (* Terraform variables with values for production environment *)<br>├─ &quot;locals.tf&quot; (* Terraform file to create expressions, so you can use the parameters multiple times within a module instead of repeating the expression. *)<br>├─ &quot;route53.tf&quot; (* Route53 definitions *)<br>├─ &quot;secrets.tf&quot; (* Secrets Manager definitions *)<br>├─ &quot;sqs.tf&quot; (* SQS Queue definitions *)<br>└─ &quot;vars.tf&quot; (* Terraform file to manage all variables *</pre><p>In the example above we have a product-catalog service that creates ECR, Route53, Secrets Manager, and SQS resources through the files above (ecr.tf, route53.tf, secrets.tf and sqs.tf respectively). Beyond that, specific variables are set either on env/dev.tfvars or env/prod.tfvars.</p><p>An important point of this architecture is that the recipe files are rarely (preferably never) changed, the changes are made using env/dev.tfvars or env/prod.tfvars variables, depending on the environment.</p><p>For example, the sqs.tf looks something like this:</p><pre>module &quot;list_queue_deadletter&quot; {<br>  # Module reference<br>  source = &quot;...&quot;<br>  for_each = { for r in var.sqs_list_of_queues : r.name =&gt; r }<br>  name                      = &quot;${each.value.name}-dlq&quot;<br>  ...<br>}<br>module &quot;list_encrypted&quot; {<br>  # Module reference<br>  source = &quot;...&quot;<br>  for_each = { for r in var.sqs_list_of_queues : r.name =&gt; r }<br>  name                       = each.value.name<br>  sns_sub                    = each.value.sns_sub<br>  event_sub                  = each.value.event_sub<br>  s3_sub                     = each.value.s3_sub<br>  ...<br>  depends_on = [<br>    module.list_queue_deadletter<br>  ]<br>}</pre><p>And the dev.tfvars/prod.tfvars looks like this:</p><pre>...<br># SQS vars<br>sqs_list_of_queues = [<br> {<br>   &quot;name&quot;: &quot;product-catalog-created-event&quot;,<br>   &quot;sns_sub&quot;: true,<br>   &quot;event_sub&quot;: false,<br>   &quot;s3_sub&quot;: null<br> }<br>]<br>...</pre><p>The being that every time we want to create a new SQS queue for this service we only need to add a new entry to the sqs_list_of_queues array.</p><h3>Before the IDP…</h3><p>Now, say that we have a new hire that wants to create a queue for a service that still does not have any queues. The creation flow would be as follows:</p><ol><li>Clone the monorepo where we declare the resources for each service;</li><li>Find the service folder and create a sqs.tf file (probably copying from other services);</li><li>On the env/dev.tfvars and/or env/prod.tfvars create the sqs_list_of_queues array;</li><li>Add the queue with the necessary information to the array;</li><li>Open a PR so the platform team can review the changes;</li><li>If everything in the step above is correct, merge the PR. The resources will be created automatically through the pipeline.</li></ol><p>The flow works, but it has its flaws:</p><ol><li>Application developers should not necessarily know the ins and outs of how the infrastructure is created. The extra cognitive load that the process above brings takes away valuable time (and brain power) that could be focused on delivering value to the product.</li><li>The code quality can not be guaranteed at any level when the PR is opened, leading to more time used by the platform developers on the review process.</li><li>The process above has a lot of boilerplate and copy-and-paste, which leads to a more time-consuming and error-prone flow.</li></ol><p>To address both points we decided to use Backstage’s Software Templates feature to help us automate all the steps necessary to create an infrastructure resource, but before tackling that we had a roadblock in the way that we needed to address…</p><h3>Terraform, Backstage, and Updating Files</h3><p>The Software Templates is a great feature from Backstage, but it had a limitation for our specific use case. You see, it’s all well and good when we want to create new files or delete existing files during the template steps, using built-in actions like fetch:template, fetch:plain and fs:delete, the problem comes when we want to <strong>update</strong> an existing file surgically.</p><p>Using our SQS example, if we wanted to add a new product-catalog-deleted-event we should change the *.tfvars files to something like:</p><pre>...<br># SQS vars<br>sqs_list_of_queues = [<br> {<br>   &quot;name&quot;: &quot;product-catalog-created-event&quot;,<br>   &quot;sns_sub&quot;: true,<br>   &quot;event_sub&quot;: false,<br>   &quot;s3_sub&quot;: null<br>+ },<br>+ {<br>+   &quot;name&quot;: &quot;product-catalog-deleted-event&quot;,<br>+   &quot;sns_sub&quot;: true,<br>+   &quot;event_sub&quot;: false,<br>+   &quot;s3_sub&quot;: null<br> }<br>]<br>...</pre><p>Sadly, these changes are not easy to do using just the actions that come with Backstage. Luckily, extensibility is a topic that the Backstage team takes very seriously, and the Software Templates plugin is no exception.</p><p>Introducing <a href="https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/scaffolder-actions/scaffolder-backend-module-utils/README.md">Roadie’s scaffolder-backend-module-utils actions package</a> which, as the name implies, is an extension of the Software Templates plugin with actions that help work with files, JSON, YAML, etc. For our use case, we used the <a href="https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/scaffolder-actions/scaffolder-backend-module-utils/README.md#merge-json">merge JSON action</a> in conjunction with the fact that Terraform supports JSON files with Terraform variables.</p><p>So, for this to work, we migrated our dev.tfvars and prod.tfvars files to dev.tfvars.json and prod.tfvars.json, looking now something like this:</p><pre>{<br>...<br>&quot;sqs_list_of_queues&quot;: [<br> {<br>   &quot;name&quot;: &quot;product-catalog-created-event&quot;,<br>   &quot;sns_sub&quot;: true,<br>   &quot;event_sub&quot;: false,<br>   &quot;s3_sub&quot;: null<br> }<br>],<br>...<br>}</pre><p>No other changes were made in the structure and now we are ready to implement the template on Backstage :)</p><h3>Template Implementation</h3><p>With all that we discussed above the idea is to create a template that automatically configures the necessary files depending on what is needed:</p><ul><li>If the service already has the SQS configuration it just updates the sqs_list_of_queues array with the new queue.</li><li>If not, it creates the array, and creates the sqs.tf file.</li></ul><p>An example of how this template YAML looks like is:</p><pre>apiVersion: scaffolder.backstage.io/v1beta3<br>kind: Template<br><br># some metadata about the template itself<br>metadata:<br>  name: sqs-queue-template<br>  title: SQS Queue Template<br>  description: Template for creating a new SQS queue<br>spec:<br>  owner: engineering-foundation<br>  type: sqs-queue<br><br>  # these are the steps which are rendered in the frontend with the form input<br>  parameters:<br>    - title: Service information<br>      required:<br>        - entity<br>      properties:<br>        entity:<br>          title: Service<br>          description: Choose a service to create the queue for<br>          type: string<br>          ui:field: EntityPicker<br>          ui:options:<br>            allowArbitraryValues: false<br>            catalogFilter:<br>              - kind: Component<br>                spec.type: service<br>            defaultKind: Component<br>    - title: SQS queue general information<br>      required:<br>        - queueName<br>        - envs<br>      properties:<br>        queueName:<br>          title: Queue name<br>          type: string<br>          description: Name of the new queue<br>        envs:<br>          title: Envrioments<br>          description: Select environments to create the queue<br>          type: array<br>          minItems: 1<br>          items:<br>            type: string<br>            enum:<br>              - production<br>              - development<br>          uniqueItems: true<br>          ui:widget: checkboxes<br>        createConfiguration:<br>          title: Create initial SQS configuration<br>          description: Should the initial SQS configuration be created for the service?<br>          type: boolean<br>          default: false<br>    - title: SQS queue specific information<br>      properties:<br>        snsSub:<br>          title: SNS subscription<br>          description: Will the queue subrcibe to a SNS topic?<br>          type: boolean<br>          default: false<br>        eventSub:<br>          title: Eventbridge subscription<br>          description: Will the queue subrcibe to a Eventbridge event?<br>          type: boolean<br>          default: false<br>        s3Sub:<br>          title: S3 subscription<br>          description: Set the ARN of the S3 bucket (leave empty if the queue will not subscribe to an S3 bucket)<br>          type: string<br><br>  # here&#39;s the steps that are executed in series in the scaffolder backend<br>  steps:<br>    - id: fetch-vars<br>      name: Fetch service terraform variables<br>      action: fetch:plain<br>      input:<br>        url: &lt;https://github.com/inventa-shop/aws-resources/blob/main/terraform/$&gt;{{ parameters.entity | parseEntityRef | pick(&#39;name&#39;) }}/env<br>        targetPath: ./output/env<br>    - id: add-queue-dev<br>      name: Add queue to list of queues (development)<br>      if: ${{ parameters.envs.includes(&#39;development&#39;) }}<br>      action: roadiehq:utils:json:merge<br>      input:<br>        path: ./output/env/dev.tfvars.json<br>        mergeArrays: true<br>        content:<br>          sqs_list_of_queues:<br>            - name: ${{ parameters.queueName }}<br>              sns_sub: ${{ parameters.snsSub }}<br>              event_sub: ${{ parameters.eventSub }}<br>              s3_sub: ${{ parameters.s3Sub if parameters.s3Sub else null }}<br>    - id: add-queue-prod<br>      name: Add queue to list of queues (production)<br>      if: ${{ parameters.envs.includes(&#39;production&#39;) }}<br>      action: roadiehq:utils:json:merge<br>      input:<br>        path: ./output/env/prod.tfvars.json<br>        mergeArrays: true<br>        content:<br>          sqs_list_of_queues:<br>            - name: ${{ parameters.queueName }}<br>              sns_sub: ${{ parameters.snsSub }}<br>              event_sub: ${{ parameters.eventSub }}<br>              s3_sub: ${{ parameters.s3Sub if parameters.s3Sub else null }}<br>    - id: add-empty-queue-list-dev<br>      name: Add empty queue list (development)<br>      if: ${{ parameters.envs.includes(&#39;development&#39;) === false and parameters.createConfiguration }}<br>      action: roadiehq:utils:json:merge<br>      input:<br>        path: ./output/env/dev.tfvars.json<br>        mergeArrays: true<br>        content:<br>          sqs_list_of_queues: []<br>    - id: add-empty-queue-list-prod<br>      name: Add empty queue list (production)<br>      if: ${{ parameters.envs.includes(&#39;production&#39;) === false and parameters.createConfiguration }}<br>      action: roadiehq:utils:json:merge<br>      input:<br>        path: ./output/env/prod.tfvars.json<br>        mergeArrays: true<br>        content:<br>          sqs_list_of_queues: []<br>    - id: fetch-skeleton<br>      name: Fetch new SQS configuration files<br>      if: ${{ parameters.createConfiguration }}<br>      action: fetch:plain<br>      input:<br>        url: ./skeleton<br>        targetPath: ./output<br>    - id: publish-pr<br>      action: publish:github:pull-request<br>      name: Create pull request<br>      input:<br>        repoUrl: github.com?repo=&lt;REPO&gt;&amp;owner=&lt;OWNER&gt;<br>        branchName: feature/add-${{ parameters.queueName }}-queue<br>        targetBranchName: main<br>        title: Create ${{ parameters.queueName }} SQS queue<br>        description: |<br>          This PR adds a new ${{ parameters.queueName }} SQS queue for the ${{ parameters.entity | parseEntityRef | pick(&#39;name&#39;) }} service.<br>          Author: @${{ user.entity.metadata.name }}<br>          _Created automatically via Backstage_ 🚀<br>        sourcePath: ./output<br>        targetPath: terraform/${{ parameters.entity | parseEntityRef | pick(&#39;name&#39;) }}<br>        commitMessage: (feature) add ${{ parameters.queueName }} SQS queue<br>        forceEmptyGitAuthor: true<br><br>  # some outputs which are saved along with the job for use in the frontend<br>  output:<br>    links:<br>      - title: Pull Request<br>        url: ${{ steps[&#39;publish-pr&#39;].output.remoteUrl }}</pre><p>In this example, we get the service that needs the queue via the EntityPicker and, after that, the queue information and which environment it will be created. On the steps, we can see that we use the createConfiguration variable to determine if we are going to use the skeleton or not (where the sqs.tf file resides).</p><p>Below we have some screenshots of the actual template that we are using (a little more complex than the one we presented above but the basics are the same):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*u4fLPrBdlFMCgh918pr__w.png" /><figcaption>On this page, we select for which service the queue will be created</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v33J1BI7him7JJmoVJ-R3w.png" /><figcaption>Here we set the queue name, envs on which it will be created, and if the initial configuration needs to be done</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rtcpODkKBhyx-63hK8C4Gw.png" /><figcaption>Here we set some configurations of the queue</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IIj1uUcqZrGN4lQsmuthbw.png" /><figcaption>Finally, we can check if everything is correct</figcaption></figure><p>After the PR is opened the platform team can review it as usual, with the difference that only what is changed by the user on the template needs to be checked since the rest is pre-configured on the template.</p><p>This logic can be extended for all kinds of resources in the company and this strategy solves the pain points mentioned at the beginning of the article, making it possible for application teams to easily create resources and focus time and effort on the business while platform teams have to focus only on code that is generated via user input.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f36ae01633b5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/infrastructure-resource-creation-with-backstage-f36ae01633b5">Infrastructure Resource Creation with Backstage</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Product Migration: Moving from an MVP to a Scalable Product Base]]></title>
            <link>https://medium.com/building-inventa/product-migration-moving-from-an-mvp-to-a-scalable-product-base-4435293792ea?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/4435293792ea</guid>
            <category><![CDATA[migration]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[scale]]></category>
            <category><![CDATA[mvp]]></category>
            <dc:creator><![CDATA[Pamela Peixinho]]></dc:creator>
            <pubDate>Mon, 29 Jan 2024 19:38:55 GMT</pubDate>
            <atom:updated>2024-01-29T19:38:55.773Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>We have transitioned from an MVP SaaS-based approach for validating market fit to a scalable product-based approach for the entire workflow.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*onivQIW-F5isOId2" /><figcaption>“Wooden house being held by ropes like the MVP atop SaaS.” (<a href="https://unsplash.com/pt-br/fotografias/casa-de-madeira-azul-e-branca-no-campo-de-grama-verde-durante-o-dia-1UMrSoItdDE">Unsplash</a>)</figcaption></figure><h3>Context</h3><p>In the world of product development, building a Minimum Viable Product (MVP) is a common approach to validate market fit without investing too much time and resources upfront. Once the MVP is validated, it’s time to migrate to a more scalable product base to support the entire flow.</p><p>This was the situation when I joined Inventa, a startup that had been running for a year with an MVP constructed based on Shopify and Zoho customizations and a series of 1000-line lambdas. The market-fit results were impressive, but Inventa faced problems in product evolution and handling a lot of incidents. Our main goal was to change this scenario! We needed to prioritize a full product migration to a solid foundation for this promising startup.</p><h3>The migration prioritization</h3><p>We realized the need to internally establish a scalable foundation for our product in our infrastructure, which we referred to as “Shopify / Zoho Out”.</p><p>Creating a new marketplace involves more than just the front end. We had to reconstruct the entire workflow, from user sign-up to order management, finance operations, and internal back offices. Understanding and maintaining everything was challenging, so we recognized the need for an “engineering rebranding”. To ensure a successful product, our focus was on creating a solid and flexible foundation that would allow for easy and quick product evolution. We were able to do that because we shared the main limitations and incidents that caused a lot of instability and limitations to our business.</p><p>However, we also acknowledged the potential for quick wins and improvements in the current product. Therefore, we aimed to strike a balance between enhancing the current product and developing the new marketplace in-house, considering all trade-offs. This approach helped us understand the ROI (return on investment) for each promising new change.</p><p>Before proceeding with coding and developing the entire platform with 8 teams, we needed to create a detailed plan for executing this game-changing project. The following topics will outline this plan.</p><h3>Mapping AS-IS e TO-BE</h3><p>To ensure a smooth and successful migration, we conducted a thorough analysis of the system’s current state (AS-IS). Since understanding the current product was challenging, we created a service blueprint of the product as it is, which includes all its business rules and interactions with internal and external users.</p><p>The Engineering leadership led this process because no one truly understood the product, and we were still in the process of building a Product and Design Team at the beginning of 2022 and 2023.</p><p>Through this process, we gathered existing knowledge about the product and made several discoveries to understand and map the current developments. This helped us identify end-to-end rules that could be developed or improved.</p><p>We created a template for the expected service blueprint we needed. Although we did not strictly adhere to precise service blueprint principles, we created a representation of customer actions and backstage actions that could be taken by internal users or our system. You can see the template below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nvgf47FqfZl5xj5uB4gQqw.png" /><figcaption>AS-IS Template (Source: Article Author)</figcaption></figure><p>With this template and through a rigorous and multidisciplinary effort led by the Engineering team, we have mapped the entire journey of Inventa. This includes the user journeys from their initial interaction to the final interactions, covering the perspectives of sellers, buyers, and internal operations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9YhpDxYOLbhU_SQsV0Fstw.png" /><figcaption>AS-IS Big Picture (Source: Article Author)</figcaption></figure><p>This allows us to envision the desired future state (TO-BE). In this step, we need to:</p><ul><li>Address the issues identified in the current state (AS-IS)</li><li>Incorporate new desired flows</li><li>Consider potential possibilities</li></ul><p>As a result, we have created a TO-BE blueprint using the same template:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*PuuMHIfkfn4BWyBguC09SA.png" /><figcaption>TO-BE Big Picture (Source: Article Author)</figcaption></figure><p>This process was collaboratively built. We brought together different disciplines, stakeholders, and C-level individuals in the same room. Our goal was not to dictate every detail of how things should be done but rather to establish a guiding principle to follow.</p><blockquote>“If you do not know where you want to go, it doesn’t matter which path you take.” — Lewis Carroll</blockquote><p>This approach allowed us to identify areas that needed improvement and establish a clear vision for the migration process. Now, with all of this in mind, we need to create a plan to put it into action.</p><blockquote>“A goal without a plan is just a wish.” — Antoine de Saint-Exupéry</blockquote><h3>Developing a migration plan</h3><p>Putting it all together, we had a clear understanding of both the current product and our north star based on the available information. Now, it was time to develop a strategy for migrating our product. While many companies opt for the big bang approach, we were determined to avoid that.</p><p>When discussing a phased migration, the first step is to take a step back and consider all the potential options. This could involve exploring different user migrations, product flow migrations, or internal system migrations. There is no right or wrong approach, but it is important to determine the best option for your specific case.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/838/1*CA8ezxFzavdQaHc3-z0NVQ.png" /><figcaption>Migration Ways (Source: Article Author)</figcaption></figure><p>When examining Inventa&#39;s scenario, I thoroughly considered the following aspects:</p><ul><li><strong>Viability</strong>: Is it feasible to migrate in this manner? Are there any dependencies that could make it difficult or even impossible to change one part without affecting the other?</li><li><strong>Pros</strong>: What are the potential positive impacts of this migration? How would it benefit the team, maintenance, users, and the company?</li><li><strong>Cons</strong>: What are the potential negative impacts of this migration? What challenges might arise during the development process?</li></ul><p>After carefully analyzing all the possibilities, it became evident that using the legacy product based on Shopify and lambdas was not a viable option for either the product flow migration or the system migration. The most suitable approach was to migrate by user groups.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/838/1*fVNEy4MIsUZmOK_4PbqCRw.png" /><figcaption>Migration Choice (Source: Article Author)</figcaption></figure><p>This means that I needed to understand the personas of our users and the business requirements for each type of persona. This understanding would help me determine if there was a clear boundary that I could use to create delivery phases. The first types of personas were the Individual User (CPF) and the Business User (CNPJ).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/421/1*Ks9AYSIqAq41ElhP2kwP8Q.png" /><figcaption>Inventa&#39;s Users (Source: Article Author)</figcaption></figure><p>However, having thousands of users for each type was not sufficient. We needed to determine the minimum business requirements for users to comprehend how we could promptly deliver the initial version and gradually enhance it to accommodate more users.</p><p>Consequently, we identified the following initial features that could assist us: <strong>Historical Info</strong> (for new users or those already registered with historical information) and <strong>Credit for Business Users</strong> (pre-approved credit that they could utilize in the marketplace).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/652/1*WqPqRz1lEoGtS9J90ovP-w.png" /><figcaption>Inventa’s Users Detailed (Source: Article Author)</figcaption></figure><p>Based on that vision, we drafted an initial rollout version with 6 phases, taking into account the business requirements and engineering complexities of the product development. We then held meetings with various teams and stakeholders. During these interactions, we evaluated and improved our versions of the phases to be delivered. The revised versions are outlined below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*NE_I_AfZEj-H5tId8cMz4Q.png" /><figcaption>Versioning Migration Phases Updates (Source: Article Author)</figcaption></figure><p>Then we arrived at the final version (2.1) after successfully completing 4 phases. The final result was:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/728/1*O37UWyiNvSgpl7ZlWRD2dQ.png" /><figcaption>Migration Phases (Source: Article Author)</figcaption></figure><p><strong>V0 — Alpha:</strong> This phase focused on signing up individual users. Since these users were unfamiliar with our site and we had minimal features, signing them up was the first step.</p><p><strong>V0.1 — Beta:</strong> In this phase, we targeted all individual users who didn’t require the complexities associated with businesses, such as taxes, shipping, and enrichment by government providers.</p><p><strong>V0.2 — Gamma:</strong> We focused on current business users in this phase. These users were already on our platform and had undergone credit analysis and business profile enrichment by the government. As a result, we were able to introduce these complexities in the final phase while delivering it to our existing users.</p><p><strong>V0.3 — Delta:</strong> This phase involved signing up business users. It represented the last and most comprehensive stage of product development, catering to all users, including new ones, with all the necessary complexities.</p><p>The Beta and Gamma phases underwent internal rollouts, which were collaboratively constructed by the engineering and data teams. This allowed us to select the most active users for the rollout while excluding inactive ones.</p><p><strong>Migrating Users</strong></p><p>There are two important points to understand about the process of migrating all users: Users data migration and User group rollout.</p><p>For the user’s data migration, we started using the legacy platform to migrate users and their access to the new platform. When users log in to Shopify, we began the migration process 2/3 months in advance. This allowed us to migrate the data of all active users. When the migration time arrived, we constructed the structure and performed the data migration with the help of the data and engineering teams.</p><p>For the rollout of the user group, we created an isolated table in our database to control the migrated users. For each rollout, we brought together the product, engineering, and data teams. We defined the strategy for selecting users based on our business strategy, promotional campaigns, and what we wanted to test and monitor on the new platform. Next, we created a list of users and the engineering team added all of these users to our system through an endpoint. When a user did visit our homepage <a href="http://inventa.shop/">inventa.shop</a>, they are redirected to the correct platform based on the information in this control table.</p><h3>Mapping Dependencies and Delivery Dates</h3><p>While creating the release phases, we considered the scope of each team and identified areas with higher demand. To align with the migration plan, it was crucial to understand how to execute it. This involved planning the game with all teams and mapping out the dependencies between them. Thus, the team planning puzzle emerged.</p><p>Delivery planning plays a vital role in ensuring a smooth and successful product migration. By understanding the relationships between different tasks, identifying dependencies, and estimating timeframes, teams can effectively coordinate their efforts. This is exactly what we did.</p><p>Initially, we faced the worst-case scenario where critical dependencies were estimated to be completed after the development tasks that required integration with them. To address this, we realigned the scopes and adjusted the delivery order for all teams and tribes. Recognizing the need for additional assistance, we focused our team efforts accordingly. With this plan in action, we successfully achieved the first Alpha release, marking a significant milestone.</p><p>Moving on to the next phase, we conducted a similar dependencies mapping exercise. As you can see, we successfully aligned all the necessary components for the upcoming versions. This accomplishment is illustrated in the image below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fI99BpcoN8-QkFYaGCc6kQ.png" /><figcaption>Real Notion Print from Beta phase (Source: Article Author)</figcaption></figure><h3>Follow-up Process</h3><p>As mentioned before, this project was the most important one for the company at that time. It was crucial for us to ensure that we stayed on track, monitored the progress, managed dependencies, addressed cross-discussions, resolved pending definitions, and handled any potential issues. To achieve this, we established a weekly meeting with the following structure:</p><p><strong>Discussion Points</strong></p><p>We began with an open agenda, providing an overview of the project’s progress. Then, all meeting attendees had the opportunity to add topics they wanted to discuss in the meeting notes. This approach worked effectively for everyone involved, as it allowed us to address cross-tribe themes that were important to all stakeholders.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2F-_da2aGBell0a5qYyLTA.png" /><figcaption>Discussions points (Source: Article Author)</figcaption></figure><p><strong>Pending Definition</strong></p><p>We also need to align follow-up pending definitions for each phase. This will help us keep track of progression, definition, and development blockers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*StqCtnLBXNPzL5a3GA6kzg.png" /><figcaption>Pending definitions (Source: Article Author)</figcaption></figure><p><strong>Intruders</strong></p><p>One major challenge when prioritizing a structural task like migration is that the company’s operations continue, which means new bugs and requests may arise. Depending on how many of these we prioritize, it can increasingly delay our migration progress. It is important for us to be able to report this to the company leadership. Therefore, we make a note of all the issues that arise during development. This has been very helpful, especially when we needed to update the proposed timeline and get approval from the leadership.</p><p>This approach provides a clear and easy way to communicate what is happening and keep everyone informed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9i87oE-g5btIuqbtInLEsQ.png" /><figcaption>Intruders (Source: Article Author)</figcaption></figure><p><strong>Progression</strong></p><p>Each tribe was responsible for conducting their own follow-up and updating the meeting table with the progression of each macro delivery before each meeting. They also indicated their level of confidence. This process greatly helped us understand which teams required more attention or assistance, as well as when we needed to rethink the plan or the defined dates. The discussions that followed were beneficial for everyone involved, particularly for the company and our users.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/808/1*bnDXym0jXNFv1B0aSrkjeQ.png" /><figcaption>Releases Progression Updates (Source: Article Author)</figcaption></figure><h3>Results</h3><p>I wrote this article one year after the migration, and I can confidently say the results were excellent. Not only during the migration process but also in terms of the solid foundation we built for our product. This foundation enabled us to conduct various tests, experiments, and developments for the growth and profitability of the company, all while keeping maintenance costs low. The engineering team has a clear and concise understanding of our system architecture and takes ownership, resulting in fewer recurring and preventive maintenance tasks. Incidents are now rare, which is a significant improvement compared to when we started this article.</p><p>It is not easy for any company to stop delivering new features or fixes to prioritize structural changes. However, the results that follow have a huge impact on everyone involved, especially our users. They now have a stable product that allows for faster delivery of new features and changes.</p><h3>Learnings</h3><p>Of course, it wasn’t a smooth journey. We have learned a lot from things we did right and from the challenges that gave us headaches.</p><p><strong>Collaborative work between product, data, and engineering in the rollout</strong></p><p>This was the best choice we made. We executed it well, and the results greatly contributed to the successful completion of the migration.</p><p><strong>Corner Cases</strong></p><p>One corner case that we overlooked was the creation of Personal and Business users with the same email on different platforms (legacy and the new marketplace). Although we had accounted for some of these cases, one slipped through. As a result, we had to perform additional migrations to fix it promptly. The main lesson here is to give more emphasis to corner cases and test them thoroughly before releasing them to the product. However, some cases will inevitably arise that we were unable to anticipate. This is part of the process, and the key is to monitor and respond quickly.</p><p><strong>Data migration errors due to low-quality legacy data</strong></p><p>We encountered challenges with low-quality legacy data that needed to be migrated to our new system for accurate take rate calculations. As we progressed, we discovered more and more corner cases, which led to the creation of complex rules to understand the migrated data. The key takeaway here is to simplify as much as possible and test the rules with a diverse group of people who may not have the same level of context as you do. This will help validate the rules being created.</p><p>In conclusion, the journey of migrating Inventa’s product from its initial MVP constructed on Shopify and Zoho customizations to a more scalable and stable platform was a complex yet rewarding process. The team recognized the need for a solid foundation and undertook a meticulous engineering rebranding. The results one-year post-migration were deemed excellent, with a stable product, reduced incidents, and enhanced understanding of the system architecture. The article highlights the significance of collaborative work, addressing corner cases, and the importance of data quality in ensuring a successful and impactful product migration.</p><p>If you want to know more about us, add me on <a href="https://www.linkedin.com/in/pamepeixinho/">LinkedIn</a> or apply to one of our <a href="https://jobs.lever.co/inventa?department=Technology">open positions</a>! Let’s learn together and build something bigger and better than we could do individually!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MtoLGLI-s-vaUpXY.png" /></figure><p><em>Thanks for reading! I’m always open to receiving feedback, recommendations, or questions, feel free to contact me!!</em></p><p><strong><em>Pamela Peixinho, Engineering Lead @ Inventa <br></em>LinkedIn</strong>: <a href="https://www.linkedin.com/in/pamepeixinho">https://www.linkedin.com/in/pamepeixinho</a></p><blockquote><strong>My last name is LittleFish, “sea” you later!</strong> <em>😉</em></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4435293792ea" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/product-migration-moving-from-an-mvp-to-a-scalable-product-base-4435293792ea">Product Migration: Moving from an MVP to a Scalable Product Base</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scaling Pods based on Prometheus Metrics using Keda]]></title>
            <link>https://medium.com/building-inventa/scaling-pods-based-on-prometheus-metrics-using-keda-64686565ed11?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/64686565ed11</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[prometheus]]></category>
            <category><![CDATA[scalability]]></category>
            <category><![CDATA[hpa]]></category>
            <category><![CDATA[keda]]></category>
            <dc:creator><![CDATA[Giulio Soares]]></dc:creator>
            <pubDate>Thu, 19 Oct 2023 18:21:14 GMT</pubDate>
            <atom:updated>2023-10-19T18:21:13.939Z</atom:updated>
            <content:encoded><![CDATA[<p>At Inventa, we utilize Amazon EKS to run a production-level cluster. As a part of our standard deployment process, we deploy at least 2 pods on different nodes to ensure a certain level of availability for the service. We also use Kubernetes requests and limits for both CPU and memory, this helps us guarantee that if a service is overused, only the pod is affected and not the node as a whole.</p><p>We have observed that our applications often consume a lot of memory, even with only 2 pods. As a result, the pods frequently terminate with the “OOMKilled” exit code. This could be due to various reasons since we employ a microservices architecture.</p><p>During our search for a Kubernetes-native solution, we conducted tests using HPA (<a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">Horizontal Pod Autoscaler</a>). However, after learning more about its operation, we realized that it performs better with CPU metrics whereas we needed a solution that focused on memory. Our goal was to find a solution that allowed application developers to choose which metrics would trigger automatic pod count scaling. As a result, we explored Keda and its capabilities.</p><h3>KEDA</h3><figure><img alt="Keda Logo" src="https://cdn-images-1.medium.com/max/800/0*nvesCQMIqBPQMC-4.png" /><figcaption>Keda Logo</figcaption></figure><p>Keda is an abbreviation for Kubernetes Event-Driven Autoscaling. Simply put, it is a tool that enables automatic scaling of Kubernetes applications based on events. According to their <a href="https://keda.sh/">website</a>:</p><blockquote>With <strong>KEDA</strong>, you can drive the scaling of any container in Kubernetes based on the number of events needing to be processed.</blockquote><blockquote><strong>KEDA</strong> is a single-purpose and lightweight component that can be added into any Kubernetes cluster. <strong>KEDA</strong> works alongside standard Kubernetes components like the <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">Horizontal Pod Autoscaler</a> and can extend functionality without overwriting or duplication. With <strong>KEDA</strong> you can explicitly map the apps you want to use event-driven scale, with other apps continuing to function. This makes <strong>KEDA</strong> a flexible and safe option to run alongside any number of any other Kubernetes applications or frameworks.</blockquote><blockquote>KEDA <strong>scalers</strong> can both detect if a deployment should be activated or deactivated, and feed custom metrics for a specific event source.</blockquote><h4>Components</h4><blockquote>KEDA performs two key roles within Kubernetes:</blockquote><blockquote><strong>1. Agent</strong> — KEDA activates and deactivates Kubernetes <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment">Deployments</a> to scale to and from zero on no events. This is one of the primary roles of the <em>keda-operator</em> container that runs when you install KEDA.</blockquote><blockquote><strong>2. Metrics</strong> — KEDA acts as a <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics">Kubernetes metrics server</a> that exposes rich event data like queue length or stream lag to the Horizontal Pod Autoscaler to drive scale out. It is up to the Deployment to consume the events directly from the source. This preserves rich event integration and enables gestures like completing or abandoning queue messages to work out of the box. The metric serving is the primary role of the <em>keda-operator-metrics-apiserver</em> container that runs when you install KEDA.</blockquote><h4>Architecture</h4><blockquote>The diagram below shows how KEDA works in conjunction with the Kubernetes Horizontal Pod Autoscaler, external event sources, and Kubernetes’ <a href="https://etcd.io/">etcd</a> data store:</blockquote><figure><img alt="Keda Architecture" src="https://cdn-images-1.medium.com/max/1024/0*pIv4ijFQvlOUrOgk.png" /><figcaption>Keda Architecture</figcaption></figure><p>‎As you can see in the architectural diagram, KEDA leverages the Kubernetes Horizontal Pod Autoscaling (HPA) engine to scale your deployments. A Keda internal Kubernetes Controller is responsible for examining external events (so-called scalers) and instructing the metrics adapter to feed the HPA with corresponding scaling requests for the individual deployments.‎</p><p>To begin with, we are looking for a tool that can be seamlessly integrated with Prometheus, as we currently use it for other functions. This tool caters perfectly to our requirements, enabling us to leverage the various metrics that applications transmit to Prometheus. It provides engineers with the flexibility to choose the best possible trigger for their applications.</p><h3>Configuring and Installing Keda</h3><p>For easy installation and maintenance, we chose to install <strong>Keda</strong> through Helm 3. See the commands below:</p><pre>helm repo add kedacore https://kedacore.github.io/charts<br>helm repo update<br>helm install keda kedacore/keda --namespace keda --create-namespace</pre><p>This way, the components were installed within the <strong>Keda</strong> namespace:</p><figure><img alt="Keda components inside Kubernetes Cluster" src="https://cdn-images-1.medium.com/max/1024/1*az59xvjm1FytQssr8SlPTA.png" /><figcaption>Keda components inside the Kubernetes Cluster</figcaption></figure><p>The following parameters are used to integrate Keda and Prometheus:</p><ul><li>serverAddress - Address of Prometheus server.</li><li>metricName - Name of the metric.</li><li>threshold - Value to start scaling for. (This value can be a float).</li><li>query - Query to run.</li></ul><p>And this is the result of the YAML file:</p><pre>apiVersion: keda.sh/v1alpha1<br>kind: ScaledObject<br>metadata:<br>  name: kotlin-spring-template-deployment-memory<br>  namespace: engineering-foundation<br>spec:<br>  scaleTargetRef:<br>    name: kotlin-deployment<br>  pollingInterval: 30 # Must be seconds<br>  minReplicaCount: 2<br>  maxReplicaCount: 3<br>  triggers:<br>  - type: prometheus<br>    metadata:<br>      serverAddress: http://prometheus-server-prometheus.prometheus.svc.cluster.local:9090<br>      metricName: pod_memory_percentage_usage_kotlin<br>      threshold: &#39;80&#39;<br>      query: sum(round(max by (pod) (max_over_time(container_memory_usage_bytes{namespace=&quot;engineering-foundation&quot;,pod=~&quot;kotlin-spring-template-deployment.*&quot;,container=&quot;kotlin-spring-template&quot;}[1m])) / on (pod)(max by (pod) (kube_pod_container_resource_limits{namespace=&quot;engineering-foundation&quot;,pod=~&quot;kotlin-spring-template-deployment.*&quot;,container=&quot;kotlin-spring-template&quot;,resource=&quot;memory&quot;})) * 100,0.01))</pre><p>This query is designed to find the average memory usage of the pods. When the usage reaches 80%, a new pod is created. Similarly, when the usage drops to 40%, a pod is deleted. It is worth noting that to determine the value required to reduce the number of cans, the current usage percentage should be divided by the threshold value until the result is 0.5. This was a particularly interesting piece of information that took us some time to fully understand.</p><p>When viewed from within the cluster, it will look like this:</p><figure><img alt="ScaledObject vision inside Kubernetes Cluster" src="https://cdn-images-1.medium.com/max/1024/1*N02HbDbPyEAZFA0tcyLROg.png" /><figcaption>ScaledObject vision inside the Kubernetes Cluster</figcaption></figure><p>In this case, the example given is used as a standard in the absence of a defined metric by the engineer responsible for the application. However, it is important to note that the engineer still has the freedom to change the metric if necessary. There are a variety of metrics available in Prometheus, such as the number of requests HTTP or the resources used by the JVM, that can be used as triggers.</p><h3>Conclusion</h3><p>It’s easy to set up KEDA on an EKS cluster, using Keda autoscaler provides an efficient and flexible solution for automatically scaling workloads in containerized environments, improving the efficiency and scalability of applications in Kubernetes. With its ability to adjust resources based on real-time demand, Keda helps optimize performance and save resources, making it a valuable tool for DevOps teams and developers.</p><h4>References</h4><ul><li><a href="https://keda.sh/">https://keda.sh/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64686565ed11" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/scaling-pods-based-on-prometheus-metrics-using-keda-64686565ed11">Scaling Pods based on Prometheus Metrics using Keda</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unlocking Data Team Potential: The Advanced dbt Journey]]></title>
            <link>https://medium.com/building-inventa/unlocking-data-team-potential-the-advanced-dbt-journey-69fc354b0b8b?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/69fc354b0b8b</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[dbt]]></category>
            <category><![CDATA[analytics-engineering]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[Weber Matias]]></dc:creator>
            <pubDate>Tue, 08 Aug 2023 16:17:46 GMT</pubDate>
            <atom:updated>2023-08-08T16:17:45.902Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How our evolution as a Data Team helped create the CoRise Advanced dbt course</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VovrGtwqe8kp576u02VDRA.jpeg" /></figure><p>Scaling your dbt project is no different than scaling your data team. They share a lot of similarities. Scaling effectively is a challenge that every team must face. As it matures, the best practices need to mature too.</p><p>We started using dbt at Inventa in May 2022. As our project quickly grew, so did the need for a common ground of best practices. Initially, we selected our linter, set up CI, and embraced code reviews. As the project continued to expand, we faced new challenges. Running the full pipeline became time-consuming and the local development workflow became resource-intensive. Ensuring documentation consistency was non-trivial. The DAG grew to a point that we needed to assign ownership over different portions to different people. We could not afford the whole team looking at the entire DAG and this made focusing impossible. We realized we needed an on-call rotation to decentralize this responsibility.</p><p>By this point we had around 1,400 models and over 3,000 tests with 14 contributors.</p><p>This is when I had the opportunity to join the Advanced dbt Team at CoRise to build this course. Motivated by what we had learned during the last year, I decided to join the team. My goal was to organize our knowledge in such a way that could be useful for other data teams. The dbt community is growing at such a pace, that many teams are now struggling with the same things that we did at our early stage.</p><p>I was excited to join Lindsay Murphy (Head of Data at Secoda) and Steve Haraguchi (Learning Design &amp; Operations at CoRise). As a development partner, my role was to contribute to what Lindsay was building (kudos to Lindsay! 🥳). Not an easy task. But I was confident that the experience of our Data Team could provide a valuable case study for the course material.</p><p>The first line of the course outline was: “Learn: Best practices for scaling dbt projects, workflows, and documentation”. I would recommend any dbt practitioner to enroll based on this statement alone! But the similarities kept coming in Weeks 2, 3 and 4.</p><p>Soon we realized that our teams operate in very similar ways. We seek out feedback and improve upon it. These iterations result in many lessons and insightful projects.</p><p>The first step that we took at Inventa with our modern data stack was to embrace best practices. We defined our coding conventions, enforced them within the project and doubled down on documentation. This is what Week 1 of the course is all about: how do we enforce best practices within the project? How do you scale documentation? How do you enforce coding conventions?</p><p>Week 2 of Advanced dbt brings more lessons we learned the hard way at Inventa. As our codebase grew, we were applying the DRY (Don’t repeat yourself) principle through the use of Jinja and macros. We looked for existing packages before writing custom code. For example, <a href="https://hub.getdbt.com/calogica/dbt_expectations/latest/">dbt_expectations</a> helped us increase test coverage quickly with minimal lines of code.</p><p>Managing data quality at scale was our next challenge. With expectations of continued growth, we wrote tests often and early. But our test suite started to become too expensive and slow. We needed to optimize. We also complemented our testing stack with data quality tools like <a href="https://www.datafold.com/">Datafold</a>. These challenges are now the focus of Week 3.</p><p>Testing was not the only part of the project that needed optimization as we matured. Our runs started taking longer, slowing down the team. To move fast in the beginning, our models were materialized as tables or views. But many were taking too long and our Snowflake bill kept climbing. We decided it was time to standardize our use of incremental models. At the same time, we optimized our Slim CI (learn more about how we did this in<a href="https://medium.com/building-inventa/how-we-slimmed-down-slim-ci-for-dbt-cloud-6a944e7574e2"> How we slimmed down Slim CI for dbt Cloud</a>). In Week 4 Lindsay gets into the weeds about the importance of these practices and more!</p><p>Advanced dbt was born out of the desire of creating a game-changer course to scale data teams effectively. Leveraging our experiences at Inventa and collaborating with Lindsay and CoRise, we crafted a course that is more than dbt. It offers insights into building and scaling advanced data teams and best practices. It was so much fun to work on with Lindsay and Steve. We hope you enjoy learning from the course half as much as we enjoyed making it!</p><p><em>Link to the course:</em> <a href="https://corise.com/course/advanced-dbt">Advanced dbt Course at CoRise</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69fc354b0b8b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/unlocking-data-team-potential-the-advanced-dbt-journey-69fc354b0b8b">Unlocking Data Team Potential: The Advanced dbt Journey</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Managing Technical Quality To Ensure a Five-Star Product]]></title>
            <link>https://medium.com/building-inventa/managing-technical-quality-to-ensure-a-five-star-product-cf07f98d9a02?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/cf07f98d9a02</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[quality-software]]></category>
            <category><![CDATA[teamwork]]></category>
            <category><![CDATA[collaboration]]></category>
            <dc:creator><![CDATA[Miguel Pessoa]]></dc:creator>
            <pubDate>Wed, 05 Jul 2023 17:42:57 GMT</pubDate>
            <atom:updated>2023-07-05T17:42:57.275Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KVtvbPFMUsll1tVI" /><figcaption>Photo by <a href="https://unsplash.com/pt-br/@dawson2406?utm_source=medium&amp;utm_medium=referral">Stephen Dawson</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>In the current landscape, it is crucial to prioritize the delivery of exceptional products and services to our customers. To do so, we must learn quickly from our users and do our job as an engineering team. The quality responsibility is, in most cases, being addressed by some people from the team, the leader, some more senior engineers, or those with more time at the company, but, this could result in a lack of knowledge sharing and opportunities for people to grow. So, by focusing our energy on the core business and collaborating as a team, we can achieve the finest craft of engineering.</p><p>In order to achieve that, rotating team members through different roles and responsibilities (inside the engineering universe) ensure that every aspect of a project is tested and examined.</p><p>According to the book, <a href="https://staffeng.com/book">Staff Engineer: Leadership beyond the management track</a>, is the responsibility of the leadership/engineering team to advocate for a high-quality product. In addition, it brings some steps over in how we can manage technical quality:</p><ol><li>fix the <strong>hot spots</strong> that are causing immediate problems</li><li>adopt <strong>best practices</strong> that are known to improve quality</li><li>prioritize <strong>leverage points</strong> that preserve quality as your software changes</li><li>align <strong>technical vectors</strong> in how your organization changes software</li><li><strong>measure technical quality</strong> to guide deeper investment</li><li>spin up a <strong>technical quality team</strong> to create systems and tools for quality</li><li>run a <strong>quality program</strong> to measure, track and create accountability</li></ol><p>It is evident that having a clear understanding of the issues pertaining to domains/organizations is imperative to establish a process that fosters technical quality outcomes. The diagram presented below outlines a proposed procedure for managing technical quality, starting with an engineering quality guardian assuming responsibility for the process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MtAxFlRlnD6pZihlqb7QXA.png" /><figcaption>High-quality duties for an engineering-quality guardian</figcaption></figure><p>To support it, the main pillars are bug reports, code quality, operability, tech debts, and developer experience. Below we can understand each one of them and the complete process overview.</p><h3>High-Quality Engineering Pillars</h3><h3>Bug Reports</h3><p>The first pillar is bug reports. The team’s priority is to listen and learn from users (stakeholders, final clients). At Inventa, we have some dashboards that the engineering team could access in order to see the major problems. This helps to ensure that every team member is familiar with the product and its functionalities, making it easier to identify and fix any issues that arise.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5CAtvZB0xO_0ocB_rig2DA.png" /></figure><p>Bug checking can be time-consuming and require a lot of effort, but it is a crucial step to ensure that the product works as intended. So, a good alignment with the product team/stakeholders is important to understand the criticality of the report to address the root cause. However, be aware that some quick fixes could help the team to move forward.</p><h3>Code Quality</h3><p>To improve our code readability and maintainability, we should use some tools in order to help us with this process. Sonarqube is a tool used to detect technical debts in code. It helps identify issues that could potentially cause problems down the line, such as code duplication, security vulnerabilities, and coding standards violations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GL0mhkiFmmnJgDvLNKaYSw.png" /><figcaption>Detailed view of the sonar cloud usage, where we can see and address code-related issues.</figcaption></figure><p>With the help of coding standards like <a href="https://github.com/pinterest/ktlint">Klint</a>, <a href="https://github.com/detekt/detekt">DeteKt</a>, and the use of Sonarqube we can deliver clean code, but, this tool is only useful if we keep track of issues and keep enforcing patterns along the way. Considering this a pillar of software quality, engineering should focus on checking it regularly for projects with ownership. Not only code quality tools + CI/CD pipelines could help in this process.</p><p>The image below is an example static code analysis pipeline here at Inventa, using Github actions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*usa_rqu14STYRtN8FZflJw.png" /><figcaption>Build Pipeline Using Github Actions</figcaption></figure><p>So, to keep your code with a clear architecture and code writing those tools helps. Rotating leadership to check these tools helps to deliver a better product.</p><h3>Operability</h3><p>Operability in software engineering refers to the ease and effectiveness with which software can be operated and maintained, in a simple way it means to look at how things are running. The dashboard below contains some metrics related to a Spring Boot application at Inventa.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rC3CaNZnMg6XYEeAQ6m4Lw.png" /><figcaption>Grafana Dashboard where we can see the machine spring boot data</figcaption></figure><p>The <a href="https://sre.google/sre-book/monitoring-distributed-systems/">Google SRE book</a> introduces a concise set of key performance indicators known as golden signals. These signals, including latency, traffic, errors, and saturation, provide valuable insights into the health and performance of your application; if your team is looking where to start the reference link can guide you there. Keeping track of those numbers aims to deliver a better user experience, retention, and cost implications. To sustain long-term success your team/company should involve this as a pillar of quality.</p><p>Other important mechanisms to smooth the operation and success of a business are service level objectives, error tracking, and incident management.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8H_ross45UazYOMbPTKhRg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Uj4XbqP5eCjgzgAd37eOQ.png" /><figcaption>Grafana dashboard to view the price-engine app SLO / Error Tracking With Sentry</figcaption></figure><p>SLO defines the level of reliability and performance that a business aims to provide to its customers. Establishing targets and meeting them builds customer trust. So, having a close look at those numbers is extremely relevant to build better products.</p><p>Curious why we have adopted the following tools in our operational engineering? Check the <a href="https://medium.com/building-inventa/defining-inventas-observability-strategy-4ab0988f3c96">following</a> article written by Marcos Gomes.</p><h3>Tech Debts</h3><p>According to Philippe Kruchten, in <strong>Managing Technical Debt: Reducing Friction in Software Development,</strong> a simple analogy of technical debt could be related to mechanical devices:</p><blockquote><em>the more friction a device experiences due to wear and tear, lack of lubrication, or bad design, the harder it is to move the device, and the more energy you have to apply to get the original effect.</em></blockquote><p>Bring this to a software engineering world, Technical debts can result in the accumulation of outdated software, inefficient coding, and outdated development practices and as mechanical devices, if we don’t manage it things <em>tends</em> to fail. A simple way to keep track of it inside your organization is to write issues inside the repository project. Since those debts are technically related, is very important that the engineering team give visibility to stakeholders of the immediate impact and backlog created.</p><h3>Developer Experience</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P7XUl2jFozJkgvjQ4E1VtA.png" /><figcaption>Catalog view of Backstage Entity, showing team ownership and dependencies graph</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OJPYQDDXSgh_KQwpdC5LXg.png" /><figcaption>Swagger integration side Backstage API entity page</figcaption></figure><p>Investing in a positive developer experience has many benefits for organizations. It improves productivity, helps attract and keep talented developers, encourages teamwork, makes onboarding easier, and boosts developer satisfaction and morale. When organizations prioritize the well-being and success of developers, they create an environment that promotes innovation, speeds up software development, and leads to long-term success.</p><p>At Inventa, we have adopted <a href="https://backstage.io/docs/overview/what-is-backstage">Backstage</a> as our tool to address developer experience. Currently, we are using the service catalog, API Docs, and Tech Docs features that come from the main backstage functionalities. To get this process running, we should not limit this work inside platform teams, all the engineers should participate in searching for solutions to get our work more effective and funny. So, during the prioritization process of selecting a work to be done, we must consider DevEx prioritization. To start simply, the team can think of solutions related to this[<a href="https://microsoft.github.io/code-with-engineering-playbook/developer-experience/">source</a>]:</p><ul><li>Create — Improve project setup and infrastructure definition</li><li>Build — Verify that changes are free of syntax errors and compile.</li><li>Test — Verify that all automated tests pass.</li><li>Start — Launch end-to-end to simulate execution in a deployed environment.</li><li>Debug — Attach debugger to start solution, set breakpoints, step through code, and inspect variables.</li></ul><p>Which implies:</p><blockquote><em>If effort is invested to make these activities as easy as possible, </em><strong><em>the returns on that effort will increase the longer the project runs, and the larger the team is</em></strong><em>. [</em><a href="https://microsoft.github.io/code-with-engineering-playbook/developer-experience/"><em>source</em></a><em>]</em></blockquote><p>So, thinking about the long-term success of high-performing teams the developer experience is a must, considering it on a daily basis certainly changes the business impact.</p><h3>Prioritization is the key</h3><p>So far, we have talked about some pillars when searching for technical quality. If your team decides to dive deep into each topic, the team duties and backlog will <em>multiply ad infinitum</em> 🤯. Therefore, the art of prioritization comes in handy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kIlHAKQQfwf9VCTXhmr_ig.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zj2jsIengeCy3h9qWCyFqA.png" /><figcaption>The Impact/Effort Matrix [<a href="https://itamargilad.com/why-impact-effort-prioritization-doesnt-work/">source</a>]</figcaption></figure><p>To effectively prioritize product issues, several key steps should be followed. Firstly, it is essential to define and align product goals, establishing a framework for evaluating issues that align with the product’s strategic direction. Next, assess the impact and urgency of each issue, considering factors such as customer satisfaction, revenue impact, and technical feasibility. Collecting stakeholder input from different company areas ensures a broader understanding of challenges and effective prioritization. Also, it is crucial to estimate the effort and resources required to address each issue, considering development time, dependencies, and available capacity. Lastly, prioritize is an iterative process, requiring continuous re-evaluation and adaptation based on changing circumstances, user feedback, and new insights gained during development. Being able to adjust and adapt helps you focus on important problems and create successful products.</p><p>A simple overview of each step over prioritization:</p><ol><li>Define and Align on Product Goals</li><li>Assess Impact and Urgency, and find a useful framework 🙂</li><li>Gather Stakeholder Input</li><li>Estimate Effort and Resources</li><li>Continuously Re-evaluate and Adapt</li></ol><h3>Getting the Job Done and Assessing Impact</h3><p>Throughout our discussion, we have explored the advantages of effectively managing team quality and identified key pillars that contribute to the development of a high-performing product. It is essential to emphasize that these pillars should be tailored to meet the specific needs of your team and company. Each organization will find inspiration in these pillars as they adapt them to their unique context.</p><p>Once your backlog is populated, the next crucial step is prioritization. Given the fast-paced nature of our work in product development, there may be instances where our focus is solely on addressing bug reports or resolving technical debts. This is a normal occurrence. However, it is imperative to remain prepared for the moment when the opportunity arises to enhance the architecture, refine the code style, and improve the overall developer experience.</p><p>By continually evolving these aspects, we can ensure that our products are built on a solid foundation and foster an environment conducive to growth and innovation.</p><p>As part of the process, it is important to assess the impact of measuring the results, some metrics are:</p><p><strong>Bug Resolution/Tech Debts</strong></p><ul><li>The number of bugs reported; Bug resolution rate; Mean time to resolve (MTTR)</li></ul><p><strong>Code Quality</strong></p><ul><li>Code coverage; Code complexity, use Sonarqube for it</li><li>Architecture: <a href="https://medium.com/quintoandar-tech-blog/software-architecture-analysis-25bd0274d837">https://medium.com/quintoandar-tech-blog/software-architecture-analysis-25bd0274d837</a></li><li>Explore more metrics in [<a href="https://dzone.com/articles/code-quality-metrics">source</a>]</li></ul><p><strong>Developer Experience</strong></p><ul><li>Read Measuring an Engineering Organization [<a href="https://lethain.com/measuring-engineering-organizations/">link</a>]</li></ul><p><strong>Operability</strong></p><ul><li>To start simple: latency, traffic, errors, and saturation</li><li>Latter: Mean time to detect (MTTD) and mean time to resolve (MTTR), Availability, and uptime</li><li>Advanced: SLOs</li></ul><h3>Finale</h3><p>To achieve a five-star product, as a team we must be extremely connected and communicate in a high standard way. From the perspective of engineering, it is important to give visibility to issues and solutions that may not work long-term /or are scalable. With this visibility, the product, designer, data, and engineer can come together to decide the next steps. Listing, acting, and measuring results with consistency will lead to great results. A diligent team achieves more.</p><p>If you want to know more about us, add me on <a href="https://www.linkedin.com/in/miguelspe/">LinkedIn</a> or apply to one of our <a href="https://jobs.lever.co/inventa?department=Technology">open positions</a>! Let’s learn together and build something bigger and better than we could do individually!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9z4ao_cKWgUomiBE.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf07f98d9a02" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/managing-technical-quality-to-ensure-a-five-star-product-cf07f98d9a02">Managing Technical Quality To Ensure a Five-Star Product</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Defining an Effective Infrastructure as Code (IaC) Strategy: Building for Scalability and…]]></title>
            <link>https://medium.com/building-inventa/defining-an-effective-infrastructure-as-code-iac-strategy-building-for-scalability-and-3d3bad8beeee?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/3d3bad8beeee</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[infrastructure-as-code]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[infrastructure-management]]></category>
            <category><![CDATA[site-reliability-engineer]]></category>
            <dc:creator><![CDATA[Carlos Machado]]></dc:creator>
            <pubDate>Tue, 27 Jun 2023 14:02:18 GMT</pubDate>
            <atom:updated>2023-06-27T14:02:18.272Z</atom:updated>
            <content:encoded><![CDATA[<h3><em>Defining an Effective Infrastructure as Code (IaC) Strategy: Building for Scalability and Efficiency</em></h3><p><em>Unleashing the power of code. A step-by-step strategy for building a scalable and efficient infrastructure with IaC.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dBDyARC9eqt8Hhzx3M6_WQ.png" /></figure><p>Building a robust infrastructure is a crucial aspect of any company&#39;s success. As the business grows and evolves, the need for a scalable and resilient infrastructure that can adapt to changing requirements becomes inevitable. However, manually managing infrastructure can be time-consuming, error-prone, and inefficient. That’s where Infrastructure as Code (IaC) comes in. IaC allows you to define the infrastructure using code, making it more manageable, repeatable, and scalable.</p><p>In this article, we’ll explore how we defined an IaC strategy that enables us to build and manage infrastructure efficiently.</p><h4>IaC</h4><p>Infrastructure as Code is a method of defining and managing infrastructure using code. By treating infrastructure as code, IaC allows for greater efficiency, scalability, and repeatability than traditional manual infrastructure management methods. With IaC, infrastructure resources such as servers, storage, and networks are defined and deployed using code that can be versioned, tested, and automated. This approach provides several benefits, including the ability to quickly spin up and scale infrastructure resources, reduce human error, and increase the speed of infrastructure deployment. In the next section, we will explore the basics of IaC and how it can help engineers efficiently manage their infrastructure.</p><h4><strong>Choosing the Right IaC</strong></h4><p>Choosing the right IaC tool is a decision that can greatly impact the effectiveness and efficiency of your infrastructure management. Several popular IaC tools and frameworks are available, each with its strengths, capabilities, and limitations. When selecting an IaC, we consider the following factors:</p><ol><li>Evaluate the tool’s capabilities and ensure it supports the infrastructure providers we use or plan to use, such as AWS, Azure, or Google Cloud.</li><li>Consider the learning curve associated with it. Evaluate its documentation, community support, and available learning resources such as tutorials or courses.</li><li>Determine if the tool allows us to customize and extend its functionality to meet your requirements. Look for features such as support for custom plugins or the ability to define your resource types.</li><li>Assess the size and activity level of the tool’s community. A vibrant community can provide valuable resources, plugins, and community-contributed modules, making it easier to leverage best practices and benefit from shared knowledge.</li><li>How the IaC tool integrates with our existing development and deployment workflows. Look for features like integration with popular CI/CD pipelines, source control systems, or infrastructure monitoring tools.</li><li>Evaluate the tool’s roadmap, release cycle, and vendor support. Choose a tool that is actively maintained, regularly updated, and has a solid user base to ensure ongoing support and updates.</li></ol><p>Evaluate different IaC tools and frameworks to find the best fit for their unique requirements. Terraform, with its extensive provider ecosystem, offers a versatile and cloud-agnostic solution. Ansible, on the other hand, excels in configuration management and automation. CloudFormation provides native integration with AWS services, making it an excellent choice for organizations heavily reliant on AWS.</p><h4>Embracing Terraform for Streamlined Infrastructure Management</h4><p>With its extensive feature set and widespread adoption, Terraform offers different benefits that align with the needs of seeking efficient infrastructure management and that is why it was chosen by us <em>as our IaC provider</em>. Let’s delve into some of the advantages of choosing Terraform as your IaC tool:</p><ul><li>It supports multiple cloud providers, and integrates with different cloud services and platforms, allowing organizations to avoid vendor lock-in, simplify the management of complex infrastructures, and adopt a multi-cloud or hybrid cloud strategy.</li><li>Enables defining infrastructure state declaratively, automating provisioning and management, and ensuring scalability and repeatability.</li><li>Promotes a modular approach, creating reusable building blocks for accelerated development and consistent environments.</li><li>A thriving community, contributed modules, best practices, and documentation make Terraform a reliable and well-supported choice for infrastructure management.</li></ul><h4>Designing Your Infrastructure as Code</h4><p>Designing an effective IaC solution involves careful planning. We started by defining infrastructure components and their interdependencies — breaking down infrastructure into reusable modules fosters code reusability and maintainability. We employed best practices, such as utilizing variables for flexibility, keeping code modular, and implementing testing and validation to ensure the reliability of the infrastructure.</p><p>Here are some reusable modules that were implemented and used by all the engineering teams:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/446/1*BN6LtA5rPoppwNy0Jnop3Q.png" /></figure><h4>Implementing the IaC Solution</h4><p>Once the design is in place, it’s time to implement the IaC solution. This involves deploying code, managing configuration, and monitoring infrastructure. Engineers can reduce manual effort, minimize human errors, and maintain consistency across environments by automating these processes. Additionally, adopting practices like change management ensures smooth updates and enhancements to the infrastructure codebase.</p><p>When developing Terraform modules, it is essential to follow best practices to ensure reusability, ease of use, and maintainability. Here are some insights that we use to implement a Terraform module that adheres to these principles:</p><ul><li>Start by organizing our Terraform module into a logical directory structure, making it easier to navigate and understand.</li><li>Split the modules into smaller, reusable components to promote modularity.</li><li>Leverage input and output variables to allow users to customize the module’s behavior.</li><li>Provide comprehensive and clear documentation for your module explaining the module’s purpose and usage examples.</li><li>Implement variable validation to ensure inputs conform to expected formats.</li><li>Foster a community around your Terraform module by encouraging user feedback, contributions, and issue reporting.</li><li>Actively address user concerns, feature requests, and bug reports to improve the module’s quality and user experience.</li></ul><h4>Managing Our IaC Infrastructure</h4><p>To effectively manage infrastructure as IaC, we consolidated all Terraform projects into a mono repository. This speeds up code management, simplifies version control, promotes collaboration, and enables consistent CI/CD pipelines with GitHub Actions. Treating infrastructure code like a software project, the mono repository facilitates efficient tracking, branch management, and pull request handling, also boosts collaboration, automates testing and deployments, and encourages code reuse. This approach ensures stable and more reliable infrastructure management.</p><p>Let’s go a little deep to understand our pipeline workflow:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7YYjOJGWeNur27mL86mukw.png" /></figure><p>The above workflow runs using a Terraform project with files structure like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/554/1*oYwOl4IaAc9Kn45boK8YQQ.png" /></figure><p>Imagine we have the “Product Catalog” project, where we manage all the necessary cloud resources for this application. Let’s take a look at the files you’ll find in this project:</p><pre>&quot;product-catalog&quot; (* Project main path *)<br>├─ &quot;catalog-info.yaml&quot; (* Backstage configuration file *)<br>├─ &quot;ecr.tf&quot; (* Elastic Container Register definitions *)<br>├─ &quot;env&quot;<br>│   ├─ &quot;backends&quot;<br>│   │   ├─ &quot;dev.tfvars&quot; (* Terraform backend file definitions *)<br>│   │   └─ &quot;prod.tfvars&quot; (* Terraform backend file definitions *)<br>│   ├─ &quot;dev.tfvars&quot; (* Terraform variables with values for development environment *)<br>│   └─ &quot;prod.tfvars&quot; (* Terraform variables with values for production environment *)<br>├─ &quot;locals.tf&quot; (* Terraform file to create expressions, so you can use the parameters multiple times within a module instead of repeating the expression. *)<br>├─ &quot;route53.tf&quot; (* Route53 definitions *)<br>├─ &quot;secrets.tf&quot; (* Secrets Manager definitions *)<br>├─ &quot;sqs.tf&quot; (* SQS Queue definitions *)<br>└─ &quot;vars.tf&quot; (* Terraform file to manage all variables *</pre><p>The ECR, Route 53, Secrets Manager and SQS definition files are using our <strong>Terraform Modules</strong> repository as a source.</p><p>Building a solid infrastructure is vital for engineering success, and Infrastructure as Code offers a powerful solution. Organizations can establish a strong foundation by understanding the concept, selecting the right tools, designing a scalable architecture, implementing best practices, and adopting robust management strategies. Embracing Infrastructure as Code empowers to iterate quickly, scale effortlessly, and focus on what matters most: innovation and growth in an ever-evolving business landscape.</p><p>If you’re seeking a dynamic and cutting-edge environment, we have some exciting opportunities for you. At Inventa, we pride ourselves on leveraging state-of-the-art platforms and pushing the boundaries of technology development. We’re on a mission to build a remarkably reliable, highly observable, and scalable product that will make waves in the industry.</p><p>Ready to embark on a thrilling journey with me? Take the first step by applying simply click <a href="https://www.linkedin.com/jobs/search/?currentJobId=3437853272&amp;f_C=71737018&amp;geoId=92000000&amp;originToLandingJobPostings=3437853267%2C3437852421%2C3437853272%2C3437846946%2C3437853264%2C3437847949%2C3457031342%2C3437850571%2C3437852426">here</a> to explore our open positions. If you have any questions or want to learn more about our team, don’t hesitate to reach out to us on <a href="https://www.linkedin.com/in/marcosangomes/">LinkedIn</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*VD0ntHxAogUIJybl58cXeQ.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3d3bad8beeee" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/defining-an-effective-infrastructure-as-code-iac-strategy-building-for-scalability-and-3d3bad8beeee">Defining an Effective Infrastructure as Code (IaC) Strategy: Building for Scalability and…</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Supercharge your code with Kotlin Observable: Watch your properties recompute in real time!]]></title>
            <link>https://medium.com/building-inventa/supercharge-your-code-with-kotlin-observable-watch-your-properties-recompute-in-real-time-f593074db3fc?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/f593074db3fc</guid>
            <category><![CDATA[hibernate-jpa]]></category>
            <category><![CDATA[kotlin-beginners]]></category>
            <category><![CDATA[backend-development]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[kotlin-observable]]></category>
            <dc:creator><![CDATA[Bruno Donizeti da Silva]]></dc:creator>
            <pubDate>Tue, 13 Jun 2023 14:01:36 GMT</pubDate>
            <atom:updated>2023-06-13T22:13:08.778Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eZ0XRLZhQIKGs63qnRy4Qw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@logz05?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Nick Loggie</a> at <a href="https://unsplash.com/pt-br/fotografias/fRVPzBYcd5A?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Kotlin is a versatile programming language that is widely used in developing mobile and web applications. At Inventa, we’re using Kotlin extensively and having really nice results in terms of maintainability, reliability, scalability, and also in developer experience. One of the most prominent features of Kotlin is <em>observable</em>, which enables developers to create reactive and event-driven programs. In this article, we will explore what Kotlin’s observable feature is and how it can be used in practical applications.</p><p>Here on the order-management squad at Inventa, we are responsible for managing the lifecycle of an order from its payment at checkout until it is delivered to the retailer. During the order process, there are various steps that can result in changes to the order amounts. Some scenarios include:</p><ul><li>Insufficient product quantity: If the supplier does not have the purchased quantity of a product in stock, they can communicate with the retailer and reduce the quantity accordingly. This reduction in quantity will lead to a decrease in the order&#39;s total value.</li><li>Recalculating service fees: When the order’s total changes, it may be necessary to recalculate the service fee for the order. The service fee calculation can be adjusted based on the updated order&#39;s total value.</li><li>Adjusting vendor&#39;s receivable amounts: In addition to service fees, changes in the order total may also impact the vendor’s receivable amount. If there are modifications to the service fee, the vendor’s receivable amount should be recalculated accordingly.</li></ul><p>This is just one of the scenarios where order amounts ​​can change and cascade changes to other fields.</p><p>Initially, we implemented the logic for recalculating the values ​​in Hibernate’s listeners <em>@PrePersist</em> and <em>@PreUpdate</em>, with this we were able to guarantee that regardless of the use case that changed some value of the order, the persisted data would always have the correct value. The example below shows how it was implemented:</p><pre>@PreUpdate<br>@PrePersist<br>fun updateReceivableAmount() {<br>   this.receivableAmount = this.subtotal<br>       .plus(this.taxes)<br>       .plus(this.freight)<br>       .minus(this.serviceFee)<br>       .minus(this.discounts)<br>}</pre><p>The orders table was getting too big with too many fields, so we split that table and created a new one with the shipping-related information. Among these moved fields, there is a field containing the value of the freight, so every time this field changes, we need to recalculate the vendor’s receivable amount. As they are now different entities, even if we change the shipping value using the order entity as a base, hibernate’s @PreUpdate will not be triggered, since the change was not the Order entity, but the Shipping entity. This solution presented above no longer works for us.</p><h4>How to solve the problem</h4><p>Looking for alternatives on how to figure this thing out, we found Kotlin’s <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html">Observable feature</a>.</p><p>Observable properties are variables that can be monitored for any changes in their value. When a change is detected, the property notifies any listeners that have subscribed to it. This allows other parts of the application to react to changes in the property’s value.</p><p>To create an observable property in Kotlin, we can use the ‘observable’ function provided by the Kotlin standard library. Here’s an example:</p><pre>import kotlin.properties.Delegates<br><br>class KotlinObservableExample {<br>    var total: BigDecimal by Delegates.observable(BigDecimal.ZERO) { <br>        prop, old, new -&gt; <br>        println(&quot;$old -&gt; $new&quot;)<br>    }<br>}</pre><p>In this example, we create a <em>KotlinObservableExample</em> object with an observable property called <em>total</em>. The property is initialized with the value <em>BigDecimal.ZERO</em>. We use the ‘observable’ function to specify a lambda expression that will be called whenever the property’s value changes. The lambda expression takes three arguments: the property being changed, the old value, and the new value.</p><p>Applying this to our use case, we have something like this:</p><pre>class Order(<br>  val id: Long,<br>  val uuid: UUID<br>  ... // other propeties<br>) {<br>  var subtotal: BigDecimal by Delegates.observable(BigDecimal.ZERO, computeTotals())<br>  var serviceFee: BigDecimal by Delegates.observable(BigDecimal.ZERO, computeTotals())<br>  var discounts: BigDecimal by Delegates.observable(BigDecimal.ZERO, computeTotals())<br>  var taxes: BigDecimal by Delegates.observable(BigDecimal.ZERO, computeTotals())<br>  var shipping: Shipping? by Delegates.observable(null, computeTotals())<br>  <br>  private fun computeTotals(): (<br>     property: KProperty&lt;*&gt;,<br>     oldValue: Any?,<br>     newValue: Any?<br>  ) -&gt; Unit = { _, _, _ -&gt;<br>     this.computeServiceFee()<br>     this.computeTotalPrice()<br>     this.computeReceivableAmount()<br>  }<br>}</pre><p>In the property declaration, instead of just declaring the type, we declare a delegate of the observable type, informing the default value of the property and the callback method to be triggered when the value changes. In our use case, for any value that changes we want to recalculate the service fee, the total price, and the receivable amount. We use the same callback for all properties, but you can create many different callbacks depending on your use case.</p><p>The callback method has three parameters with the following information:</p><p>1. The first parameter contains information about the changed property itself. It provides details such as whether the property is final or open, its type, and other relevant information that you can check <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property/">here</a>.</p><p>2. The second parameter holds the value of the property before the change occurs. It allows you to access the previous value and perform any necessary comparisons or operations.</p><p>3. The third parameter represents the value of the property after the change. It provides the updated value, allowing you to track the modification that took place.</p><p>With this solution, in addition to being able to solve the problem we have:</p><ul><li><strong>Loose coupling:</strong> we removed the dependency of the database layer for updating the properties value.</li><li><strong>Testability:</strong> we can test this logic with unit tests. When using Hibernate&#39;s listener it was necessary to create integration tests, loading the application to insert and update an entity to verify if the business logic was right.</li><li><strong>Maintainability:</strong> we don’t need someone to know how Hibernate’s listeners work to understand that value will only be updated when the entity is saved. We also can change our entity without having side effects on the business logic, and if it does, the unit tests will fail.</li></ul><p>I hope to have helped you to get to know a little about this Kotlin feature and how can it be useful in a real-world problem. If you’ve used this feature in any other way that you find interesting to share, add your comment below to enrich the article. Thanks for reading.</p><h4>If you want to know more about us, add me on <a href="https://www.linkedin.com/in/bdonizetisilva">LinkedIn</a> or apply to one of our <a href="https://jobs.lever.co/inventa/">open positions</a>! Let’s learn together!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h8T4PjiKIhJYjceV7Q4HOg.png" /><figcaption>Open Positions: <a href="https://www.linkedin.com/company/inventashop/jobs/">LinkedIn Jobs</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f593074db3fc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/supercharge-your-code-with-kotlin-observable-watch-your-properties-recompute-in-real-time-f593074db3fc">Supercharge your code with Kotlin Observable: Watch your properties recompute in real time!</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Defining Inventa's Observability Strategy]]></title>
            <link>https://medium.com/building-inventa/defining-inventas-observability-strategy-4ab0988f3c96?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/4ab0988f3c96</guid>
            <dc:creator><![CDATA[Marcos Gomes]]></dc:creator>
            <pubDate>Thu, 25 May 2023 14:21:14 GMT</pubDate>
            <atom:updated>2023-05-25T14:21:14.564Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eH7bD3aeSm-5Bupf" /><figcaption>Photo by <a href="https://unsplash.com/ko/@freddymarschall?utm_source=medium&amp;utm_medium=referral">Frederick Marschall</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Upon arriving at Inventa, one of my primary responsibilities was monitoring our applications to better understand their behavior and our infrastructure.</p><p>At this point, Inventa was about a year old and we were transitioning from a serverless Lambda architecture to a microservices architecture with Kubernetes (EKS), where we didn&#39;t have a strategy for observability, alarms, or logs. More here <a href="https://medium.com/building-inventa/how-we-developed-our-deployment-model-in-inventa-part-1-ea3f83c07157">How we developed our deployment model.</a></p><p>So, we started investigating something that would allow us to scale, that was modern, and that the price wouldn’t be a pain for the business as we scaled.</p><p>Based on these requirements, an RFC was structured and the result came up with three possible options.</p><p><strong>Zabbix:</strong> Simple and easy to implement, with an agent on the instances, but the tool was created to monitor networks and evolved to keep up with the evolution of highly dynamic systems in a structure of containers and microservices. Since we were starting from scratch, we understood that something more robust and modern could meet our needs in the short, medium, and long term. We immediately ruled it out.</p><p>And then we went into a more in-depth comparison between <strong>Prometheus</strong> and <strong>Elastic Search</strong>, where we analyzed the following aspects:</p><h4><strong>Purpose:</strong></h4><ul><li>Prometheus: is a real-time monitoring tool for distributed systems, designed to collect, store, and query metrics in real-time.</li><li>Elasticsearch: is a search and data analysis engine that can be used to store, search, and analyze structured and unstructured data at scale.</li></ul><h4>Storage of Data:</h4><ul><li>Prometheus: stores data in a time series format.</li><li>Elasticsearch: uses an inverted index engine to store data.</li></ul><p>This means that Prometheus is optimized for storing and querying time series data, while Elasticsearch is more suitable for search and unstructured data.</p><h4>Scalability:</h4><ul><li>Prometheus: is highly scalable and designed to perform well in distributed environments.</li><li>Elasticsearch: is also scalable, but requires more configuration and tuning to perform well in distributed environments.</li></ul><h4>Queries and Analysis:</h4><ul><li>Prometheus: optimized for querying and analyzing real-time metrics.</li><li>Elasticsearch: more suitable for analyzing search data in unstructured data.</li></ul><p>With this, we understood that the ideal strategy for our scenario, in the medium and long term, would be to use both, where application and infrastructure metrics would be collected and stored using Prometheus, and for querying event logs and application logs, we would use Elastic Search.</p><p>However, we faced a significant challenge: we only had one engineer available to do the entire implementation. It was a large project where we wanted to cover the basic pillars of observability, which are logs, metrics, and tracing.</p><p>In the presentation of the RFC, one of our lead engineers blurts out in the virtual room:</p><blockquote>Egberto: Do you guys know about Grafana Cloud?</blockquote><p>Although no immediate decisions were made, an action point was added to explore the potential benefits of this solution.</p><p>In the study, we identified:</p><p>Roughly speaking, when we create an account on Grafana Cloud, which is a SaaS observability tool, the structure of the following services is automatically provisioned:</p><ul><li><strong>Grafana</strong>: Customizable data visualization platform that allows users to create, explore, and share dashboards and panels in real time. It supports multiple data sources, including databases, cloud services, and APIs, and offers a range of features for monitoring and analyzing systems like Kubernetes, Instances, Synthetic monitoring, Usage insights, Network usage, and more….</li><li><strong>Grafana Loki</strong>: A service used for log ingestion and querying. Configuration is done through an agent provided by Grafana Cloud upon account creation.</li><li><strong>Grafana Prometheus</strong>: A powerful open-source time series database, that can be a monitoring and alerting toolkit that enables users to collect and analyze metrics from various systems in real time, like our apps in Kubernetes.</li><li><strong>Grafana Tempo</strong>: On the <em>server side</em>, it is an easy-to-use, highly scalable open-source distributed tracing backend. It is deeply integrated with Grafana, Prometheus, and Loki. It can ingest common open-source tracing protocols, including Jaeger, Zipkin, and <strong>OpenTelemetry, which is our case</strong>. On the application side, an auto-instrumentation agent for Java based on OpenTelemetry was integrated to generate tracing spans.</li><li><strong>Grafana Alerts</strong>: It is a customized version of AlertManager highly integrated with the above-mentioned services.</li><li><strong>Grafana Incidents</strong>: also integrated with the services mentioned above, for incident management, in the same format as PagerDuty, but all these services are already available and configured when creating an account on Grafana Cloud.</li><li><strong>Synthetic Monitoring</strong>: We monitor how a specific endpoint is performing, with the possibility of configuring logins, headers, tokens, and more. For example, we use it to monitor our blog hosted outside our engineering stack.</li><li><strong>Grafana OnCall</strong>: Also integrated with the services above, when we have an alert with an incident, it is possible to escalate it to the responsible team to act on the incident, and at the same time we have a unified location where all engineers can track the status of the entire incident cycle.<br>In the free version we can register up to 5 incidents.</li><li><strong>Grafana Data sources</strong>: Grafana has plugins to integrate with virtually any possible data source, from Cloudwatch, Elastic Search, Kubernetes, Databases, Sentry, Windows, GitHub, and more.</li></ul><p>Amazing, isn’t it?</p><p>Based on this new information, we decided to adopt Grafana Cloud as our observability stack, and then we got down to work:</p><p>By leveraging Grafana Cloud’s pre-existing infrastructure as a SaaS model, we were able to streamline our configuration process and focus on creating processes and automation to effectively utilize their dashboard and visualization service, alerting and notifications service, and data source and integrations service.</p><p>But we know that not everything is perfect, right? Let’s not forget that only one engineer is working on this project. At the beginning of any new tool that we start using, we have a learning curve, which is normal and during this journey, we noticed an incredible team in the CS department, with amazing and delightful customer service always helping us!</p><p>If you’ve made it this far, you’re likely curious about the cost of using Grafana Cloud. The pricing for Grafana Cloud is determined by four different variants:</p><ul><li>Active metrics: the number of metrics being generated by the clusters and applications and consumed by Grafana Prometheus.</li><li>Logs: the number of logs in GB consumed by Grafana Loki.</li><li>Tracing: the amount of tracing in GB consumed by Grafana Tempo.</li><li>As it is a SAAS with a customized UI and authentication, we also pay per active user per month.</li></ul><p>After the applications were created and deployed in the new infrastructure, the costs grew as expected, but we identified an opportunity to improve our costs related to large-scale consumption of custom metrics, given each application sends thousands of metrics by default and this would significantly increase the costs of Grafana Cloud. Therefore, we adopted a strategy to consume these custom metrics from our Kotlin applications using a local Prometheus that we deploy and manage ourselves, and later, we extended this strategy to infrastructure metrics as well.</p><p>Today, an overview of our observability architecture would look like this:</p><p>As we were migrating from Lambda to Kubernetes, we still needed to see the logs from the Lambdas. In Grafana Cloud this is easy because we can integrate a bunch of Datasource providers as a plugin by Grafana.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/801/1*A9bnOV0s7PNNxuFyOxEj1A.png" /></figure><p>With Grafana Cloud working in our favor to abstract all the infrastructure parts (creating, configuring, and maintaining), we only had to configure integrations and create processes and automation. It took us a little over 2 months to create scalable and applicable ingestion processes for logs, metrics, tracing, incidents, and on-call in our organization.</p><p>This is how our days are here at Inventa. How about joining us!? We are looking for Engineers who enjoy the state-of-the-art platform and application development, and want to help build a very reliable, observable, and scalable product in Inventa! Hit <a href="https://www.linkedin.com/jobs/search/?currentJobId=3437853272&amp;f_C=71737018&amp;geoId=92000000&amp;originToLandingJobPostings=3437853267%2C3437852421%2C3437853272%2C3437846946%2C3437853264%2C3437847949%2C3457031342%2C3437850571%2C3437852426">here</a> to apply and, if you have any questions, do not hesitate to ping us on <a href="https://www.linkedin.com/in/marcosangomes/">LinkedIn</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*sNbD3eHefks8KwSd.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4ab0988f3c96" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/defining-inventas-observability-strategy-4ab0988f3c96">Defining Inventa&#39;s Observability Strategy</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unlocking the Secret to Engineering Team Success: How a People Partner Can Make All the Difference!]]></title>
            <link>https://medium.com/building-inventa/unlocking-the-secret-to-engineering-team-success-how-a-people-partner-can-make-all-the-difference-3667a99de47f?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/3667a99de47f</guid>
            <category><![CDATA[people-management]]></category>
            <category><![CDATA[teamwork]]></category>
            <category><![CDATA[people]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Marina Prado]]></dc:creator>
            <pubDate>Tue, 16 May 2023 14:01:42 GMT</pubDate>
            <atom:updated>2023-05-16T14:01:41.252Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XnoHTfqmIag8CuAVzI9ciQ.png" /></figure><p>What is the most crucial thing the People team has to have to work with technology teams?</p><p>I was asked by Inventa’s Head of Engineering on my interview for the role I’m currently in. I’ve been working in the HR area for over a decade now, and this simple and tricky question made me realize how much things have changed for the People area — including its name.</p><p>In the past, Human Resources was primarily viewed as a service and support function, focused on meeting basic business needs and legal requirements such as hiring and payment. The human factor was often treated separately from the goal of delivering productivity, with employees being seen as mere resources. However, over time, this perspective has changed to view human needs and well-being as critical to business success (this is a very recent event, but… finally!). This new approach considers factors such as behavior and purpose, recognizing that engaged employees perform better and lead healthier lives.</p><p>Despite this shift in perspective, one critical aspect of Human Resources has remained largely unchanged: its mindset. Many companies still view HR as a service-oriented department tasked with creating processes and projects that are thought to be best for everyone, without truly understanding how these initiatives impact employees. HR teams often lack direct experience with the routine challenges and experiences that employees face, making it difficult to develop effective solutions. This disconnect is particularly problematic when it comes to product development and squad management, where a deep understanding of employee needs and experiences is not only crucial for achieving success but also for creating solutions that have genuine meaning for people.</p><p>I want to make it more tangible with an experience I had in the past. As a People Partner within a squad that was developing a new product for a retail tech company. The developers’ coding skills were awesome, they were really good at making code scalable and clean, but they weren’t a proactive team. The team tended to get stuck on minor issues or obstacles, relying heavily on the leader to resolve every single problem, they also depended on him to stretch to other squads to understand the core part of the product. As a result, the leader became very overwhelmed, the team lost time, and progress stalled. It’s easy in a situation like that to think as a People area person: “They don’t have the culture of autonomy yet, we could do a group dynamics, or a culture talk to solve this”, etc. Since I had the opportunity to participate in retrospectives, daily meetings, and planning sessions, as well as hold regular 1-on-1s with the team, I had the right proximity to stay in the loop. In addition, my ongoing learning and willingness to ask even the most basic questions (no questions are silly here) meant that I had a good understanding of software development.</p><p>I observed that the team had not been actively involved in refining our backlog and that the items on the list were too narrowly defined as tasks. The squad leader took charge of organizing all the information and always provided direction. While it’s certainly helpful for a leader to be objective and support the team’s progress, this approach had the unintended consequence of diminishing the team’s sense of ownership over building solutions alone. So, we decided to make a change: distribute more responsibilities, making them owners of agile rites and refinement, asking them more questions on development directions than providing the answers (and a lot of changes more on routine, planning meetings, etc). One month later, we saw a significant shift in our team’s dynamic, they became the protagonists. Through our 1-on-1s, we reinforced our culture of autonomy and empowerment, using recent actions as examples and providing valuable feedback.</p><p>People can play a crucial role in any technology team by not only helping to bridge the gap between the team and the wider organization but boosting the area by applying psychology knowledge and techniques in an assertive way. By actively participating in the team’s activities, gaining deep knowledge of their area, and building close relationships with team members, People can offer valuable insights and support on issues related to people, leadership, structure, group relationship, and organizational culture. The gap between People solutions and what the tech team really needs will become smaller and smaller.</p><p>Moreover, by working closely with specialists in areas such as design, data, engineering, and product, People can help to ensure that the team is fully aligned with the wider goals of the business. Through these efforts, a skilled People person can make a significant difference in driving team success and fostering a culture of innovation and collaboration.</p><p>This is the kind of boost that the People team should strive to provide. Instead of running group dynamics that don’t resonate with employees, we should aim to be a true Partner to the team, working alongside specialists in design, data, engineering, product, and business. To be effective we’ve gotta be there, we’ve gotta know tech and how things work.</p><p>That was my answer to Adilson.</p><p>Processes and organization alone may define my role as a department, but it’s my proximity, deep understanding of the area, and active participation that enable me to become a strategic partner… a true partner to people — therefore — to business.</p><p>You can apply this mindset to any business area and people’s specialty. Did you identify with our mindset and way of thinking? Feel free to get in touch with me on <a href="https://www.linkedin.com/in/marina-prado-brocchi/">LinkedIn</a> or visit our <a href="https://jobs.lever.co/inventa/">website</a> to learn more about careers and job openings!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3667a99de47f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/unlocking-the-secret-to-engineering-team-success-how-a-people-partner-can-make-all-the-difference-3667a99de47f">Unlocking the Secret to Engineering Team Success: How a People Partner Can Make All the Difference!</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Inventa: Frontend Stack]]></title>
            <link>https://medium.com/building-inventa/building-inventa-frontend-stack-1d37f78ba0b7?source=rss----c27bd647448b---4</link>
            <guid isPermaLink="false">https://medium.com/p/1d37f78ba0b7</guid>
            <category><![CDATA[axios]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[nextjs]]></category>
            <dc:creator><![CDATA[Soter Padua]]></dc:creator>
            <pubDate>Wed, 19 Apr 2023 17:24:32 GMT</pubDate>
            <atom:updated>2023-04-19T17:24:32.660Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bh6trMR2gkxRFSpI" /><figcaption>Photo by <a href="https://unsplash.com/@impatrickt?utm_source=medium&amp;utm_medium=referral">Patrick Tomasso</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>As a software-reliant company, we understand the importance of selecting the right tools and technologies for our projects. With so many options available, we’ve carefully chosen our frontend stack based on their unique benefits and ability to help us build robust and reliable frontend applications quickly and efficiently. This article aims to present the challenges, specific requirements, and trade-offs that we considered when defining our frontend stack.</p><h3><strong>Past:</strong></h3><p>At the moment I’ve joined the company (August 2022), we had some different ways of doing/deploying frontend applications:</p><ul><li>React + AWS S3</li><li>Vanilla JS + AWS S3</li><li>NextJS + <a href="https://aws.amazon.com/amplify/">AWS Amplify</a></li></ul><p>This fragmentation made every team a universe in itself, each doing things its own way and solving the same problems many times over.</p><p>As a first investigation, I’ve attempted to identify the cons from our current usage of NextJS (with AWS Amplify), mainly because I believe that NextJS is a good tool to solving the kind of frontend problems that we have here at Inventa.</p><p>Some of the issues, at the time, that this approach involving NextJS and AWS Amplify presented:</p><ul><li>Next.js major version at the time (12) wasn’t supported so we were locked into 11.x.x</li><li>Amplify creates all the AWS resources required for the deploy in a automatic fashion, the problem with that is managing all this resources was not trivial, some of it wasn’t ‘visible’ on AWS Console, neither clearly specified on billing section.</li><li>At the time we’re doing some restructuring of our AWS management principles in order to optimize the billing and provide more uniformity to our resources and its configurations. This context also pushed us away from keeping Amplify as a solution internally.</li><li>Also regarding the automatic management of AWS resources, we’ve had problems when trying to fine-tune the Cloudfront distribution invalidations upon deploying ( resulting in a wasteful deploy process from the perspective of billing).</li><li>Required a lot of webpack-specific customizations</li></ul><p>After taking the decision to abandon AWS Amplify as the default “deploy stack” we started reviewing which components of our current stack made sense to be kept and formalized and which should be, at least, not recommended.</p><h3>Engineering Design Review Process:</h3><p>At the company we have a design review process to solicit feedback from peers and domain experts on your proposed changes to the codebase in pursuit of some objective.</p><p>When is a design review necessary?</p><ul><li>A new project, service, or component is being created</li><li>Anytime a data contract change is proposed for an established service</li><li>Any significant change to an existing design or deployment</li><li>Services/Patterns that could be used as cross-company approach</li></ul><p>The result of this process is a series of documents and discussions structured in a way to enable all engineers to quickly grasps what’s being discussed and, if applicable, a high-level of implementation details.</p><p>Given that we were considering to change the default frontend stack for the company, this process was required to make sure that the solution was appropriated.</p><p>Throughout this article we’ll use the material from this process to present a what was researched.</p><h3><strong>Present/Future:</strong></h3><p>We’re in a continuous process of improvement so what follows is a description of one of those iteration processes.</p><p><strong>Stack Core</strong></p><p>One of the kept frontend technologies is Next.js, a React framework that offers complex features and provides ‘missing’ React features in a well-defined API. Next.js comes with a built-in router that is easy to use and helps maintain uniformity in the project’s folder structure. Additionally, Next.js offers multiple rendering strategies, including Static Generation, Server-Side Rendering, Incremental Static Regeneration, and Client-Side Rendering.</p><p>This flexibility allows us to choose the best rendering strategy for each project and ensures our code is well-documented and widely used. Next.js also allows for the easy creation of BFFs (using the `/api` route) which reduces the necessity of extra logic in a different project to ensure that we’ll be able to interact with a specific service.</p><p>(We also support static deployed web applications but they aren’t the focus of this article).</p><p><strong>Internationalization</strong></p><p>To streamline our internationalization efforts and ensure our applications are ready to be accessible to users that speak other languages, we use <a href="https://formatjs.io/docs/getting-started/installation">react-intl</a>, a library related to internationalization. It helps us build applications that can be easily translated into different languages. It does that by using <strong>messages</strong>.</p><blockquote>Simple message definition:</blockquote><pre>const presentationMessage = {<br>  description: &#39;A simple presentation message&#39;, // Description should be a string literal<br>  defaultMessage: &#39;My name is {name}&#39;, // Message should be a string literal<br>}</pre><blockquote>Simple message usage in React:</blockquote><pre>// Hook<br>import { useIntl } from &#39;react-intl&#39;<br><br>const Component = () =&gt; {<br>  const intl = useIntl();<br><br> return (&lt;div&gt;{intl.formatMessage(presentationMessage)}&lt;/div&gt;)<br><br>}</pre><pre>// Component<br>import { FormattedMessage } from &#39;react-intl&#39;<br><br>const Component = () =&gt; {<br>  const intl = useIntl();<br><br> return (&lt;FormattedMessage {...presentationMessage} /&gt;)<br>}</pre><p><strong>Styling</strong></p><p>For building responsive and accessible user interfaces, we rely on <a href="https://chakra-ui.com/">ChakraUI</a>, a flexible and easy-to-use component library. ChakraUI offers a range of pre-built components that can be easily customized to fit our specific needs, making it a valuable tool for building complex interfaces quickly and efficiently. When deciding we’ve compared it with Material UI, Chakra UI offers developers more flexibility in modifying CSS classes of exported components and layouts, which can save time and effort in customizing interfaces. On the other hand, Material UI provides a wider range of pre-styled UI components and may be more suitable for projects that do not require extensive custom styling. For our context as a company, the ability to do more custom styling but still keeping some sort of theme structure is really valuable.</p><p>Another option considered was <a href="https://tailwindcss.com/">Tailwind CSS</a> (we have projects that used tailwind and still do); Chakra UI provides a more comprehensive solution, offering not only styling benefits but also a library of carefully created React components that takes care of all the details, such as accessibility, keyboard navigation, and component composition. While Tailwind CSS provides atomic CSS classes to help style components, Chakra UI comes with an intuitive, prop-based model of styling components, making it easier to learn and understand. We’re still maintaining the internal projects that use Tailwind and constantly learning its limitations and advantages, but for now the decision is to avoid it in new projects.</p><p>In a goldilocks-like situation, it seemed like MaterialUI was too much (in various senses but mainly: bundle size, opinionated components), and Tailwind CSS was too little (in the sense of requiring too much time/code to create simple React components that matched our current design). ChakraUI provided a more direct approach to do the necessary modifications without defining too hardly the component style.</p><p>To style and manage our React components, we use <a href="https://emotion.sh/docs/introduction">Emotion</a> (@emotion/styled, @emotion/react) — a library designed for writing CSS styles with JavaScript. Emotion provides powerful and predictable style composition, it uses the same API as `styled-components` (pure CSS inside a template string syntax that gets parsed by a utility function). Another point for `emotion` was the fact that ChakraUI uses it under the hood.</p><pre>import styled from &#39;@emotion/styled&#39;<br> <br>const Button = styled.button`<br>  padding: 32px;<br>  background-color: hotpink;<br>  font-size: 24px;<br>  border-radius: 4px;<br>  color: black;<br>  font-weight: bold;<br><br>  &amp;:hover {<br>    color: white;<br>  }<br>`</pre><p>We also use Typescript to add type safety to our projects, which helps us catch errors earlier in the development process and improve our overall development experience. With Typescript, we get better IDE support, making it easier to work with complex codebases. What is currently ongoing is a complete transition to TS only, as we have a legacy codebase where the majority of code is in JavaScript, there is the necessity to support less strict linter rules to enable this hybrid environment.</p><p><strong>Testing</strong></p><p>And when it comes to testing, we’ve chosen Jest and React Testing Library as our preferred testing frameworks, one more focused on unit tests and the other on testing the interaction with React components. Jest offers a range of features that make it easy to write and maintain tests, such as a simple and intuitive API and code coverage analysis.</p><p>Given the need for robust and reliable frontend applications, it is important to use a tool that can ensure that the applications are working as expected, for that we are using Cypress as our end-to-end testing tool in order to write tests that simulate user interactions and provide features that can help us to deliver fast and reliable tests such as:</p><ul><li>Intercepting requests and mocking endpoints helping us reducing flakiness, speeding up test execution doing by api things that we don’t need to validate in each test and simplifying test setup</li><li>Adding plugins to extend Cypress capability like <a href="https://www.npmjs.com/package/@cypress/grep">cypress-grep that allow us to tag our tests and run a suite of tests based in tags like smoke and regression</a>, <a href="https://www.npmjs.com/package/cypress-axe">Axe plugin for accessibility tests</a>, <a href="https://www.npmjs.com/package/@cypress-audit/lighthouse">Lighthouse Plugin to runs performance, accessibility, best practices, SEO</a> and other useful plugins from a rich ecosystem.</li><li>Cypress allows us to define custom commands that can be reused across our tests. This helps us simplify our test code and make it easier to maintain.</li><li>Since our cypress tests live in the same repository that our application source codes we can reusing locators from the source code. With Cypress we can easily reuse the same locators from our applications to write tests. This means that we don’t have to duplicate the element locators in our tests, which can save a lot of time and reduce the chances of errors.</li></ul><p><strong>Data Fetching</strong></p><p>Finally, we use Axios for handling HTTP requests in our projects. We’ve considered using the new `fetch` API available for Node 18 (the majority of our projects were using it), but the fact that <a href="https://nodejs.org/dist/latest-v18.x/docs/api/globals.html?ref=console-log#fetch">it was experimental</a> (Stability 1) made us choose a more consolidated solution.</p><p>Axios offers a unified API that can be used across both Node.js and browser environments, allowing us to easily reuse code regardless of the JS runtime. It’s a powerful tool that abstracts some of the more tedious aspects of other implementations, such as `await data.json()` from XMLHttpRequests. This makes it easy for us to extract parts of the logic to its own packages using the instance mode, creating “SDK-like” structures.</p><p>During the research about how to architect isolated(or at least easy to extract) solutions using Axios some options were considered:</p><p><strong>Option 1</strong></p><p><strong>Overview</strong></p><ul><li>Create a function for every endpoint that will be consumed in the application.</li><li>Avoid using SSR to simplify the flow and implement the applications on the client side only.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P_QwMS5klkktlZ2npR9C9w.png" /><figcaption>Option 1</figcaption></figure><p><strong>Pros</strong></p><ul><li>Simpler approach.</li><li>Avoids having to know anything about SSR.</li><li>Provides more flexibility for the engineer.</li></ul><p><strong>Cons and Limitations</strong></p><ul><li>It does not scale so well if we have a bigger quantity of endpoints.</li><li>Each endpoint is an independent part of the code.</li><li>Specific logic that is shared cross API needs to be repeated, request timeout, auth logic, for example.</li><li>Doesn’t leverage SSR.</li></ul><p><strong>Option 2</strong></p><p><strong>Overview</strong></p><ul><li>Map the APIs used on the application using <a href="https://github.com/axios/axios#creating-an-instance">axios’ instance mode</a></li><li>This way is like we have mini SDKs for the APIs and later on, is easy to extract this logic to a separated package and re-use it across projects.</li><li>This approach can be used in conjunction with Next’s SSR for more simple pages.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M1oWh5VddHxOzR4_PuS1bQ.png" /><figcaption>Option 2</figcaption></figure><p><strong>Pros</strong></p><ul><li>Easy to re-use.</li><li>Enables us to create sensible defaults for all the APIs, such as error reporting, cache rules, custom headers.</li><li>Easy to share API configurations across endpoints, such as timeout, authentication, custom redirect logic, status code validation.</li></ul><p><strong>Cons and Limitations</strong></p><ul><li>More overhead when compared with Option 1</li><li>Makes more sense when we have multiple endpoints to implement at once, implementing a single endpoint with this logic would be overkill.</li></ul><p><strong>Option 3</strong></p><p><strong>Overview</strong></p><ul><li>This approach builds upon the second option, with a slight change.</li><li>Use <a href="https://react-query.tanstack.com/overview">react-query</a> as a wrapper around axios, <a href="https://react-query.tanstack.com/guides/ssr#using-initialdata">providing cache manager behavior</a> and enabling SSR data fetching to be simpler and consistent across client and server.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UDyJQkToVj_VMKaqH9UxXA.png" /><figcaption>Option 3</figcaption></figure><p><strong>Pros</strong></p><ul><li>leverages Next’s SSR.</li><li>added cache layer that is SSR aware helps to fine-tune cache behavior.</li><li>prevents unnecessary refetches, react-query manages fetch calls and tracks information staleness.</li></ul><p><strong>Cons and Limitations</strong></p><ul><li>adds an overhead that can be confusing or misused introducing bugs.</li><li>usage is not trivial</li><li>as we would be promoting react-query to a core dependency, there would need to be extensive internal documentation to support the teams’ ramp-up when using this dependency.</li></ul><p>As a default option we’ve chose the <strong>second option</strong>, keeping the third as a special case to be studied and formalized later on. The main reason behind this decision was a trade-off, we’ve recognized the value of the 3rd option but also knew it to be more complex to implement and maintain at our current stage.</p><h3><strong>Conclusion</strong></h3><p>At the end of the day, our frontend stack choices are critical to the success of our projects. By carefully selecting the right tools for the job, we’re able to deliver high-quality software that meets the needs of our clients and end-users alike. We’re proud of our frontend stack choices, and we’re confident that they will continue to help us build robust and reliable applications for years to come.</p><p>Did you like the challenge? How about joining us!? We are looking for software engineers who enjoy the state-of-the-art platform and application development, and want to help build a very reliable, observable, and scalable product in Inventa! Hit <a href="https://www.linkedin.com/jobs/search/?currentJobId=3437853272&amp;f_C=71737018&amp;geoId=92000000&amp;originToLandingJobPostings=3437853267%2C3437852421%2C3437853272%2C3437846946%2C3437853264%2C3437847949%2C3457031342%2C3437850571%2C3437852426">here</a> to apply and, if you have any questions, don’t hesitate to call me on <a href="https://www.linkedin.com/in/soter-padua-836671116/">LinkedIn</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UOZ0ej86dsE4kPFK_lQsig.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d37f78ba0b7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/building-inventa/building-inventa-frontend-stack-1d37f78ba0b7">Building Inventa: Frontend Stack</a> was originally published in <a href="https://medium.com/building-inventa">Building Inventa</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>