<?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: Kristijan Kanalaš</title>
    <description>The latest articles on DEV Community by Kristijan Kanalaš (@kristijankanalas).</description>
    <link>https://dev.to/kristijankanalas</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%2F197303%2F5a7e59e8-8e5f-4a53-ab7b-f4ded9118663.png</url>
      <title>DEV Community: Kristijan Kanalaš</title>
      <link>https://dev.to/kristijankanalas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kristijankanalas"/>
    <language>en</language>
    <item>
      <title>Modernizing Emails: Innovations for Efficient Handling in Distributed Systems</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Tue, 16 Jul 2024 22:29:58 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/modernizing-emails-innovations-for-efficient-handling-in-distributed-systems-47g</link>
      <guid>https://dev.to/kristijankanalas/modernizing-emails-innovations-for-efficient-handling-in-distributed-systems-47g</guid>
      <description>&lt;p&gt;Since the beginning of digital businesses, one of the main channels of communication between the business and a client was, and still is, email. Email is a technology that is over 50 years old, and that's one of the reasons for the wide variety of mail clients that render HTML differently from each other — Outlook, I'm looking at you here 👀&lt;/p&gt;

&lt;p&gt;Aside from that problem, there is one more: how do we handle sending emails in big distributed systems?!&lt;/p&gt;

&lt;h2&gt;
  
  
  So... Here is how we modernized emails
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmky0qq6j4tdq0n5ch8he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmky0qq6j4tdq0n5ch8he.png" alt="Architecture" width="682" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As is customary nowadays for distributed systems, especially for microservices, we are using a message broker (in our case, RabbitMQ). The message broker handles the communication between multiple applications that want to send an email and an email service that is responsible for rendering and scheduling the email. Here is an example of a message that is being passed through the message broker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"toEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"to@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test Subject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"templateName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"templateData"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attachments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/test/path/file.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"original_filename.pdf"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/test/path/file2.pdf"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"from@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"From Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"replyToName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reply-to Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"replyToEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"replyto@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"toName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"To Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"cc1@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"cc2@example.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bcc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"bcc1@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"bcc2@example.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-TEST-HEADER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test Value"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"locale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"transportType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable rapid integration and keep the above message standardized between applications we made an SDK that contains configuration and DTOs.&lt;/p&gt;

&lt;p&gt;Aside from the email properties you likely recognized in the message, you can see &lt;code&gt;emailTemplate&lt;/code&gt; and &lt;code&gt;emailData&lt;/code&gt;. These two properties allow us to control which template in the email service will be rendered and with what data it will be rendered.&lt;/p&gt;

&lt;p&gt;The email service is built with Symfony and its templating engine, Twig. Templates are written in &lt;a href="https://mjml.io/" rel="noopener noreferrer"&gt;MJML&lt;/a&gt; markup, an email framework that helps us address the first problem we mentioned. Twig allows us to pass the data to the MJML template without breaking the MJML structure. After the data is rendered, we send the result to a small MJML service that converts the MJML markup to HTML. Here is an example of a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s1"&gt;'mail/layout/main.mjml.twig'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;header&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;"mail/common/header.mjml.twig"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mj-section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;mj-column&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;mj-text&lt;/span&gt; &lt;span class="na"&gt;font-weight=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;font-size=&lt;/span&gt;&lt;span class="s"&gt;"24px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
                    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'greeting'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;trans&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
                &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/mj-text&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;mj-spacer&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"16px"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;mj-text&lt;/span&gt; &lt;span class="na"&gt;font-weight=&lt;/span&gt;&lt;span class="s"&gt;"700"&lt;/span&gt; &lt;span class="na"&gt;font-size=&lt;/span&gt;&lt;span class="s"&gt;"24px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'intro'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;trans&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/mj-text&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/mj-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/mj-section&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Main text --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mj-section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;mj-column&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;mj-text&amp;gt;&lt;/span&gt;
                &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'dataLabel'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;trans&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/mj-text&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;mj-text&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;/mj-text&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/mj-column&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/mj-section&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;contact&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;"mail/common/contact.mjml.twig"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;footer&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;"mail/common/footer.mjml.twig"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We extend a layout that has defined blocks and fill those blocks with either common reusable parts or new type of content.&lt;/p&gt;

&lt;p&gt;After the email is rendered, all that is left is to send it to a transport system, and voilà, we have an email that is beautifully rendered in most common email clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Five years ago, we were constantly struggling with synchronizing our layouts across multiple applications, styling emails, and dealing with them randomly breaking in different email clients. But having had this system in place for the last five years, creating emails has become not only manageable but actually kind of fun. We've turned a major headache into a streamlined process, and it's rewarding to see our emails looking great in every inbox — even Outlook 😄&lt;/p&gt;

