<?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>Forem: Niklas Lampén</title>
    <description>The latest articles on Forem by Niklas Lampén (@niklam).</description>
    <link>https://forem.com/niklam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F501426%2Fd184c3c5-93da-47bc-8f98-f588e2915de5.jpeg</url>
      <title>Forem: Niklas Lampén</title>
      <link>https://forem.com/niklam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/niklam"/>
    <language>en</language>
    <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://forem.com/supermetrics/interfaces-and-exceptions-29fo</link>
      <guid>https://forem.com/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>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://forem.com/supermetrics/avoiding-feature-creep-as-a-developer-57l4</link>
      <guid>https://forem.com/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>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://forem.com/supermetrics/getting-rid-of-anti-patterns-of-development-work-3m8i</link>
      <guid>https://forem.com/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>
  </channel>
</rss>
