<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Supermetrics</title>
    <description>The latest articles on DEV Community by Supermetrics (@supermetrics).</description>
    <link>https://dev.to/supermetrics</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3232%2F0577d7aa-2056-4154-8adc-481c955d5f6c.png</url>
      <title>DEV Community: Supermetrics</title>
      <link>https://dev.to/supermetrics</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/supermetrics"/>
    <language>en</language>
    <item>
      <title>Meet the Supermetrics Product Development Team</title>
      <dc:creator>Siiri Hakulinen</dc:creator>
      <pubDate>Thu, 29 Aug 2024 12:17:39 +0000</pubDate>
      <link>https://dev.to/supermetrics/meet-the-supermetrics-product-development-team-525g</link>
      <guid>https://dev.to/supermetrics/meet-the-supermetrics-product-development-team-525g</guid>
      <description>&lt;p&gt;The Supermetrics products allow businesses to integrate their marketing data from siloed sales and marketing platforms and manage, transform, and analyze that data to inform decision making, optimize marketing efforts, and improve business results. &lt;/p&gt;

&lt;p&gt;It sounds simple, but when you factor in the scale we operate on, how many petabytes of data we process, and the fact that we do this live, it isn't simple. Around fifteen percent of global advertising is reported through our products. &lt;/p&gt;

&lt;p&gt;It's a team effort to make all that happen. In this blog post, we'll introduce you to our Product Development organization, how it works, and which product areas each team owns.&lt;/p&gt;

&lt;h2&gt;
  
  
  100+ engineers, three product groups
&lt;/h2&gt;

&lt;p&gt;Currently, our teams are divided into three product groups, with 4–6 development teams in each group: &lt;br&gt;
The Data Integrations Group owns the data sources — they build and maintain the API integrations between our product and the marketing and sales platforms where we pull the customers’ data. &lt;br&gt;
The Platform and Apps Group owns where the data goes and what is done with it — they build the Supermetrics Marketing Intelligence Cloud, and develop a host of value-adding functionality for, combining, analyzing, and managing data. &lt;br&gt;
The Infrastructure Group encompasses DevOps, performance engineering, security, and developer experience. &lt;/p&gt;

&lt;p&gt;Each development team consists of people with various roles working together. Software Engineers, Lead Software Engineers, and Engineering Managers are paired with a dedicated Product Manager and Designer, and experts from fields like performance engineering and DevOps are on hand when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we work together
&lt;/h2&gt;

&lt;p&gt;Each team has the autonomy to choose its ways of working. We share some working patterns to make internal communication and alignment easier and avoid siloing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most (if not all) teams have chosen to use Kanban, which we’ve modified a bit to suit our needs.&lt;/li&gt;
&lt;li&gt;We're agile and combine useful features from Scrum and Kanban to manage stories.&lt;/li&gt;
&lt;li&gt;Jira is our tool of choice for keeping tabs on tasks, epics, and the like. We plan each epic and discuss these plans across the organization to keep everyone on the same page.&lt;/li&gt;
&lt;li&gt;We prefer more communication over less. As the engineering organization grows and our products become more complex, teams must communicate with each other to avoid silos. On top of team dailies and project-level communications, the Product Development team has a monthly all-hands meeting for topical updates, highlights from the product groups’ work, and team updates. There’s also a Super Product Session every month, where the Product Development team shares news on the product strategy, roadmap, and release calendar and demoes new features to the rest of the company. &lt;/li&gt;
&lt;li&gt;While different teams are for different things, all engineers at Supermetrics can contribute anywhere in the codebase. If you see a bug on another team's domain, you're welcome to submit a pull request and get the appropriate team member to review it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our tech stack
&lt;/h2&gt;

&lt;p&gt;Most of our backend code uses PHP, MySQL, and Redis. You might see some Go, Node.js, or Python, too. We use these technologies locally in Docker containers. Our staging and production rely fully on cloud services provided by Google, Amazon, and Cloudflare.&lt;/p&gt;

&lt;p&gt;Our frontend is based mostly on React, TypeScript, and Google App Script. Some projects still use Vue.js. We use REST APIs for communication.&lt;/p&gt;

&lt;p&gt;Without going too deep into the weeds, here's a short list of other tools and technologies we use in our daily work: Jira, GitHub, PHPStan, Psalm, Jest, PubSub, PHPUnit, and Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing our engineering groups and teams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Integrations
&lt;/h3&gt;

&lt;p&gt;A data integration is an API integration that fetches data and processes it to match the user's request before sending it to a destination. The Data Integrations Group is responsible for these third-party API integrations and fetching data from various marketing and sales platforms. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Connectors Engineering team&lt;/strong&gt; maintains and migrates APIs within existing coded connectors. We currently have over 150 connectors, half of which are coded. It means they're written using actual code and need to be maintained and adjusted as the user base grows. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Connector Platform team&lt;/strong&gt; owns the technology that allows us to build data integrations, including codeless connectors. You can read more about them in this &lt;a href="https://dev.to/supermetrics/accelerating-api-integration-development-with-codeless-connectors-1bb4"&gt;tech blog post&lt;/a&gt;. Essentially, the Connector Platform team empowers us to build and maintain data integrations faster and better than before.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Connectors Apps&lt;/strong&gt; team builds end-to-end products with user-facing interfaces, such as File Import and the Connector Builder. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;New Connectors team&lt;/strong&gt; uses our own technology to configure new data integrations. The Connector Specialists don't have to have coding skills. They only need to understand the API documentation and our configuration schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Platform and Apps
&lt;/h3&gt;

&lt;p&gt;The Platform and Apps Group leads the charge on our new product strategy, where we expand beyond the data pipelining solution that has made Supermetrics successful in the past ten years. They’re building a product roadmap that will cover all the stages of the marketing data lifecycle, including exciting new fields like data storage, governance, and data transformations.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Destinations team&lt;/strong&gt;  focuses on building and maintaining the external data destinations to which Supermetrics pipelines data. We partner with and build functionality on top of widely used data analysis and visualization applications, like Microsoft Excel, Google Sheets, Looker Studio, PowerBI.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Customer Growth team&lt;/strong&gt; is responsible for maintaining, improving, and measuring the new customer journey, including registration for a new account, starting a trial, and providing purchasing services within the Supermetrics user interface. The team provides contextual nudges for customers to try out new products and offers embedded upgrade mechanics for subscriptions. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Data Management team&lt;/strong&gt; builds value-added features related to data management, like Custom Fields and Data Blending that allow customers to normalize and merge their marketing data from a multitude of marketing and sales platforms into meaningful formats for reporting and analytics.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Data Storage team&lt;/strong&gt; builds and maintains value-added features related to data storage, including external data warehouses like BigQuery and Snowflake, as well as the Supermetrics Storage offering.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Customer Platform team&lt;/strong&gt; is responsible for the overall function, look, and feel of the Supermetrics user interface. They improve and maintain the design system, advocating for component usage across the group, and developing value-added features related to the user interface. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure Group
&lt;/h3&gt;

&lt;p&gt;The Infrastructure Group  operates cross-functionally and supports the work of other product groups and teams. Infrastructure teams are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;Performance Engineering&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Developer Experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Product Development organization have expanded over time
&lt;/h2&gt;

&lt;p&gt;Each group and team offers a unique perspective and distinct technical challenges. What they have in common is that engineers can truly influence not just their own work but the evolution of the Supermetrics products and their technical implementation. All our teams are also made up of wildly talented and helpful colleagues.&lt;/p&gt;




&lt;p&gt;We continue to grow and are looking for talented people to join our Product Development team. Explore career opportunities at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Interfaces and exceptions</title>
      <dc:creator>Niklas Lampén</dc:creator>
      <pubDate>Mon, 07 Aug 2023 07:47:16 +0000</pubDate>
      <link>https://dev.to/supermetrics/interfaces-and-exceptions-29fo</link>
      <guid>https://dev.to/supermetrics/interfaces-and-exceptions-29fo</guid>
      <description>&lt;p&gt;Using interfaces – or protocols in some languages – is something we as programmers definitely want to do. Interfaces should be used to define dependencies between classes as it comes with quite a few benefits such as following the &lt;a href="https://dev.to/tamerlang/understanding-solid-principles-dependency-inversion-1b0f"&gt;Dependency Inversion Principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm using PHP in my examples, but this applies to other languages, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;An interface is a contract, defining dependencies with interfaces makes it easy to replace an implementation, and you should only throw specified exceptions from them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an interface?
&lt;/h2&gt;

&lt;p&gt;An interface is a specification that the implementing class has to follow. This means that no matter what concrete class we operate with, we know what to expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple example
&lt;/h2&gt;

&lt;p&gt;Let’s start with a simple example. In this example we want to store user data to a storage. Because we’re looking well ahead, we understand to not lock ourselves with our current storage solution, which is MySQL. As the first step we went through the requirements and realized that we need to be able to store users to the storage, and fetch users from the storage based on user’s ID.&lt;/p&gt;

&lt;p&gt;After thinking about it for a while, we came up with a definition of how we want to communicate to the storage, a.k.a. interface, a.k.a. &lt;code&gt;UserDriverInterface&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserDriverInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Store User to a database
     *
     * @param User $user The user to store to the storage
     *
     * @throws UserException Thrown if storing of the User fails.
     * @return bool
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Find a User from the database by the ID
     *
     * @param string $id ID of the user to find from the storage
     *
     * @return User|null Returns an instance of User if user is found from the storage, NULL otherwise.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As said, our current storage solution is MySQL, so we wrote our first implementation, which connects to a MySQL database and uses that as the storage. As you can see, it &lt;code&gt;implements UserDriverInterface&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MysqlUserDriver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;UserDriverInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;?PDO&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param string $username User name for the MySQL server
     * @param string $password Password for the MySQL server
     * @param string $server   MySQL Server URL
     * @param int    $port     MySQL Server port to use
     * @param string $database Name of the MySQL database/schema
     *
     * @throws UserException
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$connectionString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mysql:host=%s;port=%d;dbname=%s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\PDOException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'Connecting to MySQL server failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;$e&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INSERT INTO users (id, name) VALUES (:id, :name)'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bindValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PARAM_STR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bindValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PARAM_STR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Couldn't store user to MySQL database"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT id, name FROM users WHERE id=:id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bindValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PARAM_STR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PDO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FETCH_OBJ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userData&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="cd"&gt;/**
         * This should rather be done in separate converter
         */&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userData&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How do we use this?
&lt;/h3&gt;

&lt;p&gt;When we're writing our code, the only thing we refer to is the interface. One way could be fetching this driver from a DI-container with something like &lt;code&gt;$container-&amp;gt;get(UserDriverInterface::class)&lt;/code&gt;. Or maybe you want to abstract it more and add a &lt;code&gt;UserService&lt;/code&gt; to the stack. In the latter case only the service would know about the driver interface, and the code using the service would just use the service and not know what happens inside it.&lt;/p&gt;