&lt;p&gt;If you'd like to support me writing feel free to buy me a cup of coffee.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/wSd4q6U" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xaeAVXvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-blue.png" alt="BuyMeCoffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>architecture</category>
      <category>microservices</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Gitlab CI/CD for npm packages</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Wed, 21 Jul 2021 14:56:37 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/gitlab-ci-cd-for-npm-packages-4ncj</link>
      <guid>https://dev.to/kristijankanalas/gitlab-ci-cd-for-npm-packages-4ncj</guid>
      <description>&lt;p&gt;A couple of weeks ago the IT team in my company talked about having repositories for the packages we make for our PHP applications so we can switch to a more natural use of composer. We left the meeting with ideas but not with a concrete solution nor a promise to research this topic.&lt;br&gt;
Few days ago I needed to make a javascript package, after creating a repository on our gitlab I noticed an option for &lt;code&gt;Packages &amp;amp; Registries&lt;/code&gt;. As it blew my mind that such an option exists I decided to research it a little and use it for this javascript package if possible. &lt;br&gt;
Here is what I learned in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options
&lt;/h2&gt;

&lt;p&gt;Gitlab offers a few registries you can work with: Composer, Conan, Maven, NPM, NuGet, PyPi. I have only tried out the NPM registry, but others should also be easy to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing an NPM package to registry
&lt;/h2&gt;

&lt;p&gt;This was actually my first time making an NPM package. So I would like to recommend this post &lt;a href="https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c" rel="noopener noreferrer"&gt;Step by Step building and publishing an NPM typescript package&lt;/a&gt; to the first timers like me. It was very easy to understand and no steps were missed.&lt;/p&gt;

&lt;p&gt;First of all in your &lt;code&gt;package.json&lt;/code&gt; you should scope your project cause Gitlab requires packages to be scoped.&lt;br&gt;
For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@scope/example-package-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;After we have this setup, if we use a &lt;code&gt;.npmrc&lt;/code&gt; file or &lt;code&gt;npm config set registry&lt;/code&gt; we can tell npm where we want it to publish our package. Looks something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

//gitlab.example.com/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken=${GITLAB_DEPLOY_TOKEN}


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

&lt;/div&gt;

&lt;p&gt;If the repository is set to internal or private you need to use a Gitlab deploy token. On how to get one, you can read at &lt;a href="https://docs.gitlab.com/ee/user/project/deploy_tokens/" rel="noopener noreferrer"&gt;Deploy tokens&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;After running &lt;code&gt;npm publish&lt;/code&gt; you should be able to see your package in the registry of your repository.&lt;/p&gt;

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

&lt;p&gt;And you should be able to see a version 1.0.0 that says it was pushed manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD
&lt;/h2&gt;

&lt;p&gt;To make our life and the life of our colleagues better we can make good use of the gitlabs CI/CD system here.&lt;br&gt;
We can use &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; configuration that looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tags&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-cache&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lib/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.npmrc&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}"&amp;gt;.npmrc&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run build&lt;/span&gt;
&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tags&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-cache&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lib/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.npmrc&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run test&lt;/span&gt;
&lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tags&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-cache&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lib/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.npmrc&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run lint&lt;/span&gt;

&lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tags&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-cache&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lib/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.npmrc&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm version --no-git-tag-version ${CI_COMMIT_TAG}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm publish&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Notable points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In build stage we make a &lt;code&gt;.npmrc&lt;/code&gt; file that contains the path of the registry made by using the CI environment variables&lt;/li&gt;
&lt;li&gt;All the stages run only on &lt;strong&gt;tags&lt;/strong&gt;, a special way to tell the CI/CD system to only activate when you tag the code in your repository&lt;/li&gt;
&lt;li&gt;We build a cache for node_modules, lib and .npmrc as such we limit the number of scripts we need to run after the build step&lt;/li&gt;
&lt;li&gt;Only the build step makes the cache others only use it, it is defined by push/pull policy&lt;/li&gt;
&lt;li&gt;In publish stage we use a &lt;code&gt;npm version --no-git-tag-version ${CI_COMMIT_TAG}&lt;/code&gt; command. &lt;code&gt;npm version&lt;/code&gt; is a noisy command that tags and commits code if it detects a directory being a git repository so that's why we use &lt;code&gt;--no-git-tag-version&lt;/code&gt; here. As the stage was triggered by us tagging the code, we have the &lt;code&gt;${CI_COMMIT_TAG}&lt;/code&gt; environment variable available to use for package versioning. After that we just publish the package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
I didn't have a gitlab runner that was setup to use docker normally nor did I have node and npm installed on the machine so I had to use &lt;code&gt;docker run&lt;/code&gt; commands like shown. So... not the most elegant way of doing it.&lt;/p&gt;

&lt;p&gt;The end result is this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jbx68m5ugb238na0l9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jbx68m5ugb238na0l9a.png" alt="Pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the developers don't have to run any scripts locally, just to commit to the repository and tag the code.&lt;/p&gt;

&lt;p&gt;If you'd like to support me writing feel free to buy me a cup of coffee.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/wSd4q6U" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-blue.png"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>npm</category>
      <category>git</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>Why you shouldn't cascade functionality</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Sat, 28 Nov 2020 02:55:27 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/don-t-cascade-functionality-4mp8</link>
      <guid>https://dev.to/kristijankanalas/don-t-cascade-functionality-4mp8</guid>
      <description>&lt;p&gt;Don't do it! I'm serious, it will come back and bite you in the ass. 😆&lt;/p&gt;

&lt;h1&gt;
  
  
  So here is what I did
&lt;/h1&gt;

&lt;p&gt;I was working on a microservice that should extract images that have faces in them from PDF files. I used Symfony and as such I made a few services that were responsible for their part of the job. For example, &lt;code&gt;ImageExtractionService&lt;/code&gt; or &lt;code&gt;FaceDetectionService&lt;/code&gt;.&lt;br&gt;
I was pretty proud of the finished project, using some open source services to detect faces in pictures and choosing what program to use to extract images from PDF files, that I forgot to think ahead about code reusability and overall code quality.&lt;/p&gt;
&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;I cascaded the functionality between the services. It looked 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="c1"&gt;//ImageExtractionService&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;faceDetactionService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;//FaceDetectionService&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//some code&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;imageService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//ImageService&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//some code&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;cleanupService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cleanUp&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;Chaining it like this you end up making unwanted connections, for example if I was only interested in what FaceDetectionService "saw" I couldn't obtain that data because it doesn't return that data instead it immediately tries to process the image.&lt;/p&gt;

&lt;h1&gt;
  
  
  How did it come to bite me in the ass?
&lt;/h1&gt;

&lt;p&gt;Well, few days after the feature went to production I noticed a need for a service debug option. But because my code is chained and &lt;strong&gt;needs&lt;/strong&gt; a link to download a PDF and &lt;strong&gt;needs&lt;/strong&gt; a link where to upload the images I just couldn't make a local debug unless I truly separated the services.&lt;/p&gt;

&lt;h1&gt;
  
  
  So... Don't do it! Don't chain the functionality of separate services.
&lt;/h1&gt;

&lt;p&gt;If you'd like to support me writing future posts feel free to buy me a cup of coffee.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/wSd4q6U" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xaeAVXvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-blue.png" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>codenewbie</category>
      <category>php</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How can competitions help you improve your skills</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Fri, 03 Jul 2020 17:28:04 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/how-can-competitions-help-you-improve-your-skills-39bb</link>
      <guid>https://dev.to/kristijankanalas/how-can-competitions-help-you-improve-your-skills-39bb</guid>
      <description>&lt;p&gt;&lt;strong&gt;A quick note before we begin:&lt;/strong&gt;&lt;br&gt;
I started writing this post long before 2020 was a thing. So even tho in this post I talk a lot about offline competitions, at this time I highly recommend online competitions. If you do enter an offline competition, please be sure to keep social distancing and to follow your country's guidelines regarding the current situation.&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Every software developer was a beginner once, some people at a very young age, some people at an age of 50.&lt;br&gt;
It doesn't matter how old you are, if you are self-taught or computer science student, healthy competition can boost your developer skills. If you'd like to broaden your knowledge, gain experience or make sure that programming is for you, read on.&lt;/p&gt;

&lt;h1&gt;
  
  
  About competitions
&lt;/h1&gt;

&lt;p&gt;There are many types of competitions, the ones that I enjoy being a part of are offline 24 hours team &lt;a href="https://en.wikipedia.org/wiki/Hackathon" rel="noopener noreferrer"&gt;hackathons&lt;/a&gt;. As you can see by that elaborate wording there are different kinds of competitions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offline or online&lt;/li&gt;
&lt;li&gt;Variable duration, spanning from 2 hours to a month&lt;/li&gt;
&lt;li&gt;Team or solo&lt;/li&gt;
&lt;li&gt;Different types and themes&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How can you get into competitions
&lt;/h1&gt;