&lt;p&gt;As we're not really referencing to the concrete classes anywhere, it’s easy for us to change the concrete implementation from MySQL to something else. Maybe it’s for testing, or maybe we just decided that using plain text files is the way to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another implementation
&lt;/h2&gt;

&lt;p&gt;Let’s assume that we want to use a in-memory storage for testing our main implementation. We could write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryUserDriver&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;UserDriverInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var array The data storage
     */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Can\'t store User without ID'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @inheritDoc
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the code you have written for &lt;code&gt;MysqlUserDriver&lt;/code&gt; will work just as fine with &lt;code&gt;MemoryUserDriver&lt;/code&gt; as both are implementing the &lt;code&gt;UserDriverInterface&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you wanted to use the memory storage in your development environment, and to use MySQL storage in production, you could have something like this in your factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyUserDriverFactory&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;FactoryInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerInterface&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;UserDriverInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'environment'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ENV_PROD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Return MysqlUserDriver&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Return MemoryUserDriver&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interface and exceptions
&lt;/h2&gt;

&lt;p&gt;As a reminder, here’s the definition of &lt;code&gt;store()&lt;/code&gt; method of the &lt;code&gt;UserDriverInterface&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="cd"&gt;/**
     * Store User to a database
     *
     * @param User $user The user to store to the storage
     *
     * @throws UserException Thrown if storing of the User fails.
     * @return bool
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the only exception it should throw is &lt;code&gt;UserException&lt;/code&gt;. But what if our implementation is using a library that might throw an &lt;code&gt;\AwesomeLibrary\SpaceTimeContinuumException&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;What you should do is catch the &lt;code&gt;\AwesomeLibrary\SpaceTimeContinuumException&lt;/code&gt;, and throw the &lt;code&gt;UserException&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother with catching and throwing?
&lt;/h2&gt;

&lt;p&gt;Because, if we don’t, the code using the interface needs to know about the exact implementation of the interface – which pretty much defeats the purpose of an interface. The code using the interface shouldn’t know about the implementation.&lt;/p&gt;

&lt;p&gt;When we are working on an interface that’s internal (used only inside the project), it’s easy to alter the interface each time you modify one of the implementations – but you shouldn’t do this. You should think about your interface as being defined somewhere else. Could you then add your own exceptions to it’s PHPDoc in that case? Well, no.&lt;/p&gt;

&lt;p&gt;Or, what if there were 100 implementations of the interface? To list all of the possible exceptions, the interface should know about each implementation, and change the description of the interface for each of the implementations. Sounds sane? I didn’t think so.&lt;/p&gt;

&lt;p&gt;And more importantly: what about the existing implementations? Let’s assume there’s this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$driver&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Storing user failed: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$driver-&amp;gt;store()&lt;/code&gt; is expected to thrown a &lt;code&gt;UserException&lt;/code&gt; but no other exceptions. If someone decided that now the &lt;code&gt;store()&lt;/code&gt; method can also throw &lt;code&gt;\AwesomeLibrary\SpaceTimeContinuumException&lt;/code&gt;, the code wouldn’t handle it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;When defining an interface you should think about things going wrong as well as the happy paths, and you should have a plan how these issues are communicated to the class/function using the interface. Your mindset should be that you don't know anything about the concrete classes implementing the interface, and the concrete classes don't know anything about where they are being used.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>php</category>
    </item>
    <item>
      <title>Summer Reading: Our Tech Blog Posts from 2023</title>
      <dc:creator>Siiri Hakulinen</dc:creator>
      <pubDate>Wed, 28 Jun 2023 14:05:19 +0000</pubDate>
      <link>https://dev.to/supermetrics/summer-reading-our-tech-blog-posts-from-2023-5hb1</link>
      <guid>https://dev.to/supermetrics/summer-reading-our-tech-blog-posts-from-2023-5hb1</guid>
      <description>&lt;p&gt;Whether taking time off or enjoying the quiet time at work this July, you might be looking for something interesting to read.&lt;/p&gt;

&lt;p&gt;Here's a list of our Tech Blog posts from this year, in case you'd like to bookmark them for summer reading.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/getting-rid-of-anti-patterns-of-development-work-3m8i"&gt;Getting rid of anti-patterns of development work&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/five-success-factors-for-building-a-new-software-product-in-70-days-42j0"&gt;Five success factors for building a new software product in 70 days&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/avoiding-feature-creep-as-a-developer-57l4"&gt;Avoiding feature creep as a developer&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/the-power-of-metrics-driven-development-how-to-build-better-products-4k1p"&gt;The power of metrics-driven development: How to build better products&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/improve-communication-and-transparency-with-architecture-decision-records-adr-k45"&gt;Improve communication and transparency with Architecture Decision Records (ADR)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/benefits-of-a-developer-experience-devex-team-gca"&gt;Benefits of a Developer Experience (DevEx) team&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/supermetrics/how-to-organize-an-internal-bugathon-clean-up-your-backlog-and-help-charities-in-8-steps-34j2"&gt;How to organize an internal bugathon, clean up your backlog, and help charities in 8 steps&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here are two well-loved blog posts from 2022 as an extra treat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/supermetrics/optimizing-jsonpath-lookups-in-php-to-extract-data-up-to-7-times-faster-2oi0"&gt;Optimizing JSONPath lookups in PHP to extract data up to 7 times faster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/supermetrics/accelerating-api-integration-development-with-codeless-connectors-1bb4"&gt;Accelerating API integration development with Codeless Connectors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Learn more about &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;working in Engineering at Supermetrics&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>json</category>
      <category>api</category>
      <category>management</category>
    </item>
    <item>
      <title>Improve communication and transparency with Architecture Decision Records (ADR)</title>
      <dc:creator>Paweł Lewtak</dc:creator>
      <pubDate>Fri, 12 May 2023 12:29:55 +0000</pubDate>
      <link>https://dev.to/supermetrics/improve-communication-and-transparency-with-architecture-decision-records-adr-k45</link>
      <guid>https://dev.to/supermetrics/improve-communication-and-transparency-with-architecture-decision-records-adr-k45</guid>
      <description>&lt;p&gt;Architecture Decision Record, or ADR for short, is a tool that will improve communication, increase transparency in your engineering department, foster collaboration, and make future maintenance easier. We at Supermetrics are using ADRs and have found these benefits for ourselves.&lt;/p&gt;

&lt;p&gt;Having up-to-date and useful documentation is a constant struggle. It’s hard for software engineers to think about writing documentation when writing actual code is often more interesting. It’s even harder to keep that documentation up to date if there’s a production incident or a tight deadline for delivering a project. As a result, documentation might not exist or not be as useful as it could be. That’s where ADRs can be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Architecture Decision Record?
&lt;/h2&gt;

&lt;p&gt;Simply put, an ADR is the sweet spot between having extensive, deep-dive documentation and no documentation at all. Think of it as minimal documentation that’s always up to date with just a little effort.&lt;/p&gt;

&lt;p&gt;More specifically, an Architecture Decision Record is a document that captures the important decisions made during the development of any project. It’s about writing down your thoughts, the context around the changes you’re making, your assumptions at the time of writing, expected consequences, and the changes themselves. &lt;strong&gt;Michael Nygard&lt;/strong&gt; &lt;a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions.html" rel="noopener noreferrer"&gt;coined the name ADR&lt;/a&gt; and created &lt;a href="https://github.com/joelparkerhenderson/architecture-decision-record/blob/main/templates/decision-record-template-by-michael-nygard/index.md" rel="noopener noreferrer"&gt;a handy template&lt;/a&gt; for creating them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A template for ADRs
&lt;/h2&gt;

&lt;p&gt;The original template, as suggested by Nygard, looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title&lt;/strong&gt; - May include a sequential number and/or date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt; - The status of ADR, such as proposed, accepted, rejected, deprecated, or superseded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt; - An explanation of why the change is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision&lt;/strong&gt; - A summary of the actual change being made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consequences&lt;/strong&gt; - The anticipated positive and negative consequences of the change. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Supermetrics, we’ve decided to extend the template with a few more items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt; - Will the change have a low/medium/high impact on the code base?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributors&lt;/strong&gt; - Who contributes to preparing, reviewing, and approving the ADR?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Options considered&lt;/strong&gt; - A summary of what other options were considered, if any.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outcome&lt;/strong&gt; - What do we expect the end result to be when this decision is fully implemented?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt; - How will we measure the success of this decision?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roll-out strategy&lt;/strong&gt; - What’s the plan for releasing this change to users?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each development team is welcome to customize the template if they find it limiting. When using an ADR template, customize it to fulfill your needs and cover your use case. &lt;/p&gt;

&lt;h2&gt;
  
  
  When to write an ADR?
&lt;/h2&gt;

&lt;p&gt;If you made a code change but didn't document and communicate it properly, you can hardly expect your teammates to know about it or understand it. As short-form documentation, an ADR makes getting everyone on the same page easy. It creates a common understanding of new changes before they happen.&lt;/p&gt;

&lt;p&gt;So, prepare an ADR before you make a change that impacts another part of the system or how code is written. As a bonus, it'll help reduce the number of different solutions for the same problem, which often result from undocumented changes.&lt;/p&gt;

&lt;p&gt;Nothing stops you from going back to old changes and writing ADRs about them. It’s even recommended, especially when different developers repeatedly ask the same questions about a part of your system. Next time, you can refer them to the ADR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where and how to store ADRs?
&lt;/h2&gt;

&lt;p&gt;The original idea by Nygard suggests you should use Markdown for easier formatting and reading and a git repository for storing the .md files themselves— ideally in the same repository as the code. At Supermetrics, we decided to keep ADRs in our wiki Confluence because we have multiple products and many repositories. It’s easier to find and read ADRs if they’re all in the same place, especially if individual ADRs affect multiple products. &lt;/p&gt;

&lt;h2&gt;
  
  
  Benefit #1: easier documentation maintenance
&lt;/h2&gt;

&lt;p&gt;As you can see from the template, the final ADR isn’t going to be a very long document. Depending on your template, it could be from one to a few pages of text. Nor should it be highly abstract or require deep-dive knowledge to decipher. The ADR is supposed to be a simple document that gives concrete, practical information about how a change has been thought out and executed. It’s not about something abstract, or about explaining some architecture that requires deep dive knowledge. Writing down the context, decision, and consequences is straightforward enough since a developer has to consider them anyway when making changes to code. &lt;/p&gt;

&lt;p&gt;An ADR explains the thinking behind a change at the time when the change was made. Once approved, its contents don't change much. Later, if you find out that you made a poor decision, or if the requirements of the decision (the context) change, you'll prepare a new ADR and deprecate or supersede the older one, changing only its status. The ADR releases you from maintaining old documentation. If something changes in the code, you’ll simply create a new ADR for the alterations.&lt;/p&gt;

&lt;p&gt;You can compare an ADR to a blockchain, where only new pieces are added, and you need to go through the whole chain of records to understand the current state of the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefit #2 - Insight into the past
&lt;/h2&gt;

&lt;p&gt;Understanding the why behind past software design decisions allows you to better understand their outcome. ADRs can help you with that. Traditional documentation typically explains how different parts of the software are connected and how they communicate. What it fails to explain is why something was done a certain way.&lt;/p&gt;

&lt;p&gt;An ADR is a kind of a brain dump, covering the context of a change and its potential effects, and considering alternative options. So, when a new person joins the team and asks, "Did you consider X?" or "Why was it done this way?" they can go and read the ADR.&lt;/p&gt;

&lt;p&gt;If you use an ADR throughout your projects for all meaningful changes, you’ll notice that it becomes a changelog and a single source of truth for your software. There’ll be less speculation about past changes and less knowledge lost between silos. ADRs also make code ownership a real thing, making project handovers and learning from past mistakes easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefit #3 - improved transparency, collaboration, and communication
&lt;/h2&gt;

&lt;p&gt;The ADR template we use at Supermetrics includes a list of collaborators, which ensures that any planned changes are discussed between at least a few people, enforcing communication and collaboration. Teammates may have differing opinions about how to solve a problem, but collaborating on the ADR makes it easier to find common ground and choose the best solution. It also helps us see more of the potential consequences of implementing a change.&lt;/p&gt;

&lt;p&gt;Listing contributors also makes it easy for developers to discuss past changes and their results with change authors and plan future changes that would supersede the old ones. Not knowing why something was done in the past makes future changes in the same area very risky, so that context should be written down in an ADR and made easily accessible to all developers.&lt;/p&gt;

&lt;p&gt;ADRs make communication easier by improving transparency and collaboration within and between development teams. To make it even better, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use simple, easily understandable language in your ADRs. &lt;/li&gt;
&lt;li&gt;Skip details on technical implementation — those aren’t meant to be part of an ADR.&lt;/li&gt;
&lt;li&gt;Limit ADRs to a single decision or change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  No documentation is perfect
&lt;/h2&gt;

&lt;p&gt;We think ADR is a good-enough form of documentation for architecture and software changes. While it’s not a silver bullet by any means, it’s simple to prepare and has great benefits. ADRs allow us to make decisions widely understandable within the Engineering team and course-correct later if needed while keeping a paper trail of all the changes. &lt;br&gt;
These easy-to-read documents make it easier to onboard new developers to the inner workings of the system architecture and why it was built in a certain way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;Give ADRs a try, and let us know what you think. Were they helpful? What information did you find essential to include in your ADR template? We’d love to learn from your experiences!&lt;/p&gt;




&lt;p&gt;Learn more about how the Engineering team at Supermetrics works at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to organize an internal bugathon, clean up your backlog, and help charities in 8 steps</title>
      <dc:creator>Paweł Lewtak</dc:creator>
      <pubDate>Fri, 28 Apr 2023 07:40:17 +0000</pubDate>
      <link>https://dev.to/supermetrics/how-to-organize-an-internal-bugathon-clean-up-your-backlog-and-help-charities-in-8-steps-34j2</link>
      <guid>https://dev.to/supermetrics/how-to-organize-an-internal-bugathon-clean-up-your-backlog-and-help-charities-in-8-steps-34j2</guid>
      <description>&lt;p&gt;Bugathons can be a fun and collaborative way to clear up that backlog of issues you've been putting off. In this article, we'll share the lessons we learned from hosting a bugathon at Supermetrics and how we turned it into an opportunity to donate to charity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Identify your goals
&lt;/h2&gt;

&lt;p&gt;At Supermetrics, we have three main reasons for hosting bugathons: We wanted to give our distributed team the opportunity to work closely with each other. We also wanted to spring-clean our backlog of issues in a fun way. Finally, we wanted to donate to charity, and the bugathon was the perfect excuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Gather your team
&lt;/h2&gt;

&lt;p&gt;First, decide who will take part. Maybe you'll involve the entire engineering department, and maybe it's just for one development team. Whichever the case, we recommend blocking off a day (or a few hours at least) in the participants' calendars for the bugathon so they can focus on it fully. &lt;/p&gt;

&lt;p&gt;Bugathons are the best fun when you do them with the whole team in person. The Supermetrics Engineering team is distributed in various cities globally, but for our latest bugathon, we brought everyone together at our HQ in Helsinki. It was a great opportunity for everyone, especially our fully remote team members, to practice pair programming and work together face-to-face. &lt;/p&gt;

&lt;p&gt;Of course, you can also run successful bugathons fully remotely. That's what we did during the pandemic — it was a joyful experience, too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvhedyitro5jc6h532qd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvhedyitro5jc6h532qd.jpg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Make it feel like a fun competition
&lt;/h2&gt;

&lt;p&gt;We started our bugathon with a kick-off session where we briefly explained the objectives and rules of the event. Teams then picked a mascot and came up with a team name — both important elements in creating team spirit. &lt;/p&gt;

&lt;p&gt;Before the teams spread around the office to squash their bugs, everyone joined the bugathon Slack channel to keep tabs on the competition and banter with rivaling teams. We've also found that having a screen displaying our progress toward the bugathon's goals creates a shared feeling of accomplishment. Including a timer on the screen can also boost the sense of urgency for teams to complete their tasks. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Assign points to each story
&lt;/h2&gt;

&lt;p&gt;If you make a competition out of it, you should have a system for grading the bug-fixers' accomplishments. In our system, each story was labeled with a specific color — bronze, silver, gold, or platinum — representing a different number of points. More importantly, each label represented a specific amount of money we'd donate to charity at the end of the day — but more about that in Step 8. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Maintain quality standards
&lt;/h2&gt;

&lt;p&gt;While the goal of a Bugathon is to fix as many bugs as possible, it shouldn't mean lowering your standards for quality. In our events, each change goes through a proper code review and automated tests, as with any other code we write. And if a story turned out not to be a bug after all, its points were still counted for the team. After all, figuring out a bug isn't a bug, or figuring out a game plan for resolving a bug is almost as important as fixing the bug itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Vary the day with extra activities
&lt;/h2&gt;

&lt;p&gt;Hunting for bugs can be fun, but doing it for hours can get mind-numbing. To keep things fun and engaging, we had some extra activities throughout the day. The team had a chance to bond over a catered and take a fun trivia quiz with questions about the company, its culture, and traditions. There was a chance to earn extra points for secret achievements. Some of those achievements were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first bug of the day.&lt;/li&gt;
&lt;li&gt;More tests than actual code.&lt;/li&gt;
&lt;li&gt;The team with most stories closed.&lt;/li&gt;
&lt;li&gt;The best team work. &lt;/li&gt;
&lt;li&gt;The least new code added.&lt;/li&gt;
&lt;li&gt;The best photo taken with the team mascot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also like to have talks from experts inside and outside our team during our bugathons. In the past, we've had talks from various experts — once, we had Google coming to talk about Pub/sub — and this time, two of our own developers gave talks. One was on debugging, the other on the lessons learned by building a new software product in 70 days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Celebrate success
&lt;/h2&gt;

&lt;p&gt;At the end of the bugathon, you should gather together, count the points, crown the winners, and celebrate the shared achievements. In our latest Bugathon, we identified and resolved nearly a hundred bugs. It was a fantastic achievement with a positive impact on our products and our customers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn2k1w811zweue9f6xrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn2k1w811zweue9f6xrq.png" alt="Image description" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Donate to charity
&lt;/h2&gt;

&lt;p&gt;Last but not least, we encourage you to use bugathons as opportunities to donate to charity. You can assign the stories a monetary value based on the number of points and donate the pot to a charity of your choice. In our latest bugathon, we donated 8000 EUR to earthquake relief in Turkey and humanitarian aid in Ukraine. Not only did we have a great day of collaboration and fixing bugs, but we also positively impacted the world around us.&lt;/p&gt;

&lt;p&gt;We believe organizing bugathons is a great way to bring your development team together, clear up the backlog, and give back to the community through charitable donations. We hope these steps help you run your own bugathon, and you'll see the same positive impacts as we have.&lt;/p&gt;




&lt;p&gt;Learn more about dev life at Supermetrics: &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>The Power of Metrics-Driven Development: How to Build Better Products</title>
      <dc:creator>Evgenii Kazmiruk</dc:creator>
      <pubDate>Wed, 12 Apr 2023 10:46:13 +0000</pubDate>
      <link>https://dev.to/supermetrics/the-power-of-metrics-driven-development-how-to-build-better-products-4k1p</link>
      <guid>https://dev.to/supermetrics/the-power-of-metrics-driven-development-how-to-build-better-products-4k1p</guid>
      <description>&lt;p&gt;In today's fast-paced digital world, product development teams must move quickly to deliver products that meet the needs of their users. However, without a data-driven approach to development, it can be challenging to understand how well a product meets those needs. &lt;/p&gt;

&lt;p&gt;That is where metrics-driven development comes in. By collecting and analyzing data on user behavior and product performance, development teams can make informed decisions about how to improve their products. In this article, we'll explore the power of metrics-driven development and provide examples of how it can be used to build better products.&lt;/p&gt;

&lt;p&gt;At Supermetrics, we are in the process of adopting metrics-driven development and learning how to use it to improve our products and development processes. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xz7lkmoige6ie8ddlvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xz7lkmoige6ie8ddlvf.png" alt="Image description" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine the Power of Technical, Product and Business Metrics
&lt;/h2&gt;

&lt;p&gt;Metrics-driven development is a powerful approach to product development that emphasizes using data to make informed decisions. This method involves tracking key metrics over time to understand how a product is performing, including user engagement, retention rates, conversion rates, and more. By analyzing these metrics, development teams can gain insights into how users interact with their product and identify areas for improvement.&lt;/p&gt;

&lt;p&gt;But metrics-driven development is not limited to product metrics only. Technical metrics also play an important role in creating successful products. Technical metrics such as response time, error rates, and throughput can give insight into the performance of a product's infrastructure and identify potential bottlenecks or areas for optimization.&lt;/p&gt;

&lt;p&gt;By combining both product and technical metrics, development teams can gain a more comprehensive understanding of their product's performance. For example, suppose a product metric such as user engagement is declining. In that case, development teams can use technical metrics like response time to identify if slow performance contributes to the issue. They can improve the product metric by addressing the technical issue and ultimately enhance the user experience.&lt;/p&gt;

&lt;p&gt;In addition to product and technical metrics, business metrics are also crucial to consider. These metrics are tied to a company's overall goals and can include metrics such as revenue, customer acquisition costs, and lifetime value. By tracking these metrics, development teams can ensure that their work is aligned with the company's goals and delivers value to the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support Iteration and Continuous Improvement
&lt;/h2&gt;

&lt;p&gt;Metrics-driven development is an ongoing process that involves constantly measuring, analyzing, and iterating based on the data. It's essential to have a continuous improvement mindset when using this approach. By adopting metrics-driven development, development teams can create products that are more effective, efficient, and aligned with the needs of their users and their business.&lt;/p&gt;

&lt;p&gt;For example, let's consider a scenario where a team works on an authentication system for a mobile app. By tracking product metrics such as user engagement and technical metrics such as response time, the team can identify that the authentication system is causing frustration and delays for users. By analyzing the technical metrics, the team discovers that the authentication system is not scalable and is experiencing high latency due to database performance issues.&lt;/p&gt;

&lt;p&gt;The team can improve the authentication system's performance by addressing these technical issues, resulting in faster response times and a better user experience. They can then track the product metrics to see if the changes have positively impacted user engagement and retention rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Metrics-Driven Development
&lt;/h2&gt;

&lt;p&gt;Adopting a metrics-driven development approach can significantly benefit product managers, engineering managers, and developers alike. Collaboration between these roles is crucial in establishing a successful metrics-driven environment.&lt;/p&gt;

&lt;p&gt;Product managers play a critical role in metrics-driven development by identifying key metrics tied to the product's overall goals. They are responsible for setting measurable goals and tracking progress against these goals. Product managers should work closely with engineering managers and developers to ensure the tracked metrics are meaningful and aligned with the product's goals.&lt;/p&gt;

&lt;p&gt;Engineering managers and developers are responsible for implementing the technical infrastructure necessary to track metrics and collect data. They should work closely with product managers to ensure that the right data is being collected in an accurate and secure way.&lt;/p&gt;

&lt;p&gt;In a metrics-driven environment, data is used to inform decisions at every level of the organization, including decisions about whether to continue building a particular feature or product. If metrics show that a feature or product is not delivering the expected results, it may be time to stop investing resources and move on to something else.&lt;/p&gt;

&lt;p&gt;One of the key benefits of a metrics-driven development approach is the ability to iterate quickly. By tracking metrics and making data-driven decisions, development teams can make changes to their products more quickly and efficiently. It can be particularly valuable in industries where the speed of innovation is critical, such as in technology or startup environments.&lt;/p&gt;

&lt;p&gt;Another benefit of metrics-driven development is the ability to identify areas for improvement and optimize processes. By tracking metrics related to technical performance, engineering teams can identify potential bottlenecks and optimize their infrastructure to improve overall system performance.&lt;/p&gt;

&lt;p&gt;Overall, adopting a metrics-driven development approach can help organizations create products that are more effective, efficient, and aligned with the needs of their users and their business. Collaboration between product managers, engineering managers, and developers is critical in establishing a successful metrics-driven environment. By working together and using data to inform decisions, development teams can create products that deliver greater value to users and drive business success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtorhsy20nliqg2u6kob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtorhsy20nliqg2u6kob.png" alt="Image description" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Adopt Metrics-Driven Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1 Building a successful metrics-driven development process starts with a clear problem statement.&lt;/strong&gt; Identify the specific and actionable problem you're trying to solve and define success criteria that can be measured with metrics. These steps will give you a clear goal to work towards and help you track progress effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2 Next, it's important to identify and define the appropriate metrics.&lt;/strong&gt; The right metrics are relevant, actionable, and aligned with your business objectives.  So you need to carefully consider what data you want to collect and analyze and ensure that it yields meaningful insights into the performance of your product or project.&lt;br&gt;
When it comes to choosing your metrics, less is often more. It's better to focus on a few key metrics that are truly impactful and actionable rather than trying to measure everything. Focusing helps you avoid getting bogged down in irrelevant data and stay focused on what matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3 The next step is to use metrics to inform decisions.&lt;/strong&gt; Use your metrics to make data-driven decisions about how to improve your product or process. A/B testing and other experiments can help you test hypotheses and validate improvements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4 Remember to communicate your metrics and progress to stakeholders.&lt;/strong&gt; Use data visualization and storytelling techniques to make the data more accessible and understandable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5 Iterate based on metrics to continuously improve your product or processes.&lt;/strong&gt; Establish a feedback loop to gather input from users and incorporate it into your development process. This approach will enable you to identify areas that require enhancement and make data-driven decisions about which changes to implement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6 Finally, cultivate a metrics-driven culture by encouraging data-driven decision-making and experimentation.&lt;/strong&gt; Make sure everyone on the team understands the importance of metrics and how to use them effectively. &lt;/p&gt;

&lt;p&gt;Following these steps, you can build a metrics-driven development process that helps you make data-driven decisions, improve your product or process, and achieve your business objectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples of Metrics-Driven Development
&lt;/h2&gt;

&lt;p&gt;Let's look at a couple of real-world examples of how metrics-driven development can be used to improve products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Improving Service Reliability
&lt;/h3&gt;

&lt;p&gt;A development team was working on a trigger service responsible for initiating a process when a specific event occurred. But the service was plagued with issues related to stability and error rates, and they needed more metrics to identify and address the root causes of the problems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97a9ixydruiob2obqwbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97a9ixydruiob2obqwbr.png" alt="Image description" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve these issues, the team adopted a metrics-driven approach, starting by identifying the most critical errors and focusing on fixing them. They worked on one metric at a time, which helped the team reduce noise in the logs and address the most pressing issues. Using metrics to measure and monitor the service's performance, the team achieved a success rate  close to 99.6% after several iterations.&lt;/p&gt;

&lt;p&gt;The team then analyzed technical, product, and business metrics to estimate the costs and benefits of fixing the remaining 0.4% of issues. They determined that a simple retry mechanism addressed the remaining issues.&lt;/p&gt;

&lt;p&gt;To ensure the service's reliability, the team implemented a service-level objective (SLO) to monitor the trigger service's performance. The SLO defined the target reliability level and provided a framework for monitoring stability and error rates. The team used metrics to measure and monitor the service's performance regularly, ensuring it met the SLO's requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Improving Conversion Rates
&lt;/h3&gt;

&lt;p&gt;Another team was working on a new product that had just been launched and had only a few customers. They could only get feedback through customer demos, and they were struggling to identify the right metrics to determine whether the product was successful. &lt;/p&gt;

&lt;p&gt;Initially, they focused on tracking the number of new users, but they soon realized that the number was so low that any fluctuations would significantly affect the metric. To address this issue, the team decided to make their metrics more granular and examined the steps to which they could add metrics, and then calculate the conversions between those steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjxzqxfk3ke3tpwkcyva.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjxzqxfk3ke3tpwkcyva.png" alt="Image description" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By doing so, the team was able to gain a better understanding of how users interacted with the product and identify areas where they could improve the user experience. They focused on experimenting with the biggest conversion drops to see if they could improve the product in those areas.&lt;/p&gt;

&lt;p&gt;This example highlights the importance of adapting your metrics approach to your situation. Metrics-driven development is never a “strict” algorithm; you need to identify the right approach for gathering and comparing data in your particular use case. By doing so, you can make informed decisions that lead to more effective and successful products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3:  Avoiding wasted time and effort
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqke7nyn8mma92ns23lvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqke7nyn8mma92ns23lvv.png" alt="Image description" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Metrics-driven development can be a powerful tool for improving products, and it's essential to use it from the beginning to avoid significant problems later. Consider the case of a development team tasked with updating the authentication flow for a product. &lt;/p&gt;

&lt;p&gt;Initially, they neglected to plan for testing, success metrics, or business implications and did not clearly understand the project's potential impacts. As a result, when they released the new authentication flow to a few parts of the products and conducted trials, the results indicated that the old authentication was performing better. The team tried various attempts to identify the root cause of the problem and make changes without success.&lt;/p&gt;

&lt;p&gt;Later, the team decided to implement a metrics-driven approach, but it was too late by then. The opportunity cost of continuing to try to fix the new authentication was deemed too high, and the team ended up rolling back all the changes and shelving the project. &lt;/p&gt;

&lt;p&gt;Had the team adopted a metrics-driven approach from the start, they would have identified and measured the key metrics that would have helped them understand how the new authentication flow would perform. It would have included identifying the performance of the old flow, understanding the user flow and backend processes, and defining success metrics to measure the impact of the new authentication flow. &lt;/p&gt;

&lt;p&gt;Like this example shows, metrics-driven development is not always about taking a project to success. It’s equally important in stopping projects before they waste too much time or resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Should Consider Metrics-Driven Development
&lt;/h2&gt;

&lt;p&gt;Metrics-driven development is a methodology that emphasizes the use of data and metrics to drive product development decisions. It helps development teams make better decisions about what features to build, how to build them, and how to measure their success. Metrics-driven development involves defining metrics upfront and continuously following them throughout the product development process.&lt;/p&gt;

&lt;p&gt;There are several benefits to using metrics-driven development, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clear decision-making:&lt;/strong&gt; By defining metrics upfront, development teams can develop a clear understanding of what they want to achieve and how they will measure success. It helps them make better-informed decisions about what to build and how to build it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved collaboration:&lt;/strong&gt; Metrics-driven development encourages cross-functional collaboration and helps ensure everyone is working toward the same goals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous improvement:&lt;/strong&gt; By continuously following metrics, development teams can identify areas for improvement and make changes quickly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster feedback:&lt;/strong&gt; Metrics-driven development provides feedback quickly, allowing teams to identify and address issues before they become bigger problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data-driven decision-making:&lt;/strong&gt; Metrics-driven development provides objective data that can help guide decision-making, reducing the risk of subjective opinions or bias.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To adopt metrics-driven development, teams need to define and prioritize key metrics that align with their product goals. These metrics should be specific, measurable, achievable, relevant, and time-bound (SMART).&lt;/p&gt;

&lt;p&gt;After defining the metrics, it's important to regularly measure and track them. It involves establishing data collection systems, analyzing the data for trends, and leveraging the insights to inform decision-making.&lt;/p&gt;

&lt;p&gt;In summary, metrics-driven development is a beneficial methodology for product development teams as it encourages decision-making based on data rather than subjective opinions. Adopting this approach can enhance collaboration, facilitate informed decisions, and promote continuous product improvement.&lt;/p&gt;




&lt;p&gt;Learn more about the Engineering team at Supermetrics at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>management</category>
      <category>leadership</category>
      <category>performance</category>
    </item>
    <item>
      <title>Avoiding feature creep as a developer</title>
      <dc:creator>Niklas Lampén</dc:creator>
      <pubDate>Wed, 22 Mar 2023 07:55:46 +0000</pubDate>
      <link>https://dev.to/supermetrics/avoiding-feature-creep-as-a-developer-57l4</link>
      <guid>https://dev.to/supermetrics/avoiding-feature-creep-as-a-developer-57l4</guid>
      <description>&lt;p&gt;&lt;em&gt;TLDR: Make sure your code is extendable and do the bare minimum.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Feature creep is the excessive ongoing expansion or addition of new features in a product, especially in computer software, video games and consumer and business electronics. These extra features go beyond the basic function of the product and can result in software bloat and over-complication, rather than simple design.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Feature_creep" rel="noopener noreferrer"&gt;Wikipedia: Feature Creep&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  We've all been there
&lt;/h2&gt;

&lt;p&gt;We've all estimated an issue to be completed in a couple of hours, and days later, we're not sure when it'll be finished, but it's "almost there, just needs this one thing done". Just like yesterday and the day before that. One of the reasons why this might happen is feature creep: "I'm gonna do this one other thing too while I'm at it".&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple example
&lt;/h2&gt;

&lt;p&gt;Let's assume the requirement is "Set a value inside a multi-dimensional array using a simple &lt;a href="https://goessner.net/articles/JsonPath/index.html#e2" rel="noopener noreferrer"&gt;JSONPath&lt;/a&gt; as path to data". We'd want to use something like &lt;code&gt;$.path.to.data&lt;/code&gt; to set value of a 5 to our array. So, we'd do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$array['path']['to']['data'] = 5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem here is that we don't know the path in advance, so we need to be more creative. It's not a complex problem, and you can solve it with a simple solution like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function setArrayValue(array $array, string $path, $value): array {
    $path    = preg_replace('/^\$\./', '', $path);
    $keys    = explode('.', $path);
    $current = &amp;amp;$array;

    foreach ($keys as $key) {
        $current = &amp;amp;$current[$key];
    }

    $current = $value;

    return $current;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing this code won't take days (especially as I pretty much copy-pasted it from &lt;a href="https://stackoverflow.com/questions/13359681/how-to-set-a-deep-array-in-php" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt;). But couldn't we make it a bit better?&lt;/p&gt;

&lt;p&gt;What if the path was something more complex? JSONPath allows paths like &lt;code&gt;$.actions[action_type==video_view].foo&lt;/code&gt;. What if someone wants to do that instead? It's not included in the original requirement, but it might become handy. So, let's go ahead and implement it, right? &lt;/p&gt;

&lt;h2&gt;
  
  
  Stop!
&lt;/h2&gt;

&lt;p&gt;…and this is where you should stop. Was the more complex path  a requirement? No. It's not a "simple JSONPath". If you're unsure, ask the Product Manager for clarification. As it's not a requirement (and the PM confirmed that), you don't implement it. &lt;/p&gt;

&lt;p&gt;Instead, you ensure that such a feature could be implemented in the future if needed. Basically, you're making sure that your code is extendable. And yes, looking at the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Signature/Function" rel="noopener noreferrer"&gt;function signature&lt;/a&gt;, nothing prevents you from doing this. Your code is extendable. You're good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did you stop?
&lt;/h2&gt;

&lt;p&gt;If you chose to continue and add the more complex path, you'd be guessing future needs. If you guessed wrong and we never needed the feature, implementing it would only have wasted time and effort, and delayed the features we actually needed. Implementing the more complex logic takes more work, as does testing it.&lt;/p&gt;




&lt;p&gt;Explore engineering careers at Supermetrics at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Five success factors for building a new software product in 70 days</title>
      <dc:creator>Luís Brito</dc:creator>
      <pubDate>Tue, 07 Mar 2023 09:23:56 +0000</pubDate>
      <link>https://dev.to/supermetrics/five-success-factors-for-building-a-new-software-product-in-70-days-42j0</link>
      <guid>https://dev.to/supermetrics/five-success-factors-for-building-a-new-software-product-in-70-days-42j0</guid>
      <description>&lt;p&gt;One of our product teams recently took on the challenge of building a new product in the last quarter of 2022 in roughly 70 days. From a pivoting moment to the soft launch, the journey was filled with enthusiasm, uncertainty, and lessons learned.&lt;/p&gt;

&lt;p&gt;At the end of the quarter, our team got together in London, and we had a session to analyze the main factors that contributed to our success. The list was comprehensive, but the following five factors are what we considered the most relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set an ambitious goal early
&lt;/h2&gt;

&lt;p&gt;Our team took the chance and embraced the use of OKRs. We expected it would help our team align efforts towards specific goals. &lt;br&gt;
What was most beneficial was that adopting this framework helped us take a step back and think about what we wanted to accomplish as a team and how to measure our progress against goals.&lt;/p&gt;

&lt;p&gt;It wasn't the written OKRs per se that were the most beneficial. Sure, having that written and clear enough so everyone can easily understand them was important, but the alignment created between team members during our sessions to define those goals was really the most valuable thing.&lt;/p&gt;

&lt;p&gt;To that end, it was essential to include all team members. Everyone was involved and had the opportunity to contribute, challenge, and offer their perspectives. It helped create a shared sense of purpose and direction. After that, everyone understood what they were working towards and how their work contributed to the goals.&lt;/p&gt;

&lt;p&gt;Setting goals is important, but setting ambitious goals is even more critical. We wanted to get 10 new successfully onboarded users every week until the end of the quarter. 10 users may not seem much, but considering we didn't have a functioning product, it was an ambitious goal.&lt;/p&gt;

&lt;p&gt;If we were 90% sure we could achieve that goal, would that be a good OKR? Starting with a goal like that would mean delaying getting those new users for several weeks and losing the opportunity to learn from them.&lt;/p&gt;

&lt;p&gt;We aimed for a more ambitious goal, which meant starting the quarter with a 70% confidence level and working towards building it up.&lt;/p&gt;

&lt;p&gt;In the end, we achieved our goal. But had we finished the quarter with only 5 new users per week, would that have been a failure? Absolutely not, because that would still be better than starting with a less ambitious goal, which would probably have resulted in no users at all.&lt;/p&gt;

&lt;p&gt;Knowing that it wasn't an easy goal to reach created enough discomfort to motivate the team. It allowed us to exceed ourselves and achieve more than we thought possible. The key is to stretch the goal enough to create a healthy discomfort to trigger this response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know your customers
&lt;/h2&gt;

&lt;p&gt;We first thought about building upon an already proven concept. Why reinvent the wheel if Supermetrics proved that certain concepts work great and thousands of paying customers back that up?&lt;/p&gt;

&lt;p&gt;Because the customer was different this time.&lt;/p&gt;

&lt;p&gt;That's one tricky part of building new in a place surrounded by successful products. You feel tempted to copy past successes.&lt;br&gt;
It wasn't only the platform around the product that was different, but also the user. We were fearless in challenging the proven concepts and started actively looking for ways to validate our theory.&lt;/p&gt;

&lt;p&gt;Developing new products is like navigating a vast and unknown sea. Uncertainty is a given, and bringing clarity to the table is essential.&lt;br&gt;
&lt;em&gt;"Who are the customers?"&lt;/em&gt;, &lt;em&gt;"What features deliver value?"&lt;/em&gt;, &lt;em&gt;"How to validate our assumptions?"&lt;/em&gt;, &lt;em&gt;"What don't we know?"&lt;/em&gt; were all questions we asked ourselves.&lt;/p&gt;

&lt;p&gt;Getting to know our customers was the first step in our journey. As there were no established customer relationships for this product when we started, we needed a better idea of who our customer was and what they struggled with.&lt;/p&gt;

&lt;p&gt;We started reaching out and talking with as many people as possible. We began inside Supermetrics and asked the marketing department. We also asked colleagues if they knew anyone that could be a potential user. We asked our existing customers. And as we're working with a partner company, we asked them if they could introduce us to potential users. &lt;/p&gt;

&lt;p&gt;Asking, asking, and asking some more worked great for us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We identified that this product's users differed from the standard Supermetrics user.&lt;/li&gt;
&lt;li&gt;Our partner knew a lot about its customers and we could build on that knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, we concluded that we'd need to build the product differently than we initially thought.&lt;/p&gt;

&lt;p&gt;It's tempting to jump right into building, but misguided initial assumptions and decisions may impact the team's work for months. So, even if you're surrounded by other successful products and you're tasked with building something similar, always challenge the assumption that copying a proven concept will yield similar results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplify, simplify, simplify
&lt;/h2&gt;

&lt;p&gt;At this point, the unknown sea was less unknown. We had a better understanding of who the customer was, but there was a chance we could still make the product too complex.&lt;/p&gt;

&lt;p&gt;Keeping things simple is hard. It's easy to stray off course and start adding more features or components to make the product more attractive.&lt;/p&gt;

&lt;p&gt;We joked that our product was the onboarding, which is true. We wanted to invest our time in what really mattered to the user. For us, the path to user value needed to be the shortest possible. That required focusing on the bare minimum and challenging anything that could add complexity or require additional interactions from the user.&lt;/p&gt;

&lt;p&gt;To build simple in a simple manner is a powerful approach. This simplification mantra touches all corners of product development. It applies not just to the product but also to the way we build it. Breaking down large features into smaller ones is essential when developing software products. It helps reduce code complexity and makes it easier to maintain and update over time. Smaller features also have less code, fewer dependencies, fewer tests, and need less time to review.&lt;/p&gt;

&lt;p&gt;Another great benefit is that it's easier to fail when you're wrong, as there is less complexity to contend with. We were prepared to fail fast and iterate.&lt;/p&gt;

&lt;p&gt;It was especially during the design phase that most of the simplification occurred. When we first looked at the designs, we thought that it was fairly simple and there weren't many opportunities to make it more simple. But after carefully looking into every step, it was amazing to see how we could still simplify it.&lt;/p&gt;

&lt;p&gt;The key takeaway is that simplicity isn’t something to take for granted. While it may be tempting to rush through product development, investing extra time and effort to simplify the product can save much time in the long run and reach a better outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn by doing and avoid assumptions
&lt;/h2&gt;

&lt;p&gt;Talking about problems can only take you so far. Only by taking action and gathering real-world feedback can you truly understand what works and what doesn't. By adopting a "learn by doing" approach, our team was able to move past roadblocks and make steady progress.&lt;/p&gt;

&lt;p&gt;Building new software products comes with constraints and uncertainties, especially when it involves integrating with an external platform. We were good at finding issues but rarely made progress just talking about them. However, we progressed when we started experimenting, building a prototype, or digging into the documentation. By taking an active approach and focusing on experimentation and learning, we were able to reduce the number of assumptions before making decisions. &lt;/p&gt;

&lt;p&gt;For this to happen, fostering a culture of experimentation is essential. &lt;br&gt;
By encouraging your team to take risks and try new things, you create an environment that values innovation and creativity.&lt;br&gt;
When your team feels empowered to experiment and explore, they’re more likely to take the initiative and tackle challenging problems head-on. That can lead to new ideas, fresh perspectives, and innovative solutions you may not have considered otherwise.&lt;/p&gt;

&lt;p&gt;This approach allowed us to build momentum and make steady progress, even amid uncertainty and unknowns. We learned to trust our instincts and take calculated risks rather than being held back by fear of the unknown.&lt;/p&gt;

&lt;p&gt;Week after week, we were able to build our confidence level from 70% to 80% and then to 90%. As we approximated the end of the quarter, it became clear that we’d achieve the goal of 10 new customers per week.&lt;/p&gt;

&lt;p&gt;In the beginning, there are more assumptions in the air, and because of these unknowns confidence level is not great. We can think of this as a burn-down chart. As we progress, we "burn" these assumptions by replacing them with actual data. As we do this, we increase our confidence. It's a fun ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build trust
&lt;/h2&gt;

&lt;p&gt;Building trust is essential for any team to succeed.&lt;/p&gt;

&lt;p&gt;At Supermetrics, we take transparency very seriously. I believe transparency and trust are closely related, as transparency is one of the key ways to build and maintain trust within a team. Transparency means being open and honest in communication, sharing information freely, and avoiding hidden agendas or ulterior motives.&lt;/p&gt;

&lt;p&gt;Our team spent a lot of time together, especially in the beginning. Everybody was invited to every meeting, which helped us touch on many topics quickly. It was inefficient but necessary because building trust becomes even more essential when some of the team members are new. New team members may feel unsure or tentative in their new role and be less likely to speak up or share their ideas and concerns. It can create a barrier to effective collaboration and hinder the team's ability to achieve its goals.&lt;/p&gt;

&lt;p&gt;Trust isn’t a static state that can be maintained indefinitely without further effort. So while it was important for us to focus more on building trust in the beginning, maintaining it required all team members' ongoing effort and attention.&lt;/p&gt;

&lt;p&gt;That means constantly communicating openly and honestly. We encourage all team members to speak their minds and share their ideas and concerns. Moreover, we ensure that everyone is listening and responding constructively and respectfully.&lt;/p&gt;

&lt;p&gt;Another way to build trust is to be reliable byfollowing through on commitments and delivering on promises. It's essential to do what you say you would and be accountable when things don't go as planned.&lt;/p&gt;

&lt;p&gt;And in our team, everyone trusts each other’s skills. Everyone had opportunities to demonstrate their competence. Being together often in the beginning really helped us to count on one another's skills, expertise, and judgment. We could delegate tasks and responsibilities, knowing they would be handled competently. &lt;/p&gt;

&lt;p&gt;Lastly, being supportive and showing empathy are essential to building trust. It's important to be empathetic and supportive when team members are struggling and to celebrate their successes when things go well. Helping and being there for our team members when they face challenges or obstacles encourages collaboration and teamwork. It shows that you’re invested in the success of the team as a whole.&lt;/p&gt;




&lt;p&gt;Learn more about Supermetrics as a workplace at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting rid of anti-patterns of development work</title>
      <dc:creator>Niklas Lampén</dc:creator>
      <pubDate>Tue, 28 Feb 2023 13:27:37 +0000</pubDate>
      <link>https://dev.to/supermetrics/getting-rid-of-anti-patterns-of-development-work-3m8i</link>
      <guid>https://dev.to/supermetrics/getting-rid-of-anti-patterns-of-development-work-3m8i</guid>
      <description>&lt;p&gt;In engineering, it's normal that teams sometimes struggle to get stories/tasks/bugs done. We, too, were facing this issue, so we sat down with the tech leads and managers, and discussed the most common reasons for issues we've had in our development work.&lt;/p&gt;

&lt;p&gt;The top 6 reasons we identified are (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring code as part of another task.&lt;/li&gt;
&lt;li&gt;The story's scope is unclear.&lt;/li&gt;
&lt;li&gt;The story's scope is too wide.&lt;/li&gt;
&lt;li&gt;Implementation hasn't (really) been planned.&lt;/li&gt;
&lt;li&gt;Doing other "small" changes as part of another task.&lt;/li&gt;
&lt;li&gt;Arguing over opinions in PRs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We obviously wanted to tackle these issues and decided to lay down a few ground rules for development work. Here are the rules we agreed on:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Never refactor code as a part of another task
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why not? I can do it in this small PR while I'm doing this other feature!"&lt;/p&gt;

&lt;p&gt;– Random Developer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Refactoring changes the internals of existing code. Suppose we find a new bug after deployment. It may be hard to identify if the faulty behavior is caused by the new feature or bug fix, or by the code that was refactored simultaneously.&lt;/p&gt;

&lt;p&gt;So, when should you refactor code? Here's what we think:&lt;/p&gt;

&lt;h3&gt;
  
  
  Good reasons to refactor code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The requested change can't be done on the existing code.&lt;/li&gt;
&lt;li&gt;The existing code is very difficult and/or risky to extend.&lt;/li&gt;
&lt;li&gt;Not refactoring it will definitely cause us problems.&lt;/li&gt;
&lt;li&gt;Refactoring will provide us with business value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bad reasons to refactor code:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I don't like the code.&lt;/li&gt;
&lt;li&gt;The code is legacy.&lt;/li&gt;
&lt;li&gt;It's implementing a paradigm X, and I'd want it to use Y.&lt;/li&gt;
&lt;li&gt;I'd like to refactor.&lt;/li&gt;
&lt;li&gt;It could be written better.&lt;/li&gt;
&lt;li&gt;I don't understand the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to progress to refactoring
&lt;/h3&gt;

&lt;p&gt;We decided that refactoring must be decided together with the tech lead for a good reason and it must be a separate task. No more refactoring just because someone felt like it! Our job isn't to write perfect code but to bring value to our clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements of refactoring
&lt;/h3&gt;

&lt;p&gt;When refactoring, if the code isn't tested, you must write the tests for the to-be-refactored code before the refactoring. After refactoring, you must ensure the code gives the exact same results. The bugs and all. Really.&lt;/p&gt;

&lt;p&gt;Some other code might rely on very specific behavior of the code you're refactoring, so you must refactor it to do also the not-so-obvious stuff. Changing the behavior of the code is another task — read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Know what you're doing, part 1
&lt;/h2&gt;

&lt;p&gt;You must have a clear vision of the outcome of what you're going to be working on. If you don't really know the outcome of the story, ask for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Know what you're doing, part 2
&lt;/h2&gt;

&lt;p&gt;The story/task must be split into workable chunks (subtasks). One chunk should be one PR, and one PR should be a stand-alone thing. It can be a new helper, and the second PR will use that helper. Use feature branches as the target for your PRs related to a single issue if you need multiple PRs.&lt;/p&gt;

&lt;p&gt;Remember, you can add as many subtasks as you want to the issue you're working!&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Know what you're doing, part 3
&lt;/h2&gt;

&lt;p&gt;Have an implementation plan before you implement, and don't only have inside your own head. Talk to other people about your plan and ask for feedback. We've all been guilty of just starting to implement something because it's so simple. Have you ever ended up telling yourself "I should have realized this in advance" because of poor planning? Yeah, me too.&lt;/p&gt;

&lt;p&gt;For new features (and bugs turning into adding something new), you must plan the architecture before you start implementing it. With sound architecture, the implementation can easily be replaced.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Stay within the scope
&lt;/h2&gt;

&lt;p&gt;Avoid out-of-scope changes in your PRs. In the worst case, the out-of-scope changes require quite a bit of attention, delaying the completion of the actual task. If you're thinking, "is this inside the scope?" it likely isn't.&lt;/p&gt;

&lt;p&gt;Again, as a reviewer, you're expected to reject any PRs that are doing too much, even if it seems ok.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Know when to stop
&lt;/h2&gt;

&lt;p&gt;If the PR requires more than a couple of approval rounds, one or more of the previous "Know what you're doing" steps were likely skipped. &lt;br&gt;
Stop! Go back to the drawing board, and don't try to force the current code to be approved.&lt;/p&gt;

&lt;p&gt;As a reviewer: if the PR has so many problems that you don't know where to start reviewing, you can simply reject the PR and require additional planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra: A quick guide to reviewing a PR
&lt;/h2&gt;

&lt;p&gt;Reviewing code is one of the final quality assurance tools before the code is deployed. It's the last chance for us to pick out any issues, making it a very important part of our development process.&lt;/p&gt;

&lt;p&gt;Your task as a reviewer is to offer a second pair of eyes for the big picture, not to take responsibility over the details of the code being delivered. Even if you disagree with some of the original developer's design preferences, you should still approve the PR — as long as you agree the reviewed code is working, tested, and not going to cause problems in the foreseeable future.&lt;/p&gt;

&lt;p&gt;For example, if you disagree on naming, ensure you aren't complaining about opinions. That being said, if the naming is clearly wrong, comment on it. Also, keep in mind that the PRs must follow the coding style guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-review
&lt;/h3&gt;

&lt;p&gt;Just like you review other developers' code before approving it, you should review your own code. Self-review decreases the number of review cycles and shows consideration for other people's time and effort. It's a great opportunity to catch accidental changes, forgotten comments, typos, and more.&lt;/p&gt;

&lt;p&gt;If you take a short break before reviewing for your code, you should get better results. If you doubt any part of your own code, others will too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You'll be able to avoid quite a few pitfalls and delays by following these ground rules of development work. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Know what you're expected to achieve.&lt;/li&gt;
&lt;li&gt;Plan how you're going to get there.&lt;/li&gt;
&lt;li&gt;Stick with the plan.&lt;/li&gt;
&lt;li&gt;Refactoring is a stand-alone task.&lt;/li&gt;
&lt;li&gt;Review your own code.&lt;/li&gt;
&lt;li&gt;Be strict but fair with your PR reviews.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Learn more about Supermetrics as a workplace at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>software</category>
      <category>developer</category>
    </item>
    <item>
      <title>Benefits of a Developer Experience (DevEx) team</title>
      <dc:creator>Paweł Lewtak</dc:creator>
      <pubDate>Thu, 23 Feb 2023 13:41:26 +0000</pubDate>
      <link>https://dev.to/supermetrics/benefits-of-a-developer-experience-devex-team-gca</link>
      <guid>https://dev.to/supermetrics/benefits-of-a-developer-experience-devex-team-gca</guid>
      <description>&lt;p&gt;Developer Experience (DevEx) is a concept similar to User Experience (UX). It focuses on the developer's perspective of how easy, efficient, and enjoyable it's to work with your product.&lt;/p&gt;

&lt;p&gt;DevEx can be external or internal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;External DevEx focuses on a developer who uses your product as a consumer, by, for example, integrating with your API. &lt;/li&gt;
&lt;li&gt;Internal DevEx, which this blog post focuses on, strives to create the smoothest possible experience for the engineers who develop your product. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internal developer experience impacts the productivity of the Engineering team and how satisfied and engaged developers feel at work. It can encompass various elements, from the more technical, like clean code, good documentation, and efficient internal tooling, to more intangible things, like your engineering culture and the level of psychological safety in your team. &lt;/p&gt;

&lt;h1&gt;
  
  
  How investing in DevEx has helped us at Supermetrics
&lt;/h1&gt;

&lt;p&gt;At Supermetrics, our DevEx team focuses on the technical side of things — it develops and maintains tooling and processes that make our developers' work easier and increase their productivity. &lt;/p&gt;

&lt;p&gt;Here are some of our DevEx team’s recently closed cases to give you an idea of how they improve our tooling and processes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgraded dependencies in shared repositories.&lt;/li&gt;
&lt;li&gt;Researched and compared code quality tools to be used across teams.&lt;/li&gt;
&lt;li&gt;Upgraded Docker container images.&lt;/li&gt;
&lt;li&gt;Improved error handling in &lt;code&gt;smtool&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Improved the performance of our package managers.&lt;/li&gt;
&lt;li&gt;Drove the PHP version upgrade.&lt;/li&gt;
&lt;li&gt;Added checks for required software’s versions and warned teams if it wasn’t up to date.&lt;/li&gt;
&lt;li&gt;Shared settings for PhpStorm (configured debugger and other external tools).&lt;/li&gt;
&lt;li&gt;Automated database migrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the DevEx team owns our shared tools and scripts, developers can focus on their projects without worrying about who'll implement new features and whom to report new requests and bugs. A tight feedback loop between the DevEx team and developers allows the team to react to problems, needs, and improvement ideas quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onboarding
&lt;/h2&gt;

&lt;p&gt;A good developer experience starts with a positive first impression, which is why onboarding is a key focus area for our DevEx team. Comprehensive documentation on how to set up your development environment is essential for a new hire to get up to speed quickly. &lt;/p&gt;

&lt;p&gt;We've taken this one step further with our &lt;code&gt;install_dev.sh&lt;/code&gt; script. It installs all the required software at the correct version and helps a new developer set up their local Docker environment, Git integration, common repositories, and even IDE configuration. &lt;/p&gt;

&lt;p&gt;Talk about getting a running start! Before, it could take 1–2 days to set everything up. With the script, you'll be done in 2–3 hours at most, with little chance of human error. Considering how frequently we've welcomed new developers to our team lately, we appreciate the time saved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer environment
&lt;/h2&gt;

&lt;p&gt;Another time saver is when you can easily replicate whatever you do in a production environment in any other environment, including local. It means using a similar setup, the same software version (HTTP server, PHP, database), and the same version of packages. The DevEx team helps ensure that the local environment mirrors the production one, resulting in fewer discrepancies between the two and fewer bugs.&lt;/p&gt;

&lt;p&gt;The DevEx team has built a handy tool that our developers use daily to manage their development environment — the &lt;code&gt;smtool&lt;/code&gt;. It works for both macOS and Linux, automates the running and updating of Docker containers, and keeps repositories and IDE up to date. It started as a collection of Bash scripts, but we're now migrating them to Go. &lt;/p&gt;

&lt;p&gt;My personal favorite is the &lt;code&gt;smtool check&lt;/code&gt; command. It checks if the required software is up to date and all required shared repositories and local configuration are in place. It's the first command I use to look for the cause of issues with the local developer environment. Most common issues are handled automatically. Whether that's outdated software, lack of configuration or credentials, &lt;code&gt;smtool&lt;/code&gt; will offer to solve it right away. &lt;/p&gt;

&lt;h3&gt;
  
  
  3rd party software and packages
&lt;/h3&gt;

&lt;p&gt;Our &lt;a href="https://dev.to/supermetrics/meet-our-core-engineering-group-1il1"&gt;Core Engineering Group&lt;/a&gt; tends to be the source of most packages that are used in most projects. By using &lt;a href="https://packagist.com" rel="noopener noreferrer"&gt;Private Packagist&lt;/a&gt; and &lt;a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt; we can handle them easily and ensure up to date versions are used. &lt;/p&gt;

&lt;p&gt;New vulnerabilities are discovered every week, and new versions of popular packages are released. Keeping them all up to date is tedious work that takes time. With some automation, it's only a matter of a manual review and approval before changes are made. That's because Dependabot is a software that automatically looks for possible updates and prepares a PR with an update to the latest available version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling and documentation
&lt;/h2&gt;

&lt;p&gt;Meaningful error messages in internal tooling are one way of making developers’ lives easier. Whenever &lt;code&gt;smtool&lt;/code&gt; fails to do something, it tries to come up with a helpful error message. It links to a troubleshooting guide, prints a help page, or asks for credentials to set up missing configuration. This way, you don't have to type in or copy-paste long and complex commands. &lt;/p&gt;

&lt;p&gt;If all else fails, we aim for logs to be useful enough to identify bugs quickly.&lt;/p&gt;

&lt;p&gt;It's a crucial thing to have documentation for common issues. We tend to test our internal software and document any potential issues we find that can’t be automatically solved. That way our developers get a troubleshooting guide that’s helpful when we’re doing a backward incompatible change (like new PHP or database version).&lt;/p&gt;

&lt;p&gt;Documentation is also a place where we keep good practices documented and a common coding style we’d like to follow.&lt;/p&gt;

&lt;h1&gt;
  
  
  Investing in DevEx is paying off for us
&lt;/h1&gt;

&lt;p&gt;The desired outcome of devoting time and resources to improving Developer Experience is an engineering organization that works comfortably with the local environment and handles tasks efficiently. If working with the code or the environment causes frustration, confusion, or fatigue, it might be a sign of a bad Developer Experience.&lt;/p&gt;

&lt;p&gt;We're only at the beginning of our DevEx journey, but the benefits to our developers' happiness and productivity have been encouraging. We're in it for the long run and excited to see where further emphasis on a good Developer Experience will take us! &lt;/p&gt;

&lt;p&gt;If you’re interested in diving deeper into DevEx, here are our favorite resources on the topic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://microsoft.github.io/code-with-engineering-playbook/developer-experience/" rel="noopener noreferrer"&gt;https://microsoft.github.io/code-with-engineering-playbook/developer-experience/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/architect/developer-experience" rel="noopener noreferrer"&gt;https://www.redhat.com/architect/developer-experience&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developerexperience.io/articles/good-developer-experience" rel="noopener noreferrer"&gt;https://developerexperience.io/articles/good-developer-experience&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Learn more about Supermetrics as a workplace at &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tooling</category>
      <category>productivity</category>
      <category>devex</category>
    </item>
    <item>
      <title>Meet the Supermetrics Engineering Team</title>
      <dc:creator>Paweł Lewtak</dc:creator>
      <pubDate>Fri, 27 Jan 2023 13:21:31 +0000</pubDate>
      <link>https://dev.to/supermetrics/meet-the-supermetrics-engineering-teams-5b17</link>
      <guid>https://dev.to/supermetrics/meet-the-supermetrics-engineering-teams-5b17</guid>
      <description>&lt;p&gt;The Supermetrics products help businesses streamline their marketing data from siloed sales and marketing platforms into their go-to reporting, analytics, and warehousing tools — whether that's a spreadsheet, a data visualization tool, a data lake, or a data warehouse.&lt;/p&gt;

&lt;p&gt;It sounds simple, but when you factor in the scale we operate on, it isn’t. Around fifteen percent of global advertising spend is reported through our products. Every month, we transfer about 2.5 petabytes of marketing data from more than a hundred sources through billions of queries.&lt;/p&gt;

&lt;p&gt;It's a team effort to make all that happen. In this blog post, we'll look at how the Product Engineering organization is set up at Supermetrics, how it works, and which product areas each team owns.&lt;/p&gt;

&lt;h2&gt;
  
  
  100+ engineers, five engineering groups
&lt;/h2&gt;

&lt;p&gt;In 2022, we introduced a new organizational structure with engineering groups. Previously, we had many small and separate teams, but now we're more structured, and each team has a clear set of responsibilities and product areas to focus on.&lt;/p&gt;

&lt;p&gt;Currently, our teams are divided into five engineering groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Core Group&lt;/strong&gt; owns our backend foundation.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Hub Group&lt;/strong&gt; owns our new Supermetrics Hub product.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Destinations Group&lt;/strong&gt; builds products that transfer data to destinations like Google Sheets, Looker Studio, and data warehousing.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Connectors Group&lt;/strong&gt; develops third-party API integrations for data fetching.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Infrastructure Group&lt;/strong&gt; supports the rest of the engineering organization and makes their work flow more smoothly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of these groups, we have several specialist teams servicing the entire organization, including Security, Developer Experience, Testing, and Architecture.&lt;/p&gt;

&lt;p&gt;Each development team consists of people with various roles working together. Software Engineers and their Engineering Managers are paired with a dedicated Product Manager, and experts from fields like Test Automation, DevOps, and Product Design are on hand when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we work together
&lt;/h2&gt;

&lt;p&gt;Each team has the autonomy to choose its ways of working. We share some working patterns to make internal communication and alignment easier and avoid siloing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two-week sprints with retro meetings and daily standups either as a meeting or via Slack.&lt;/li&gt;
&lt;li&gt;We're agile and combine useful features from Scrum and Kanban to manage stories.&lt;/li&gt;
&lt;li&gt;Jira is our tool of choice for keeping tabs on tasks, epics, and the like. We plan each epic and discuss these plans across the organization to keep everyone on the same page.&lt;/li&gt;
&lt;li&gt;We prefer more communication over less. As the engineering organization grows and our products become more complex, teams must communicate with each other to avoid silos. We have an all-hands dev channel on Slack for discussing general technology issues and ideas.&lt;/li&gt;
&lt;li&gt;While different teams are for different things, all engineers at Supermetrics can contribute anywhere in the codebase. If you see a bug on another team's domain, you're welcome to submit a pull request and get the appropriate team member to review it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What technologies we use
&lt;/h2&gt;

&lt;p&gt;Most of our backend code uses PHP, MySQL, and Redis. You might see some Go, Node.js, or Python, but that's rare. We use these technologies locally in Docker containers. Our staging and production rely fully on cloud services provided by Google and Amazon.&lt;/p&gt;

&lt;p&gt;Our frontend is based mostly on React, TypeScript, and Google App Script. Some projects still use Vue.js. We use REST APIs for communication.&lt;/p&gt;

&lt;p&gt;Without going into further details, here's a short list of other tools we use in our daily work: Jira, Jenkins, GitHub, PHPStan, Psalm, Jest, PubSub, PHPUnit, and Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing our engineering groups and teams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Group
&lt;/h3&gt;

&lt;p&gt;If the Supermetrics Engineering team is the Solar System, the Core Group is The Sun, and the product teams revolve around it. The Core Group is responsible for the backend foundation on which all Supermetrics products are built. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Core team&lt;/strong&gt; maintains our custom framework, fixing bugs and optimizing the code for better performance. The team owns the backend platform and provides services like data aggregation to all the other Engineering teams within Supermetrics. They also create reusable components for all our products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hub Group
&lt;/h3&gt;

&lt;p&gt;Supermetrics Hub is a new key product for us. It’s the centralized interface where customers interact with a wide variety of different services, like managing, transforming, and exporting their data. They can also manage subscriptions, user accounts, and billing in the Hub.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Hub Products team&lt;/strong&gt; builds core features like data blending, data transformation, and data source connections, which are available for all Supermetrics products. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Hub Workflow team&lt;/strong&gt; focuses on developing tools for customer engagement, onboarding, and support. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Platform Growth team&lt;/strong&gt; owns the purchasing flow and the licensing system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connectors Group
&lt;/h3&gt;

&lt;p&gt;A connector is an API integration that fetches data from various sources and processes it to match the user's request before sending it to a destination. The Connectors Group teams are responsible for these third-party API integrations and fetching data from those sources. The Connectors Group teams rely heavily on code designed by the Core Group.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Connectors Engineering team&lt;/strong&gt; mostly maintains and migrates APIs within existing coded connectors. We currently have over a hundred connectors, most of which are still coded. It means they're written using actual code (as opposed to Codeless Connectors, which we'll discuss next) and need to be maintained and adjusted as the user base grows.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Connector Platform team&lt;/strong&gt; builds Codeless Connectors — a new technology that allows us to build new connectors faster, not with code but with configuration. You can read more about this concept in our previously published &lt;a href="https://dev.to/supermetrics/accelerating-api-integration-development-with-codeless-connectors-1bb4"&gt;tech blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Connector Specialist team&lt;/strong&gt; uses the no-code configuration wizard built by the Connector Platform team to configure new Codeless Connectors. The Connector Specialists don't have to have coding skills. They only need to understand the API documentation and our configuration schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product Destination Group
&lt;/h3&gt;

&lt;p&gt;As its name suggests, the Product Destination Group focuses on building and maintaining the data destinations Supermetrics supports. We partner with and build functionality on top of widely used data analysis, visualization, and storage applications, like Microsoft Excel, Looker Studio, and Snowflake.&lt;/p&gt;

&lt;p&gt;The user workflow is very similar regardless of destination. You connect to one or more data sources, select the metrics and dimensions you want to pull into the destination application, configure things like date range and filters, and then click to request data. But since all the destination applications are unique, the Product Destination Group's development teams are specialized to work with specific destinations.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Google Sheets team&lt;/strong&gt; develops our add-on for Google Sheets. If you use Google Sheets to gather stats from different sources, this product can save a ton of time, so you can focus on making sense of raw data. Recent releases include inline data refresh and triggers for refreshing data in set intervals.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Excel/PowerBI team&lt;/strong&gt; builds an add-on for Microsoft Excel. While the user experience might feel similar to what we do for Google Sheets, the code base is entirely different. The Excel add-on requires the Microsoft environment to run, while Google Sheets uses Google Apps Script. A part of this team also develops connectors for Microsoft's Power BI data visualization tool.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Looker Studio team&lt;/strong&gt; (formerly Google Data Studio) owns our extension to Looker Studio, which marketers use to create customizable visual reports and dashboards.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;DWH and API Products team&lt;/strong&gt; works with huge volumes of data. Supermetrics supports various data warehousing and data lake platforms, like BigQuery, Snowflake, Google Cloud Storage, and Amazon S3. Users can create their own schemas to pull specific data from multiple sources and automate transfers to their preferred data storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure Group
&lt;/h3&gt;

&lt;p&gt;The Groups and teams mentioned above focus on their product areas. The Infrastructure Group, on the other hand, operates cross-functionally and supports the work of other engineering groups and teams. These teams are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Developer Experience&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plus: Team Labs
&lt;/h3&gt;

&lt;p&gt;Team Labs is a scrappy startup-like team within our Engineering organization. It takes on new product ideas and organizes itself around greenfield projects. At the moment, the team builds our monday.com product and focuses on new product ideas like how our product is enhanced by generative AI and machine learning features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our technology and engineering organization have expanded over time
&lt;/h2&gt;

&lt;p&gt;Each Group and team offers a unique perspective and distinct technical challenges. What they have in common is that engineers can truly influence not just their own work but the evolution of the Supermetrics products and their technical implementation. All our teams are also made up of wildly talented and helpful colleagues.&lt;/p&gt;




&lt;p&gt;We continue to grow and are looking for talented people to join our Engineering team. Explore career opportunities on our &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;Engineering careers page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>workflow</category>
      <category>development</category>
    </item>
    <item>
      <title>Optimizing JSONPath lookups in PHP to extract data up to 7 times faster</title>
      <dc:creator>Viktor Djupsjöbacka</dc:creator>
      <pubDate>Mon, 14 Nov 2022 13:00:00 +0000</pubDate>
      <link>https://dev.to/supermetrics/optimizing-jsonpath-lookups-in-php-to-extract-data-up-to-7-times-faster-2oi0</link>
      <guid>https://dev.to/supermetrics/optimizing-jsonpath-lookups-in-php-to-extract-data-up-to-7-times-faster-2oi0</guid>
      <description>&lt;p&gt;Supermetrics is a SaaS power tool for transferring and integrating marketing data, so much of what our development teams do is about data retrieval and processing. &lt;/p&gt;

&lt;p&gt;For example, if our customer wants to fetch the day-level engagement data for their five Facebook Pages from the last six months, we’ll likely make dozens, if not hundreds, of Facebook API calls. We then parse the API responses, traverse the result sets, and aggregate, sort, and package everything neatly into the format in which the client application, like Google Sheets, wants to receive the data.&lt;/p&gt;

&lt;p&gt;During all this processing, we perform many data checks and lookups to make sure that the API responses are successful and that we catch any errors.&lt;/p&gt;

&lt;p&gt;In this blog post, I’ll explain how we optimized the performance of JSONPath lookups for faster data extraction with an open-source PHP extension. &lt;/p&gt;

&lt;h1&gt;
  
  
  Enter JSONPath notation to make the handling of API responses more readable and maintainable
&lt;/h1&gt;

&lt;p&gt;We use the convenient &lt;a href="https://goessner.net/articles/JsonPath/" rel="noopener noreferrer"&gt;JSONPath notation&lt;/a&gt; in our PHP code for all these checks and lookups. A simplified version of a response handler that processes responses from API calls could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getResultRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;JsonResponse&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;ResultRowCollection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'$.error'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ErrorResponseException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'$.error.message'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'$.resultRows'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NoResultRowsException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResultRowCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'$.resultRows'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, when we process each result row, we might want to pick only specific properties from a nested structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getFieldsFromRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ResultRow&lt;/span&gt; &lt;span class="nv"&gt;$resultRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$fieldMap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fieldMap&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$jsonPathExpression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$resultRow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$jsonPathExpression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$fieldMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'customerName'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'$.customer.name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'productName'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'$.product.name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'productPrice'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'$.product.price.total'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resultRows&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$resultRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$resultRows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFieldsFromRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resultRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fieldMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could achieve all this perfectly well without using JSONPath — especially simplified examples like the above. But with deeper structures and more advanced lookups, the native PHP code often becomes much more verbose. The JSONPath notation gives nice syntactic sugar, making the code easier to read and maintain.&lt;/p&gt;

&lt;h1&gt;
  
  
  Forced use of a PHP library increases processing time
&lt;/h1&gt;

&lt;p&gt;But nice things often come at a cost. PHP doesn’t include native support for JSONPath, so we’ve been using a PHP library to support JSONPath lookups. Unsurprisingly, using a PHP library to perform these lookups adds some performance overhead compared to writing the equivalent lookups in native PHP code. &lt;/p&gt;

&lt;p&gt;In the grand scheme of things, the overhead is only a small fraction of the overall processing time in our requests, but I found it to be a decent target for some performance optimization. When thousands of requests are made and responses are big enough, processing time can add up quickly. In edge cases, it could even lead to being a bottleneck.&lt;/p&gt;

&lt;h1&gt;
  
  
  Alternative solution: JsonPath-PHP extension
&lt;/h1&gt;

&lt;p&gt;I decided to look for other JSONPath solutions with two criteria in mind. Any alternative solution would have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be a drop-in replacement for our current solution. I didn’t want to switch to something so different from our current solution that it would require us to rewrite a lot of code.&lt;/li&gt;
&lt;li&gt;Be significantly faster than our current solution. I mean, performance optimization was the whole reason to consider other solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One project, in particular, caught my attention. &lt;a href="https://github.com/mkaminski1988/jsonpath" rel="noopener noreferrer"&gt;JsonPath-PHP&lt;/a&gt; wasn’t a PHP library like most other potential alternatives. Instead, it implemented JSONPath support as a PHP extension. JsonPath-PHP looked very promising in terms of performance, so I decided to give it a try.&lt;/p&gt;

&lt;p&gt;And indeed, the extension filled my criteria. The improvement in processing speed was significant enough to convince me to go forward with this solution. There was only one problem — the project had not received any updates in over four years.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://github.com/mkaminski1988/jsonpath/issues/28" rel="noopener noreferrer"&gt;reached out&lt;/a&gt; to &lt;strong&gt;Mike Kaminski&lt;/strong&gt;, the creator of the project, and asked if he’d be fine with Supermetrics taking over maintenance of the JsonPath-PHP extension. Mike didn’t have an issue with it, so he transferred the ownership of the &lt;a href="https://github.com/supermetrics-public/pecl-jsonpath" rel="noopener noreferrer"&gt;PHP extension&lt;/a&gt; to Supermetrics.&lt;/p&gt;

&lt;p&gt;While I was pruning the extension code (👋 goodbye PHP 5.6 support!), Mike suddenly started to submit pull requests. First small bug fixes, then more major refactoring of the extension internals. Mike said he was inspired by the revival of the project and our real-world use of the extension, which spurred him to implement some improvements he had planned many years ago. &lt;/p&gt;

&lt;p&gt;No one was happier about it than me! We continued to exchange ideas, planned the next steps, and split the work between us. There’s a lot of discussion on social media about toxicity in the open source community, and I’ve seen some of it myself. That makes me truly appreciate the way our project is panning out.&lt;/p&gt;

&lt;p&gt;Fast-forward a couple of months, and the JsonPath-PHP extension now has an almost entirely rewritten engine, 95% code coverage, a robust CI pipeline, and &lt;a href="https://pecl.php.net/package/jsonpath" rel="noopener noreferrer"&gt;installation through PECL&lt;/a&gt;. The first ship-it, ready for production use.&lt;/p&gt;

&lt;h1&gt;
  
  
  The extension beats libraries in processing speed
&lt;/h1&gt;

&lt;p&gt;Circling back to where we started, the main goal was to improve performance for JSONPath lookups. So, how does the extension fare against the alternatives?&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://github.com/supermetrics-public/jsonpath-benchmark" rel="noopener noreferrer"&gt;benchmark suite&lt;/a&gt;, where we compare the JsonPath-PHP extension to various JSONPath libraries, JsonPath-PHP is between two and seven times faster than the fastest PHP library. The differences are biggest with small datasets, but still significant with larger ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha849pxglwb756dsvh5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha849pxglwb756dsvh5f.png" alt="Graph showing how JsonPath-PHP is between two and seven times faster than the fastest PHP library" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compared to writing the equivalent (but more verbose) code in native PHP without the JSONPath notation, the JsonPath-PHP extension usually still loses in terms of performance. With small datasets (just a few rows), native PHP is about two times faster than the extension. With big datasets (tens of thousands of rows), the performance of the extension is even slightly better than the native PHP equivalent.&lt;/p&gt;

&lt;p&gt;At Supermetrics, we like to keep our code clean, readable, and maintainable. Even if we lose some processing speed with the JsonPath-PHP extension compared to native PHP, it’s the lesser evil compared to dealing with tangled API call responses in code. And compared to the PHP libraries we’ve used before to do the same job, the extension performs significantly better.&lt;/p&gt;




&lt;p&gt;If you want to try out the JsonPath-PHP extension in your application, head over to the &lt;a href="https://github.com/supermetrics-public/pecl-jsonpath" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for instructions on how to install and use the extension. We’d love to hear your feedback! Contributions are obviously welcome too.&lt;/p&gt;

&lt;p&gt;And if you’d like to explore developer jobs at Supermetrics, visit &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;our Engineering careers page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>opensource</category>
      <category>jsonpath</category>
      <category>api</category>
    </item>
  </channel>
</rss>