&lt;p&gt;In my case I got familiar with programming competitions trough college. I had a few passionate professors that started working with local companies to organize competitions. I know that not everybody can find themselves in such a situation and that's why I want to tell you how you can find some on your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research companies you like
&lt;/h3&gt;

&lt;p&gt;Many of the bigger companies in your environment know how important for recruiting, developing talent and employer branding competitions are. So you can research a few companies you like and find out if they hold any competitions. One of many examples is &lt;a href="https://www.ecosim.hu/en/works/ericsson_championship" rel="noopener noreferrer"&gt;Ericsson Programming Championship&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Join your local IT community
&lt;/h3&gt;

&lt;p&gt;Joining your local IT community will have multiple benefits for you and one of the benefits is learning about events happening near you. Last year my local IT community center organized the first ever open hackathon in my town. Even some of my professors were participants (I must say the experience was very weird for me because I was one of the mentors at the time 😆).&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow IT blogs
&lt;/h3&gt;

&lt;p&gt;If your country has a website that is specialized in general IT news in your region, follow it right now. You can learn a lot from them and also learn about interesting events happening. &lt;br&gt;
Following DEV.to can also inform you a lot as DEV.to promotes a lot of IT events. At the moment of writing this post it's hacktoberfest so I bet if you take a look at the events on the &lt;a href="https://hacktoberfest.digitalocean.com/" rel="noopener noreferrer"&gt;hactoberfest&lt;/a&gt; page you could find some upcoming competitions you can join.&lt;/p&gt;

&lt;h1&gt;
  
  
  What you need before entering a competition
&lt;/h1&gt;

&lt;p&gt;If I had to say what is the number one thing you need it would be &lt;strong&gt;courage&lt;/strong&gt;. You need &lt;strong&gt;courage&lt;/strong&gt; to do something that's completely outside of your comfort zone but believe me it's worth it. It doesn't matter how old you are, what experience you have there is a competition out there for you. &lt;br&gt;
Skill is needed but it is definitely &lt;strong&gt;not&lt;/strong&gt; a deal breaker. Lets say, if you are interested in web technologies, knowing HTML, CSS and a little bit of JavaScript you could enter a competition that's more focused on the &lt;em&gt;idea&lt;/em&gt; and you need to code up just a semi working prototype.&lt;/p&gt;

&lt;h1&gt;
  
  
  The benefits
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boosting self-confidence&lt;/strong&gt; - expending your skill set and discovering what you are capable of doing in stressful situations really boosts your self-confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developing your programming skills&lt;/strong&gt; - you can choose a technology you never have time to work on and try to master it in the allotted time. Or you can challenge yourself to go above and beyond with the technologies you already know.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication&lt;/strong&gt; - If you are a part of the team you will have a lot of opportunities to better your soft skills. You will have to resolve potential conflicts which you can learn valuable lessons from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time and task management skills&lt;/strong&gt; - You will have to decide who will do what on your team and to estimate how long each task will take, that way you become more efficient. You should also be prepared to cover for each other because any day can be the day when you write the code but nothing works, believe me happens a lot during competitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Making friends&lt;/strong&gt; - meeting and socializing is as important in IT industry as it's in any other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job offers&lt;/strong&gt; - Most of the time competitions are not just for the sake of competitions, there are always companies around that are ready to scout any potential candidates. The company I work for now got to know me from participating in their competition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The reward&lt;/strong&gt; - Usually there is a reward for the top three teams and who doesn't like free stuff on top of already rewarding experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I've learned a lot trough my experiences competing and as we all know there is always a lot more to learn, so I can't wait for the opportunity to form a team with my friends and participate again. What are you waiting for?&lt;br&gt;
 &lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Hi 👋 &lt;br&gt;
Thanks for reading the post. I'm slowly getting back into writing, if you'd like to support me writing future posts feel free to buy me a cup of coffee.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/wSd4q6U" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-blue.png"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>motivation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I wrote a PHPStorm plugin</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Fri, 26 Jul 2019 12:36:50 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/i-wrote-a-phpstorm-plugin-1job</link>
      <guid>https://dev.to/kristijankanalas/i-wrote-a-phpstorm-plugin-1job</guid>
      <description>&lt;p&gt;There is a high chance you were once so upset at your IDE for not having a feature you need, that you thought of writing a plugin for it. Most of us end up never doing it and finishing the job without the said plugin. The same happened to me.&lt;br&gt;
I had an idea for a PHPStorm plugin for months but never &lt;em&gt;finding&lt;/em&gt; time to write it. Until I had to do the same boring routine one too many times.&lt;br&gt;
So I wrote a PHPStorm plugin.&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;When you have a class that has getters and setters in some (rare) cases you will need to write something like this: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$car&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;Car&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$car&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Audi'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$car&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'A6'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;With some auto-complete features that InteliJ platform provides I can finish this pretty quickly. But what if a class has a dozen of getters or setters you need to call?&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fja0qdq75yua7osrvs1sn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fja0qdq75yua7osrvs1sn.gif" alt="Idea"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;The idea was simple a developer places the caret on the &lt;code&gt;$car&lt;/code&gt; variable and by pressing a shortcut, generate all the getters or setters (depending on the shortcut) for the given object.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing the plugin
&lt;/h2&gt;

&lt;p&gt;I was really nervous cause I haven't touched &lt;em&gt;java&lt;/em&gt; since my school days but I made a decision to just do it.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsazo1wvrocb85c9aoeqg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsazo1wvrocb85c9aoeqg.gif" alt="Just do it!"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PHPStorm - contains &lt;code&gt;php-openapi.jar&lt;/code&gt; and &lt;code&gt;php.jar&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;InteliJ IDEA Community Edition&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;I've followed a setup written by the people at Jetbrains, &lt;a href="https://confluence.jetbrains.com/display/PhpStorm/Setting-up+environment+for+PhpStorm+plugin+development" rel="noopener noreferrer"&gt;Setting-up environment for PhpStorm plugin development&lt;/a&gt;. If you are going to follow this setup make sure that when you are configuring the SDK you add &lt;code&gt;php-openapi.jar&lt;/code&gt; and &lt;code&gt;php.jar&lt;/code&gt; like so &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtzo1t0cilg645jhc4gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtzo1t0cilg645jhc4gu.png"&gt;&lt;/a&gt;&lt;br&gt;
don't make the same mistake as me by not seeing the warning to not set those in &lt;code&gt;Libraries&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Debugger setup
&lt;/h3&gt;

&lt;p&gt;By default InteliJ will setup and run another instance of InteliJ when you click on the debug button. It works really good, but I was developing a plugin for PHPStorm, not for InteliJ. I've searched far and wide for a debugger setup if you are developing a PHPStorm plugin and I couldn't find any. So I had to discover how to do it on my own. &lt;br&gt;
You can go to the edit debug configurations like so &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa1c5md9bxk6l3wptt16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa1c5md9bxk6l3wptt16.png" alt="Debug configuration"&gt;&lt;/a&gt; in the JRE field navigate to the path of your PHPStorm and voila, now it works. Easier than I thought it would be.&lt;/p&gt;
&lt;h3&gt;
  
  
  Coding
&lt;/h3&gt;

&lt;p&gt;I must say I found it hard to do anything, documentation is pretty scarce and I forgot a lot of java rules and syntax.&lt;br&gt;
Unfortunately I couldn't find a way to tap in the power of PHPStorm and to ask it; what it knows about a certain variable. The only choice I had left is to use &lt;a href="https://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/psi_files.html" rel="noopener noreferrer"&gt;PSI&lt;/a&gt; and try to figure out where is the declaration of the variable and what type it is. After a few hours of trial and error I have succeeded in that.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;generateMethods&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AnActionEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;typeOfMethod&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PlatformDataKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROJECT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Editor&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PlatformDataKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CaretModel&lt;/span&gt; &lt;span class="n"&gt;caret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCaretModel&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;PsiFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LangDataKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PSI_FILE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;PsiElement&lt;/span&gt; &lt;span class="n"&gt;selectedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElementAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOffset&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selectedElement&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;selectedElement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getParent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getReference&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="nc"&gt;PsiElement&lt;/span&gt; &lt;span class="n"&gt;variableDeclaration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selectedElement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getParent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getReference&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;                        
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variableDeclaration&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="nc"&gt;PsiElement&lt;/span&gt; &lt;span class="n"&gt;assignmentExpression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;variableDeclaration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assignmentExpression&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PsiElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;selectedElementClass&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;SmartList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
                            &lt;span class="n"&gt;assignmentExpression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accept&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;PsiRecursiveElementVisitor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                                &lt;span class="nd"&gt;@Override&lt;/span&gt;
                                &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;visitElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PsiElement&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ClassReferenceImpl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReference&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                                            &lt;span class="n"&gt;selectedElementClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReference&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                                        &lt;span class="o"&gt;}&lt;/span&gt;
                                    &lt;span class="o"&gt;}&lt;/span&gt;
                                    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                                &lt;span class="o"&gt;}&lt;/span&gt;
                            &lt;span class="o"&gt;});&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;Believe me, I'm not proud of this code and I am looking forward to rewriting it soon.&lt;/p&gt;

&lt;p&gt;Now the only thing left was to get the getters and setters of the instantiated class and add the calls to the editor. I managed to get the methods of a class without any issues. I expected the writing to the editor to be the easiest task of them all and boy was I wrong. Again I had to spend a few hours to figure out how to do it and this is the best I could come up with&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDocument&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;firstMethodGeneration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PsiElement&lt;/span&gt; &lt;span class="nl"&gt;initClass:&lt;/span&gt;&lt;span class="n"&gt;selectedElementClass&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;PhpClassImpl&lt;/span&gt; &lt;span class="n"&gt;phpClass&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nc"&gt;PhpClassImpl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;initClass&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Method&lt;/span&gt; &lt;span class="nl"&gt;method:&lt;/span&gt;&lt;span class="n"&gt;phpClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethods&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// If method name has "set" on position 0 add it to editor&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indexOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeOfMethod&lt;/span&gt;&lt;span class="o"&gt;)==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nc"&gt;FirstSetter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;firstMethodGeneration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;WriteCommandAction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runWriteCommandAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;,()-&amp;gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FirstSetter&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
                    &lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;moveToOffset&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getVisualLineEnd&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                    &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOffset&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;moveToOffset&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getVisualLineEnd&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOffset&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="n"&gt;selectedElement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"-&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"();\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;});&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;firstMethodGeneration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;In the end I got a very limited plugin that can generate setters or getters only for the variables instantiated in the same file. It can't determine variable type from a function return or more advanced stuff like that. Having said that I'm still very happy how it turned out and I'm planning to make it more powerful in the future and add more features to the plugin.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foei1odcsjsxo3hlwe4bj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foei1odcsjsxo3hlwe4bj.gif"&gt;&lt;/a&gt;&lt;br&gt;
If you want to see more of the plugin feel free to visit the GitHub repository. &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/KristijanKanalas" rel="noopener noreferrer"&gt;
        KristijanKanalas
      &lt;/a&gt; / &lt;a href="https://github.com/KristijanKanalas/PHPCodeGeneratorPlus" rel="noopener noreferrer"&gt;
        PHPCodeGeneratorPlus
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      PhpStorm plugin that let's you generate even more PHP code
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;PHP code generator + &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e4b4619d5c0a9a76e097c721ca34ed753a587e05c2a1a71dec7e03db075ea0ee/68747470733a2f2f696d672e736869656c64732e696f2f6a6574627261696e732f706c7567696e2f642f31323539302d7068702d636f64652d67656e657261746f722d2e737667"&gt;&lt;img src="https://camo.githubusercontent.com/e4b4619d5c0a9a76e097c721ca34ed753a587e05c2a1a71dec7e03db075ea0ee/68747470733a2f2f696d672e736869656c64732e696f2f6a6574627261696e732f706c7567696e2f642f31323539302d7068702d636f64652d67656e657261746f722d2e737667" alt="JetBrains IntelliJ plugins"&gt;&lt;/a&gt;  &lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6cd0120cc4c5ac11d28b2c60f76033b52db98dac641de3b2644bb054b449d60c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;PhpStorm plugin that lets you generate even more PHP code.&lt;/p&gt;
&lt;p&gt;Plugin provides the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generates calls for the setters of the declared variable (Shift +Ctrl + NUMPAD1)&lt;/li&gt;
&lt;li&gt;Generates calls for the getters of the declared variable (Shift +Ctrl + NUMPAD2)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plugin Url&lt;/td&gt;
&lt;td&gt;&lt;a href="https://plugins.jetbrains.com/plugin/12590-php-code-generator-" rel="nofollow noopener noreferrer"&gt;https://plugins.jetbrains.com/plugin/12590-php-code-generator-&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ID&lt;/td&gt;
&lt;td&gt;PHPCodeGenerator+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Install the plugin by going to &lt;code&gt;Settings -&amp;gt; Plugins -&amp;gt; Marketplace&lt;/code&gt; and then search for &lt;code&gt;PHP code generator&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;By placing your caret at an initialized variable and pressing &lt;code&gt;ALT + Insert&lt;/code&gt; a PhpStorm code generator
menu will popup you can then choose &lt;code&gt;Get getters&lt;/code&gt; or &lt;code&gt;Get setters&lt;/code&gt; option.
Actions also have shortcuts &lt;code&gt;Shift + Ctrl + NUMPAD1&lt;/code&gt; and &lt;code&gt;Shift + Ctrl + NUMPAD2&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Authors&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Kristijan Kanalaš (&lt;a href="https://github.com/KristijanKanalas/PHPCodeGeneratorPlusmailto:kanalaskristijan@gmail.com" rel="noopener noreferrer"&gt;kanalaskristijan@gmail.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/KristijanKanalas/PHPCodeGeneratorPlus" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>php</category>
      <category>java</category>
      <category>phpstorm</category>
    </item>
    <item>
      <title>From Monolith to Microservices in One Year</title>
      <dc:creator>Kristijan Kanalaš</dc:creator>
      <pubDate>Thu, 18 Jul 2019 10:56:59 +0000</pubDate>
      <link>https://dev.to/kristijankanalas/from-monolith-to-microservices-in-one-year-1fff</link>
      <guid>https://dev.to/kristijankanalas/from-monolith-to-microservices-in-one-year-1fff</guid>
      <description>&lt;p&gt;So, there I am a year ago, living a carefree life working on a legacy monolith — a codebase written in CodeIgniter 2. Not the best framework in the world, but it did the job, at least on the surface. We had a lot of hacky code, after years and years of making things work for our clients in as little time as possible. Having said that, I think you understand that first sentence was a joke.&lt;/p&gt;

&lt;p&gt;After some changes to our business strategy — and a lot of convincing — the IT department got a green light for a no-deadline, full rewrite of our &lt;a href="https://en.wikipedia.org/wiki/Applicant_tracking_system" rel="noopener noreferrer"&gt;Applicant Tracking System (ATS)&lt;/a&gt; — only a part of our monolith, but a big part for sure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;After adding a new feature, it goes into the manual testing phase. Once it's greenlit, you deploy it to production — but what often happens is you miss a testing scenario, and you end up with lots of bugs in production.&lt;/li&gt;
&lt;li&gt;This problem is tightly connected to the previous one: If you have an older codebase, its foundation starts to slowly break down, so adding new code on top of it ends up relying on other code. With that kind of codebase, sometimes you end up breaking code that should be totally irrelevant to what you're currently writing.&lt;/li&gt;
&lt;li&gt;When you have 11 people working on the same codebase day in, day out, without any safeties in place whatsoever, problems are bound to arise.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Known solutions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Automated tests — Even if you don't cover every possible scenario, automated tests — in addition to manual testing — give you a pretty good solution to our "bugs in production" problem. One of the biggest benefits of automated tests is that they let you freely upgrade your dependencies . If you have even partial coverage, when you do a "composer update" and run your tests, you can instantly see if a change in a dependency is breaking your code.&lt;/li&gt;
&lt;li&gt;Splitting a giant codebase into smaller ones (i.e. microservices). With microservices, you can battle the "old code" problem on two fronts: On the one hand, they allow you to just leave your code sitting there without having it bother anything else, and on the other hand, if you &lt;em&gt;do&lt;/em&gt; want to do a rewrite, microservices make it easier by letting you rewrite small parts of your codebase at a time. For example, in our legacy app we use an ancient version of &lt;a href="https://github.com/tecnickcom/TCPDF" rel="noopener noreferrer"&gt;TCPDF&lt;/a&gt;, which is one of the things preventing us from migrating it to a recent version of PHP. With microservices, we can put TCPDF inside a container with the specific PHP version it needs, and it's no longer an issue for the rest of the codebase.&lt;/li&gt;
&lt;li&gt;One of the best solutions we found for our problem was splitting the codebase between our teams, and doing regular code reviews inside those teams. This way, the whole team knows the code they are working on, and we avoid any potential problems by using merge requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How we did it
&lt;/h2&gt;

&lt;p&gt;For the first time ever, we got to do a project of this without having any sort of concrete deadline (Did you think I was kidding when I said that earlier?); even so, we couldn't just rewrite &lt;em&gt;everything&lt;/em&gt; remotely related to the ATS, even if we wanted to — so, we decided to settle for rewriting only the parts that communicate with our ATS (in addition to the ATS itself, of course). The first microservice we made was the one that manages our job ads — we call it the job server. This was my first Symfony project; given that, and some of the other problems we found ourselves dealing with later on, I think you'll understand why I had to rewrite that microservice 3 times in the course of this project. And so we arrive at one of the first pieces of advice I can give: &lt;strong&gt;don't be afraid of rewriting something you just finished rewriting&lt;/strong&gt;. Even if you have experience, it's unlikely that you'll get everything right the first time around.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1yrf0usntk8fiwhtt2r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1yrf0usntk8fiwhtt2r5.png" width="421" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SDKs FTW
&lt;/h3&gt;

&lt;p&gt;If you take a look at the arrow between "ATS" and "Job server," the way it's drawn right now makes it look like they're communicating directly. We knew that the ATS wasn't going to be the only service communicating with the job server. Very early on, we discovered the need for &lt;strong&gt;SDKs&lt;/strong&gt;. There is no better way of communicating with a service other than a service having its SDK. The way we went about this , though, is questionable…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidenote:&lt;/strong&gt; What we refer to as "SDKs" are light, reusable libraries that make it easy to communicate with a specific service. We find these particularly useful because (nearly) all of our backend code is written in one language (PHP), so a service that has an SDK can be easily integrated into &lt;em&gt;any&lt;/em&gt; other service.&lt;/p&gt;

&lt;p&gt;One day, we found a package called &lt;a href="https://github.com/CircleOfNice/DoctrineRestDriver" rel="noopener noreferrer"&gt;DoctrineRestDriver&lt;/a&gt;. Immediately, we were like "OMG, this is awesome." We could make an SDK that gives you an EntityManager and lets you use Doctrine like you're talking directly to a database, even though it actually talks to the job server!&lt;/p&gt;

&lt;p&gt;We were so amazed that we immediately went ahead with implementing this package into the Job SDK.&lt;/p&gt;

&lt;p&gt;Later in the project, we discovered that it doesn't cover all of our use cases, and also that it's hard to maintain code that uses it. DoctrineRestDriver itself wasn't maintained either, so we had to fork it and spend some long hours digging into and changing the core of the library to make it work for our use case. At the time, we were in too deep to turn back, so we had to continue development with the SDK as-is. The advice I can give on this part of the process is: &lt;strong&gt;Don't look for the cheap way out&lt;/strong&gt; when building SDKs. All the other SDKs we made later on were made with a different approach — Simple, descriptive methods that make a simple HTTP request; basically just thin wrappers around an HTTP client. More code, but far easier to maintain in the long run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s18i2bhcaqxt3yiecwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s18i2bhcaqxt3yiecwu.png" width="495" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;Up until this point, we'd been working towards splitting our codebase into smaller chunks, and we were doing a pretty good job of it, too; but who was going to test all this when it was done? At the point where the project looked like the diagram above, we had zero tests. (Okay, we actually did have some tests, but I'm not proud of them. Before doing any real research into testing, I started writing tests for the job server. What happened was that, while trying to write what I thought were unit tests, I accidentally wrote integration tests. When I realized what I'd done, I decided to watch some tutorials on how to properly write unit tests. &lt;a href="https://symfonycasts.com/screencast/phpunit" rel="noopener noreferrer"&gt;PHPUnit: Testing with a bite&lt;/a&gt; from SymfonyCasts is a course that particularly helped me understand testing better.)&lt;/p&gt;

&lt;p&gt;After а lot of effort, when all was said and done, we had about 200 tests across all of our microservices. It's still not a lot, all things considered, but we're hoping to gradually increase that number as we go along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Present Day
&lt;/h2&gt;

&lt;p&gt;After one year, ~50,000 lines of code, and ~2,600 commits, a team of 3 developers — consisting of a senior full stack developer and two medior backend developers — ended up with 18 services. Today, the system powering our ATS looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aeq7urqp3k709fzbh9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6aeq7urqp3k709fzbh9i.png" width="451" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just imagine having this picture in your head before going to production for the first time. Add to that the inexperience we all had with this technology, and I think you can understand how anxious I was about the prospect of deploying all of this to production. When it came down to it, minor issues aside, we were all surprised by how smoothly the deployment actually went. Today, working in a system like this is amazing, and I wouldn't trade it for anything.&lt;/p&gt;

&lt;p&gt;We learned a lot over this past year, and this post contains only a tidbit of the knowledge we came out of this project with. If I'm being honest, probably every microservice in the image above deserves a post of its own, and my team and I are very eager to share all of that knowledge — so, consider this post to be only the first of many.&lt;/p&gt;

&lt;p&gt;That's all, folks. I can't wait to hear all your thoughts on this!&lt;/p&gt;

&lt;h2&gt;
  
  
  About me
&lt;/h2&gt;

&lt;p&gt;Hi, I'm Kristijan! Traveling enthusiast, full time food and beer lover, and occasional software developer. When I'm not planning my next trip to Japan or watching anime, I work at &lt;a href="https://poslovi.infostud.com/" rel="noopener noreferrer"&gt;poslovi.infostud.com&lt;/a&gt;, a part of &lt;a href="https://www.infostud.com/" rel="noopener noreferrer"&gt;Infostud group&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>php</category>
      <category>symfony</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
